-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Fix axis getting mixed up when split leaf #82436
Conversation
Bear in mind, there is a possibility that the comment is out of date (rather than the code wrong way). 😄 I did quite a bit of empirical testing at the time, so it would be nice to double check this is an improvement with some testing (I have vague memory some of the results were counter intuitive). Agree it does seem like it should be splitting on longest axis preferentially, but would be nice to confirm. It is very possible it could be wrong way around, as this routine occurs rarely and only affects the optimality of the tree (in cases with elongated AABBs, which may be rare), so would be unlikely to be noticed. I'll try and have another look at this. |
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 think on balance this is worth a go.
It does look like it may have been unintentional, and if the other way had been better, I should have changed the comment at the time.
So I think we should go for this and watch for any regressions. 👍
Also needs changing in 3.x.
(For reference for mergers, this is unlikely to cause bugs, as it just affects the optimality of the tree when reshaping, and thus performance.)
Aside
As an aside it's nice to have another pair of eyes looking through this. It's quite specialized so doesn't get as much review as I would ideally like .. although @pouleyKetchoupp did spend quite a while working on it too. The area most in need of scrutiny if I remember right is the _logic_balance
area as it depended on some of Randy Gaul's code.
I did in fact last week start writing a reference version as a kind of "unit test" so we could double check any physics bugs were physics side rather than BVH side. I haven't rolled this out but it didn't detect any bugs so far which is good.
I also think physics should have a reference brute force broad phase, both because of the possibility of bugs in BVH, but also because I have long suspected they are not taking proper account of tunneling. In BVH if you move AABB from A to B it will only consider collisions at A, and at B, rather than in between. Whereas for a move I suspect the physics should be passing a merged AABB of the current and previous AABB. BVH will not do this for the client, that is client responsibility.
The areas that I think are problematic are those that use After a leaf node with multiple items uses Lines 27 to 33 in 545d1c0
godot/core/math/bvh_public.inc Lines 168 to 182 in ec62b8a
|
Thanks! |
There is an optimization in To be a bit more clear on some of the thinking : in a few cases, the leaf bounds are purposefully not kept up to date. Even if a corner item is removed (and the leaf made dirty), as long as the bound is conservative (i.e. over-estimates) then nothing should go seriously wrong and the refit can be deferred. This is done in a few places as keeping the bounds exactly up to date on each change would be prohibitively expensive. Keeping bounds up to date isn't a big deal with a traditional BVH with e.g. 1-4 items per leaf, but when you may be storing e.g. 1024 items in a leaf, the full update needs to be deferred as much as possible. There may well be improvements available though, I spent a fair bit of time tweaking the refits they are quite difficult to get right. |
Before, I was concerned that it might be missing something. Now it feels like some leaf nodes may be calculated multiple times.
It seems that --- a/core/math/bvh_structs.inc
+++ b/core/math/bvh_structs.inc
@@ -83,7 +83,7 @@ public:
void clear() {
num_items = 0;
- set_dirty(true);
+ set_dirty(false);
} AsideI'm confused by the results from test_perf_broadphase. While it feels like some expected stages are shortening, others are increasing. It feels like the modification shouldn't have much effect, but it has a significant effect. Especially the Remove Time. A few days ago, testing 600x600 on my computer would take around 43s, now it takes around 30s. |
Calculated in multiple places? Yes they can be. But ideally we don't want to calculate them multiple times though, if possible. But this may occur in some situations.
Yes.
If I remember right, the general idea is this:
The AABB is thus a conservative bound.
Yes.
I'm not familiar with this test, it seems one of pouleyKetchoups. I'm trying it out but get nothing on screen (maybe it deliberately doesn't render). Perhaps you can describe what this test does and what aspect of it you find interesting and why?
Do you mean "creating objects, adding objects, moving objects"? This is unclear.
What modification are you referencing?
What has changed in these few days? I'm really not clear on what you are saying here, if you can be more precise with language this could help (maybe this has been auto-translated?), and also it is important to not assume any knowledge. |
Currently, the item will be added immediately after
Yes. The stages recorded in the output.
That's the result before and after applying this PR. I haven't figured out why yet.
I'm sure it should be before and after this PR is merged. I verified it by reverting the commit. Some other interesting tests, they all have a greater impact on the time of "Removing objects": Test 1.
Similarly, revert this PR, and then replace the above code with if (!pending_shape_update_list.in_list()) {
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
} Test 2. In the script, here, if removed in the reverse order, it takes less time. |
I'm not particularly familiar with it either, I just started learning about bvh last week and I'm not sure if these tests are appropriate. |
I don't understand, is marking it as dirty causing a problem, or was this suggesting an optimization, or am I missing a call to set this as non-dirty afterwards?
Especially in artificial benchmarks, changes in the tree structure could lead to more noticeable changes in performance. The test in question seems to create items in a regular grid as far as I can see. Now whether any change in timing is due to directly the BVH or due to more collisions being processed by the physics is a question which can be revealed by profiling. (This is part of the reason the physics should also have a switchable reference broad phase imo.)
This is generally the case due to the scene tree processing, (as well as order being likely important for tree structures such as BVH). This is why "global timings" often end up being difficult to interpret. It is important not to conflate changes in timing caused by other parts of the engine, with parts that are trying to be examined. Using several timers for different parts of the code can help here, and of course profiling. One thing that pouley did identify as a problem was that physics objects were often added at the origin then moved to their start location. This was causing all sorts of unnecessary collision processing to happen, because if everything started at the origin it all started in collision. Whether this was fully fixed in the physics I don't know. |
There seems to be no big problem. I thought that setting |
I've spent a little time this morning testing the splitting and it seems mostly ok. It is possible that we can set the child leaves to non-dirty after the split, as their AABB seems to be up to date at the end of the splitting routine. This could be causing a single extra un-needed refit at a later stage (it is done in in
I'll try and evaluate a bit better whether we can setting the child leaves as non-dirty after a split. I think it should be okay, but there's likely little gain to performance, and risk of regression, so worth treading with care. It could be that although the refit to the leaf itself isn't required, it ends up being needed for the ancestor nodes. |
This seems to be a bug. Line 137 in 4c3dc26
|
Ah yes well spotted, that doesn't look right. Interesting that it was working ok with this bug .. suspect it had been getting by because:
We should fix this, am just checking there aren't any other implications. I think given that the dirty flag wasn't actually working, you are correct and we should probably fix this and
In a way it would be nice to defer the case of adding items but I'm not sure it gains much because in almost every case the next operation requires refitting the tree to get everything up to date. 🤔 @Rindbee if you can make a PR I can review. I think for now we can just fix above bug (replacing with As far as I can see there are no major performance regressions to this (it should in theory be faster in the long run as the tree should be more optimal, but it's doing more refits than before). If necessary we could limit the refits to one per tick in the |
The number of splits is related to the number of items ( It may be a little slower when adding items because the items will be moved (removed and reinserted), leaf nodes may be more likely to be marked dirty. godot/servers/physics_2d/godot_collision_object_2d.cpp Lines 178 to 183 in 4c3dc26
The results in #82436 (comment) are uncommon because aabb in actual cases may be more random. |
order[0]
should hold the longest axis (max_axis_index()
), whileorder[POINT::AXIS_COUNT - 1]
should hold the shortest axis (min_axis_index()
).