Operating system or device - Godot version:
Tested on _ONE E1003 (OnePlus X)_ and _Samsung Galaxy S4 I9505_
Godot 2.1.2
Issue description:
TileMap has bad performance on Android. In example project, there is only single tilemap, which is filled with single tile type. When tested on both devices, this example project has only about 20 FPS at max.
It's notable, that setting Quadrant Size to 1 incerased the FPS to 50.
I feel like this shouldn't be the case even with badly optimized code, there must be some kind of issue with the drawing.
Steps to reproduce:
Link to minimal example project:
tilemap-test.zip (Godot 2.1)
Bugsquad edit: Reproduction project from #26620 for Godot 3.1: tilemap_test.zip
(Some math to follow)
Given that the included projects has quadrant_size = 16 and tile_size = Vector2(64, 64), this means that the total size of the whole quadrant is 1024x1024 pixels.
Checking the resolution of this model, it is 1080x1920, thus one can see a total of 3 by 3 = 9 quadrants at a time. Thus, we have maximum 16*16*9=2304 tiles rendered every frame. Ouch.
With quadrant_size = 1, we have total size of 64x64 per quadrant. This would mean maximum 17*31=527 tiles per frame. ~4x better for the GPU (and slightly worse for the CPU).
The main problem here comes from the fact that quadrants are culled individually (as it should be). This helps a lot when quadrants are small (in pixels), since we get just a small fraction of the culling calculations, and can get faster to rendering stuff on the screen. But, when quadrants are larger, we get lots of leftover out-of-screen drawing, which certainly doesn't help with performance.
I think this issue should be about the default quadrant size, because 16 certainly isn't good, as I've seen it hiccup on low-mid-end laptops too.
But, to determine the perfect quadrant size, we need to know the tile size, otherwise we can get with small-quadrant issues when tile size is 16x16 or even less.
Therefore, I propose that we change quadrant size to be measured in pixels rather than in tiles, and then round to a whole amount of tiles. This way, we can set something more sane, like 128x128 or 512x512 and still be good with a whole range of games and devices.
Thanks for explaining, that makes sense. I went with the tilemap tutorial advice: "don't mess with quadrant size". Decreasing this option helped in my actual project.
Still, when looking at the demo project, setting quarant_size to something smaller, like in range 1-5, the FPS is at 50 max. Is that normal? I would expect the device to be able handle more.
In fact, it's the same performance as if I manually placed sprite node for every tile. I feel like there could be some kind of performance advantage.
@SkaceKamen 50 FPS probably comes from VSync or some internal limit, since overdrawing frames is typically useless, and leads to slight tearing in some cases. So, that's not an issue.
@SkaceKamen 50 FPS probably comes from VSync or some internal limit, since overdrawing frames is typically useless, and leads to slight tearing in some cases. So, that's not an issue.
While you are right about mobile devices enforcing V-Sync, I believe phones have 60 Hz displays, not 50 Hz.
@Calinou Yes, I agree that 50 Hz sounds strange, but it is still a V-Sync cap -- there is nearly nothing else it can come from. (I couldn't find refresh rate specifications for this model, but still)
@bojidar-bg: ONE phone has 60 FPS on empty scene. It normally has no problem keeping steady 60 FPS, even on more complex projects.
It's also the approx. same for both listed devices (I tested it on both since I suspected it's specific to my device).
Maybe we can expect this performance impact to change a lot once tilemaps will benefit the new renderer optimizations (VAOs).
Yeah, but still won't cope with the issue of picking the right quadrant size. Just to restate what I said above:
I think Tilemap having close to identical performance against hand-placed sprites is expected in a static scenario (when both has no overdraw / texture / material change etc.)
If you consider that static sprites draw calls are batched and culled by quadrants, yes :) The downside would be memory then.
This was most likely resolved by the introduction of GLES2 batching (as long as you're using the GLES2 renderer, which is likely the case on mobile). Closing.