Search Unity

Overlapping 2D triggers slow even if they ignore each other

Discussion in 'Physics' started by any_user, Oct 18, 2016.

  1. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    I have a scene with many small objects with a box collider and a kinematic Rigidbody2D for each of them which is used to check the mouse ray. The collider is on a layer that doesn't collide with anything, not even itself. As long as these colliders don't overlap, the performance is pretty good. But as soon as they start to overlap (which they do often in our case), it uses about 10x more time in Physics2D.Simulate. Shouldn't it be irrelevant that they overlap if they anyway ignore each other? I could understand a small performance decrease, but right now it's really extreme.

    As a workaround, I noticed that enabling "Used by Effector" on the collider without having an effector attached increases the performance significantly, while still reacting to raycasts. But I don't completely understand why. Any ideas why that happens?

    Here are two screenshots with the same group of objects.

    Screen Shot 2016-10-18 at 12.12.44.png

    Screen Shot 2016-10-18 at 12.11.02.png
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    The fact that they ignore each other still means that it has to check if objects need to ignore each other which takes a little time but I'm surprised to see the times you posted. I guess it depends on how many objects we're talking about and the performance of the device it's running on.

    Could you provide me with a link to a simple reproduction project or perhaps report a performance bug and send me the case number so I can investigate?

    Thanks in advance.
     
  3. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    Thanks for the answer.

    I tried to reproduce the problem in a isolated case and noticed, that it is caused by effector triggers, not just by overlapping normal (non-effector) triggers. In my scene there are the (box collider) triggers which ignore each other, but also some more triggers (circles in the screenshots) which are on the same layer. I assumed they would also ignore each other, but I had "Use collider mask" on with all layers enabled, so they interacted with all the box colliders. At least theoretically, because the triggers (without effector) are kinematic anyway and don't react forces.

    The "Use collider mask" checkbox is actually a bit confusing, I wasn't aware that activating it actually overrrides everything in the layer settings, and it's on by default, which means by default effectors don't care about the physics layer mask matrix.

    So in my case I could get around it by disabling "Use collider mask" so they properly ignore each other, but I still think it shouldn't actually take a performance hit so big to have effectors interacting with kinematic objects, because they anyway don't have an effect.

    The report with a scene that shows the issue: 842783
     
  4. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    Btw. it still helps to check "Used by effector" on the trigger that shouldn't interact with anything to improve the performance. It looks like putting them into "effector-mode" without an active effector helps ignoring overlaps.
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    Thanks for the case. I've assigned it to myself and will take a look at it ASAP.
     
  6. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    I have a similar problem with the 3D physics. Moving lots kinematic rigidbodies in a small area with overlapping colliders is very slow even when all collisions are disabled from the matrix. Seems like the same thing, collisions are resolved first before checking if there should be any reactions to collisions. I wonder if it would be possible to skip collision checks altogether for specified colliders both for 2D and 3D physics. In my case I'd want to use the colliders only for raycasting against kinematic objects, so all the collision checks in the physics resolve phase are just wasted cycles.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    The only collision checks involved in 2D is trivial so I'm not sure of why such a cost. It (2D) does a broadphase check (fast AABB overlaps) to see what is potentially overlapping and then checks each to see if it *should* collide with each and that is a simple check. Only if that passes does it do the expensive narrowphase check to see if they are really touching.
     
  8. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    If there's 1000 overlapping colliders on kinematic bodies moving every frame, it's no longer trivial. Sure there probably won't be as much in an actual situation, but I'd still like to have raycast only colliders that skip the checks altogether.
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    Yes I understand that and that is a LOT of moving colliders. My trivial comment was to the cost of each one in the broadphase and updating the broadphase when it moves. Raycasts will do you no good if the collider isn't in the broadphase as raycasts (etc) all use the broadphase to perform a potential set of colliders before performing the more expensive narrow phase check to see if it actually contacts.

    A raycast only collider doesn't mean anything.

    Anyway, I work on 2D so I can't speak as to the majority cost of moving 1000 overlapping colliders and whether it is indeed the broadphase or not.
     
  10. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    It's the exact same thing with Unity's 3D and 2D physics, I made test scenes for both. Just moving a large amount of overlapping kinematic-but-not-actually-colliding-with-anything colliders is very expensive and the cost increases exponentially with the amount of overlapping colliders. Seems to me it's checking each collider versus all the others every frame and I don't undestand why that's necessary if I know the collider won't collide with anything and I'm only going to use it for raycasting (hence "raycast only collider").

    Currently 2D physics takes ~150 ms on my PC when moving 1000 overlapping boxes in a 1*1 area and something negligible when it's a 10*10 area. I guess it's 1000 * 1000 checks every frame in that worst case scenario, and I was hoping I could do with 1000 checks per raycast instead.

    Maybe the collider vs collider can't be avoided and I'm better off using a custom spatial hashing + raycasting system for that, but I'd like to be able to use the existing colliders if at all possible.
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    So if you're saying that the problem is only when overlapping and not the fact that you have 1000+ rigidbody then do you get the same when the body type is kinematic? Kinematic don't interact with other kinematic.
     
  12. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    All of them are kinematic rigidbodies with colliders, moving them from a script. All collisions disabled from the physics settings. No problems if they are further apart or if there are no colliders.
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    If they're all kinematic and stationary but overlapping then you're only seeing the check in the broadphase but as you said, for 1000 overlapping colliders it'll be 1000^2 AABB overlap checks per fixed-update. If they move, each AABB will need updating as well.

    You can't raycast without a broadphase entry and you must have a broadphase entry because you otherwise you wouldn't even know if the entry related to a 'raycast only collider' or not.

    Anyway, I'll be looking at the above reported case to see if there are any obvious optimizations that can be had here.
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    @any_user Could you try this on the latest 5.5 beta (https://unity3d.com/unity/beta)? I tried this in 5.4.1p4 and I'm seeing a lot of contacts being generated (77.6K!) which is overhead you shouldn't have whereas on 5.5.0b8 there are no contacts therefore much less overhead. Just click on the 'Physics (2D)' area and you should see the counts.

    I've not dug into why this is the case but you should not have contacts being generated here. I'm not sure of the source of the fix in 5.5 either so I'll have to investigate and see if that fix can be backported to the 5.4 patch release stream.
     
    Last edited: Oct 21, 2016
  15. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Basically I just want to (mis)use the colliders for raycasting purposes and skip the actual physics for better performance. I guess it's more of a feature request in that way, but it would be a nice optimization and great to have.

    The way I did it years ago in XNA with spatial hashing was something like this:
    1) On each frame calculate the bucket for each object.
    2) When raycasting, check which buckets the ray hits (broadphase)
    3) Check the ray against the bounding box and then the smaller colliders of each object in the bucket (narrow phase)

    There was no collider vs collider checks at all, since the colliders don't need to know if they overlap with other colliders. Now I don't know what Unity does internally and if what I'm asking is truly impossible, but "just update position, skip collision checks" doesn't seem that crazy to me.
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    I completely understand what you want for sure. It's just that isn't what Unity does internally, it's what Box2D does internally which is add all colliders to a broadphase and I guess the same for PhysX.

    Note that if you've been testing 2D colliders then please try 5.5 beta (as a test) as I stated above to see if that makes a difference in your case.
     
  17. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    I was already testing with the latest 5.5 betas.

    I just found a sort of a solution with 3D physics. If I remove the Rigidbody completely but leave the collider and use Transform.position instead of RigidBody.position, it's much faster and raycasting still works. It's however slower than using RigidBodies when there's no significant overlap.

    With 2D there's no tricks I can find. Toggling simulation off and using Transform.position makes it fast, but that leaves the object completely out of the physics and thus also breaks raycasting.
     
  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    If these colliders are set to not interact with each other then please check the profiler to ensure that there are no contacts being generated. Otherwise, send me a repro project.
     
  19. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    This is what the profiler looks like:

    physics2d_profiler.png

    My test project is attached, 2DOverlap is the scene.
     

    Attached Files:

  20. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    @iivo_k A quick test with your project reveals that it's fast (for 1000 bodies) if they don't move. It's the moving 1000 bodies that's causing the problem. The Box2D broadphase is updating the attached collider AABBs and creating pairs to be tested. In your project it generates 897472 pairs which performs a callback to Unity to ask if they should collide. Hard-wiring that to say no makes no detectable performance change so it's all in the Box2D portion of updating the broadphase.

    The specific code in Box2D that does this is:
    https://github.com/erincatto/Box2D/blob/master/Box2D/Box2D/Collision/b2BroadPhase.h
    Code (CSharp):
    1. template <typename T>
    2. void b2BroadPhase::UpdatePairs(T* callback)
    When a Rigidbody2D moves, each collider attached to it synchronizes the broadphase entry and creates a proxy move entry in a buffer. Later, all the proxy moves, in your case 1000 moves (1 for each collider) are iterated and it performs a tree AABB check (in the link above) to detect pairs and adds them to a pair buffer. It then processes those pairs which performs a callback (to Unity) to ask if the pair should become a contact (for further checking).

    I guess it would be possible to change Box2D so that each broadphase proxy could be flagged to not check for pairs at all. Being able to associate the proxyID of the moving collider with some flag to say don't create a pair would mean you'd not need to update a move.

    https://github.com/erincatto/Box2D/blob/master/Box2D/Box2D/Dynamics/b2Fixture.cpp
    Code (CSharp):
    1. void b2Fixture::Synchronize(b2BroadPhase* broadPhase, const b2Transform& transform1, const b2Transform& transform2)
    2. {...}
    In the above code, the following entry is what initiates a check for collider pairs:
    Code (CSharp):
    1. broadPhase->MoveProxy(proxy->proxyId, proxy->aabb, displacement);
    Temporary commenting out that line results in no collisions for any collider. Doing this makes your code run very fast so that proves it is possible to skip that phase but still keep the broadphase updated (for raycasting etc).

    So in summary, it would seem possible to have an option on a specific collider that would stop it from interacting in any collision but still allow explicit physics queries such as casts etc.

    I'll add that as a performance feature request. At best it wouldn't appear until 5.6a1 though.
     
    leonelvg and iivo_k like this.
  21. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Awesome! Thanks a lot for your time, it's great to get in-depth answers.

    If that gets implemented, maybe it would make sense to have Box2D skip the collisions by default if you have a Collider2D without a Rigidbody2D, since PhysX already does so if you have a Collider but no Rigidbody.

    Also, maybe this is something that should be mentioned in the physics manual / tutorials, since there are these corner cases where it actually is much better to move static colliders instead of kinematic rigidbodies.

    Now, if only I could get colliders that could be used directly from scripts without a GameObject to go along with Graphics.DrawMesh. ;)
     
  22. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    Unfortunately that would mean anything that could touch a static collider wouldn't collide either. Know that these are pairs i.e. bidirectional contacts between two colliders. The static collider might be in contact with a dynamic collider etc.

    It's never better to move static colliders, they are much more expensive to move. That might not be the case in PhysX anymore but it's still true for Box2D. That said, it's much less expensive if you use the new BodyType property and set it to static as the colliders get added to that specific body whereas not adding a Rigidbody2D means those colliders all get added to the implicit (and hidden) static ground-body that's created by default and lives at the world origin. Move a collider attached to the hidden ground-body means its geometry has to be recalculated as it lives in body-space. Moving a Rigidbody is always much faster as any attached colliders live in its space.

    I have notes drawn up for exposing Box2D is a much lower-level 'below' components however I've never actually started the work but considered doing it in one of our hackweeks. At some point I'd love to do that. Would allow end-users to essentially create their own components but still use the raw Box2D.
     
  23. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Right, good point. A checkbox is probably much more obvious anyway, seeing as how long it took me to figure it out with PhysX.

    I meant PhysX in this case, should've clarified. Using 3D colliders without rigidbodies avoids the exponential cost with overlaps but has a bit higher constant cost (I think). The PhysX tutorials and best practices are from the Unity 4.6 era and warn against moving colliders without rigidbodies.

    Maybe the different cases could be mentioned with small examples, something like:
    - Push and be pushed (a physics object) - use a rigidbody and add forces.
    - Just push others (characters) - use kinematic rigidbody and use MovePosition.
    - Ignore others (significantly overlapping hitboxes for raycasting) - with 3D don't use a rigidbody (3D) and use Transform.position / with 2D use the possibly upcoming option to ignore collisions.

    If that ends up happening, I hope the PhysX people will get competitive and do it too. In general I really appreciate the more open direction you're been going. Having new stuff as open source components that you can tweak yourself and having intensive dev presence on the beta forums is so much better than what it used to be earlier with Unity 4, when you'd feel like the bug reports are ignored and the bugs will never get fixed.
     
  24. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    I checked my project in 5.5b9, it definitely runs much faster and no contacts are generated. I'd consider my specific issue solved, thanks! If it's easy to backport the change to 5.4, I'd appreciate it, but if not, no problem.
     
  25. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    Another related question: Are contacts supposed to be generated for effector overlaps?
     
  26. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    You'd need to be more specific. Contacts being generated follow the same rules throughout i.e. if the body-types can contact and the global layer-matrix/effector layer-mask allow it then yes, a contact will be produced.
     
  27. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    I was just unsure if contact specifically means collision (with forces etc.) or any kind of trigger/collider overlap. Just to know if everything is working as expected when I see a lot of contacts generated for overlapping triggers with interacting effectors.
     
  28. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    The profiler reports contact counts but they are not all 'touching' contacts. Box2D creates a contact as soon as the respective collider AABB overlap. You only get callbacks and collision responses when the contact is deemed as touching. So there are effectively two types of contacts, those that are touching and those that are not.

    I guess it'd be possible to report touching/non-touching contacts in the profiler as an improvement.