-
-
Notifications
You must be signed in to change notification settings - Fork 21.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Greatly improve Y-sort performance on TileMaps #73813
Greatly improve Y-sort performance on TileMaps #73813
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code changes seem relatively safe, and the gain is massive, so let's make an exception and get it in 4.0 RC 4.
AFAIK there should also be a way for users to set their quadrant size to (1, 1)
to force the old behavior back in case there are edge cases.
Edit: groud found some problematic scenarios, so shouldn't be merged now. Can't dismiss my review as long as it's a draft for some reason.
Sorry, this PR still need some work. I wrongly assumed that two tiles with the same Y coordinate would share a same Y local position. However, depending on the |
Can this be implemented for 3.5 tilemap as well? By tinkering around I found out that if you replace this: int TileMap::_get_quadrant_size() const {
if (y_sort_mode) {
return 1;
} else {
return quadrant_size;
}
} with this: int TileMap::_get_quadrant_size() const {
return quadrant_size;
} then performance of an isomentric y-sorted tilemap with 400x400 tiles goes from 21 FPS to 720 FPS (34x gain) on my system. Here's were this function is in 3.5 branch: Line 50 in 16f6a5b
I'm not very proficient with C++ or Godot codebase and thus not sure if I should open a PR myself and what kind of complications/regressions it might bring. |
I guess it could. But it's not simple to implement in general.
Well, yeah. But it means it would not Y-sort anymore. We know the culprit is here. The fact the quadrant size is forced to 1 causes a lot more CanvasItems to be created, which is needed as sorting a tile requires it to be in its own canvas item. The solution to this problem is to group tiles into quadrants according to their Y-value in global space, but this requires a rework of the way quadrants are created. This is why I set this PR as draft. |
It might look like it works, but it's likely not. If I am not mistaking, tiles are drawn from top to bottom, left to right anyway, so the drawing order might stay correct in your situation. The problem arises when you need to add a scene (like a character) on the tiles. In that case the sorting cannot be done correctly. It's would be more obvious with a TileSet which has some more height. |
The buildings on the screenshot above are actually scenes, not tiles. But the tiles are flat, you're right. I didn't check it with tiles that have some height. |
In general, if the tiles are flat, then you do not need Y-sorting on the TileMap anyway. |
At this point I feel extremely stupid :D But anyway, I'm glad that at least the culpit of performance problems in clear, so it can be fixed sometime in the future. Thanks. |
Performance figures for this PR can be found here: #74478 (comment) |
a22a655
to
0659b68
Compare
0659b68
to
eb502a7
Compare
eb502a7
to
830641e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked everything besides the SelfList sorting, overall LGTM. 👍
830641e
to
bddd5a4
Compare
Some crash when opening project:
|
bddd5a4
to
1c11d7b
Compare
I've just pushed a new version, it might be fixed (I will need an MRP if it still happen, as I could not really reproduce the bug). |
It's fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it with the project in the issue and the performance is much better. I also tested with some demo project and it appears to be working correctly.
1c11d7b
to
20f8a2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's buggy (tested the latest artifact v4.2.dev.custom_build [968d174f4], things work fine in 4.2.dev5).
(drawing on the same layer, both TileMap and layer has Y sort enabled)
(after toggling the visibility I'm undoing with Ctrl+Z)
And things get funky in a similar manner as you play with it.
At some point this error popped out:
.\core/templates/self_list.h:80 - Condition "p_elem->_root != this" is true.
Oh right, I can reproduce the bug. I'll have a look. |
94fb66d
to
fd632e8
Compare
I pushed a new version, it should be solved now.
That might be an issue, though I am not sure how to solve it 🤔 . I guess I could maybe implement a custom sorting when Y-sort is enabled, but that is tricky to implement. maybe I could sort and reverse the list in that case. Though that needs some addition to selflist. But well that's a bit corner case, so I think I would keep this new behavior for now, and implement the "more complex" solution if someone asks for it to be brought back. |
Indeed, seems to work fine now. 👍 And the code makes more sense also. 🙃
Not sure if I'm missing something but wouldn't it be just a matter of replacing
I mean if we want to preserve the previous sorting then we could add it in here. As per above I think it's a matter of defining a proper comparator. Actually now I've realized that currently in this PR sorting of each "Y-slice" is using Line 106 in fe5b1c8
Meaning it sorts by X first, then by Y. Looking at the code each such Y-slice has 0.01 "height", meaning theoretically it can include tiles with different Y values and hence these could be sorted incorrectly. Highly unlikely to happen but still.
So it makes even more sense to use a custom comparator, sorting by Y first. Then X values could be compared as before (#73813 (comment)) in case we'd want to avoid potential regressions (some users might rely on the previous ordering). |
fd632e8
to
300347c
Compare
You were right, it didn't need anything more than that. But I think to keep compatibility, there's no need to look into the "local coordinate" space. There was no sorting before done using local coordinates, so using map coordinates like (I did there) should be enough. |
Right, these are in map coordinates, I guess I somehow got into thinking about them as being local. 😄 So kinda nevermind the comment. Indeed should be good enough, at least until some edge case is reported. 🙃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
300347c
to
fc0e50e
Compare
fc0e50e
to
30b94bb
Compare
Thanks! |
This greatly improves the performance of TileMaps that are Y-sorted (basically any isometric game).
On a relatively large map, it went from 60 fps to 800fps, so more than 10 times more frames!
This PR basically makes it so Y-sorting creates quadrants by grouping them in large horizontal rows. Instead of forcing the quadrant size to be (1, 1). The new size is thus
Size2i(quadrant * quadrant, 1)
. Quadrant size is squared so that, by default, the quadrant contains as many tiles whether or no Y-sort is enabled.The previous, and I guess safer behavior can be found again when quadrant size is set to 1, but that makes only sense when you rotate the TileMap, which is super unusual for isometric TileMaps.Edit: not true in the latest version.
Bugsquad edit: