Merged
Conversation
- The broad phase now emits new collision pairs and stores all pairs in a HashSet - Contact status and kind is now tracked with `ContactPairFlags` instead of booleans - The narrow phase adds new collision pairs, updates existing pairs, and responds to state changes separately instead of overwriting and doing extra work for persistent contact - State changes are tracked with bit vectors (bit sets), which are fast to iterate serially - The narrow phase is responsible for collision events instead of the `ContactReportingPlugin`
- Renamed `BroadCollisionPairs` to `BroadPhasePairSet` - Added `BroadPhasePairSet` for fast pair lookup with new `PairKey` - Improve broad phase docs
…pt-in - Removed `BroadPhaseAddedPairs` - Renamed `BroadPhasePairSet` to `BroadPhasePairs` - Moved contact creation to broad phase to improve persistence - Removed some graph querying overhead from contact pair removal by using the `EdgeIndex` directly - Made collision events opt-in with `CollisionEventsEnabled` component - Improved a lot of docs
Jondolf
added a commit
that referenced
this pull request
Apr 27, 2025
…nabled (#712) # Objective Multithreaded determinism is currently broken! This is because #699 accidentally resulted in contact constraint order being non-deterministic. CI didn't catch this, because it currently doesn't use the `parallel` feature. ## Solution When the `parallel` feature is enabled, store the pair index for each `ContactConstraint`, and just sort the constraints based on these indices. I suspect that a better approach that doesn't require sorting exists, but for now, this works as a simple hot-fix. I also tried the simple approach of returning `Vec`s from Bevy's `par_splat_map_mut`, but that would mean that we can't reuse the constraint buffers, and have to allocate from scratch every time. Either way, there's more to try and experiment with here. Because the constraints within chunks are still largely sorted, the sorting ends up being quite fast. In the `pyramid_2d` example, it increased the time of "Update Contacts" from about 0.42 ms to about 0.44 ms with ~3025 constraints. I also enabled the `parallel` feature for CI to hopefully catch multithreading problems like this automatically in the future!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Currently, contact constraints are generated serially after the narrow phase. This requires iterating over all contact pairs and redoing various queries and computations, which can add meaningful overhead.
(Note: The commit history also includes most commits from #683, sorry about that 😅)
Solution
Optimize contact constraint generation by generating constraints directly in the parallel contact update loop of the narrow phase. This way, we get "free" parallelism while getting rid of the extra iteration, and we don't need to query for the rigid bodies or colliders a second time. We can even reuse some values computed for the contact update, like
effective_speculative_margin.Constraint generation is multi-threaded by storing constraints in thread-local
Vecs and draining them serially intoContactConstraints. This preserves determinism.Performance
For physics diagnostics, the "Generate Constraints" step has been removed, and the cost is now included in the "Narrow Phase" step. This means that the narrow phase is seemingly more expensive, but the total cost is reduced quite drastically. Improvements can be seen in both single-threaded and multi-threaded performance, with the latter having a larger difference.
Note that the "Store Impulses" step is now slightly more expensive. This is because the contact pair lookup can no longer be performed with the edge index directly, as constraints are generated before contact pair removal, and pair removal can invalidate indices.
Single-threaded
Before:
After:
Multi-threaded
Before:
After:
Migration Guide
NarrowPhaseSet::GenerateConstraintshas been removed. Contact constraints are now generated as part ofNarrowPhaseSet::Update.