Search Unity

Best way to handle ICollisionEventJob at high volume?

Discussion in 'Physics for ECS' started by BigRookGames, Nov 3, 2020.

  1. BigRookGames

    BigRookGames

    Joined:
    Nov 24, 2014
    Posts:
    330
    I am using ICollisionEventsJob to handle bullet collisions and when the collision count gets around 1000 or so it starts to slow down a lot. I am probably doing something inefficient, but wanted to check if there might be a better way to handle this or there is something in the code specifically slowing things down.

    How expensive is CalculateDetails? Because I am also using that.

    Code (CSharp):
    1.  struct CollisionEventImpulseJob : ICollisionEventsJob
    2.         {
    3.             public EntityCommandBuffer.ParallelWriter cBuffer;
    4.             [ReadOnly] public ComponentDataFromEntity<CollisionEventImpulse> ColliderEventImpulseGroup;
    5.             [ReadOnly] public PhysicsWorld physicsWorld;
    6.  
    7.             public void Execute(CollisionEvent collisionEvent)
    8.             {
    9.                 CollisionEvent.Details collisionDetails = collisionEvent.CalculateDetails(ref physicsWorld);
    10.  
    11.                 Entity entityBullet;
    12.  
    13.                 bool isBodyARepulser = ColliderEventImpulseGroup.HasComponent(collisionEvent.EntityA);
    14.                 entityBullet = isBodyARepulser ? collisionEvent.EntityA : collisionEvent.EntityB;
    15.  
    16.                 BulletCollisionComponent newCollision = new BulletCollisionComponent
    17.                 {
    18.                     targetEntity = isBodyARepulser ? collisionEvent.EntityB : collisionEvent.EntityA,
    19.                     contactPointEstimate = collisionDetails.EstimatedContactPointPositions[0]
    20.                 };
    21.                 cBuffer.AddComponent(0, entityBullet, newCollision);
    22.             }
    23.         }
    24.  
    25.        
    26. [System.Serializable]
    27.     public struct BulletCollisionComponent : IComponentData
    28.     {
    29.         public Entity targetEntity;
    30.         public float3 contactPointEstimate;
    31.     }
    32.  
    33.         protected override void OnUpdate()
    34.         {
    35.             if (m_BulletGroup.CalculateEntityCount() == 0) return;
    36.             //else Debug.Log("bullet group list > 0");
    37.  
    38.             var entityCommandBuffer = m_endSimulationEntityCommandBufferSystem.CreateCommandBuffer().AsParallelWriter();
    39.  
    40.             Dependency = new CollisionEventImpulseJob
    41.             {
    42.                 physicsWorld = m_BuildPhysicsWorldSystem.PhysicsWorld,
    43.                 cBuffer = entityCommandBuffer,
    44.                 ColliderEventImpulseGroup = GetComponentDataFromEntity<CollisionEventImpulse>(true),
    45.             }.Schedule(m_StepPhysicsWorldSystem.Simulation, ref m_BuildPhysicsWorldSystem.PhysicsWorld, Dependency);
    46.  
    47.             m_endSimulationEntityCommandBufferSystem.AddJobHandleForProducer(Dependency);
    48.             Dependency.Complete();
    49.         }
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    I think that this is a problem of the fact that this is single threaded. We've thought about making this multithreaded, but with no luck so far. :(

    CalculateDetails() is also expensive though. Probably not aimed for the scenario of being called a 1000 times. It should be used when you need precise contact point info, while for these "bulk" scenarios you can probably get away with a (body1Position + body2Position) / 2 or a similar estimate of the contact point. Not sure what are you using this info for?
     
    BigRookGames likes this.
  3. BigRookGames

    BigRookGames

    Joined:
    Nov 24, 2014
    Posts:
    330
    it is for the position of the collision to spawn the particle effects, as seen below.

    Yeah, I noticed they were single-threaded and was wondering if it was possible to multithreaded so I suppose that answers another question I had. Would a raycast be cheaper than the CalculateDetails? a short one the distance of the movement each frame? That would be fine too because that is really the only piece of information I need for this system.


    and the use case being:

     
  4. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    The videos are private, can't see them. :)

    Regarding ray cast, you have 2 options: ray cast the world, or simply use the collision event and ray cast vs. the hit. The latter should be much cheaper to do. You can try and compare with current implementation.

    Also try a cheap approximation of the thing, something like the thing I mentioned above with positions of 2 bodies.

    Finally, I can try and give more advice on how to multithread the thing on your own, if you are familiar with NativeStream and how it works.
     
    BigRookGames likes this.
  5. BigRookGames

    BigRookGames

    Joined:
    Nov 24, 2014
    Posts:
    330
    Shoot, sorry. The clips should be visible now.

    I will try to implement raycast on hit and see how performance is, since that is easy to do and quick to check if it will work for my design. If not, I'll look into multithreading it.

    Thanks for your help.
     
    petarmHavok likes this.
  6. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Unless your bullets can bounce then kinematic is really the best solution here at any scale. Track current/last position and do a ray/collider cast from last to current.

    Combine that with batch instantiation of projectile entities.

    A few thousand should have no noticeable impact on framerate physics wise.
     
    BigRookGames and petarmHavok like this.
  7. BigRookGames

    BigRookGames

    Joined:
    Nov 24, 2014
    Posts:
    330
    Great. Using the raycast method works really well:



    it starts slowing down a lot when instantiating bullet hit entities (which are Visual Effect Graph simple systems), but it appears to be due to rendering the transparent effects. I will need to figure out a new solution for the bullet hit vfx.
     
    petarmHavok likes this.
  8. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    I'm doing just the bodypairs collection and then process them separately in my own parallelized job.

    Also, from your code it seems that hooking into PostCreateContacts callback might be more efficient in your case. It has already calculated contact points and normals. Also, I'd advise to collect just the necessary info in the callback and then process data in custom parallel job.
     
    petarmHavok and BigRookGames like this.
  9. BigRookGames

    BigRookGames

    Joined:
    Nov 24, 2014
    Posts:
    330
    You're absolutely right. It is being calculated, and that should be the information that the custom processing should use. I wasn't able to figure out how to do so and implemented the raycast solution. I think taking off the collider and using a raycast method would be efficient enough if implemented correctly. the raycasts over such short distances are very cheap (and in my use case, it seems to be very efficient, to the effect of about 1.5ms for 6000 projectiles conducting raycasts each frame)

    But, for anything other than a straight collision point entity destroy such as a reflecting bullet or grenade, the post create contact callback sounds like it would be the way to go, assuming that it contains the data you mentioned.

    I am not familiar with the PostCreateContacts, so I am thankful you posted about it. I found info about it here: https://docs.unity3d.com/Packages/com.unity.physics@0.0/manual/simulation_modification.html (for anyone else interested).

    Thanks again!