Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Bug Physics Jobs register write access to transforms when only running with static bodies (includes fix)

Discussion in 'Physics for ECS' started by Zec, Dec 17, 2021.

  1. Zec


    Jan 3, 2012
    What's the issue?
    Our server only uses static bodies in it's physics world to allow us to raycast and similar via the PhysicsWorld. We use zero dynamic bodies in that application. What I recently discovered was that even if we don't have any dynamic bodies, the BuildPhysicsWorld and ExportPhysicsWorld systems still register write access to the component types for Translation/Rotation/LocalToWorld etc.

    Why is this a problem?
    This means that any system that purely reads Translation after the physics world updating starts will forcibly complete the BuildPhysicsWorld jobs as those are registered with write-access to the Translation, while they could in fact keep running in the background as our pure static-body world never write anything.

    Problematic code and solution
    There were a few reasons to this with easy fixes which you can see in the attached screenshots:
    1. The body queries in BuildPhysicsWorld were written using typeof(Translation) etc which is implicitly cast to a ReadWrite accessor. The query for the static bodies should use ComponentType.ReadOnly<>()

    2. Since dependencies are system-wide, using "GetEntityQuery" is a problem as that would cause the dependencies in BuildPhysicsWorld to always contain a write-dependency to Translation etc, since the dynamic queries are registered in the system. What we can instead do is to create the queries via the entity manager and dispose them OnDestroy. That way the dependency won't ever be registered unless we have had a dynamic body in the world. The write-dependency will in this adjusted case be registered when the writeable ComponentTypeHandles are fetched. This will still cause the jobs to have write-access to the components if a dynamic body have ever existed though, but it works for our case where we never use them.

    3. ExportPhysicsWorld is always executed in editor, running ExportDynamicBodiesJob against the dynamic body query, registering write accessors

    4. BuildPhysicsWorld runs IntegrityChecks against the dynamic query even if the query is empty, which was very easy to solve as the same system had already pre-calculated whether there were any dynamic bodies. This is an editor-only issue but still a bit annoying and easy to fix.

    5. ExportPhysicsWorld always runs IntegrityChecks in the editor against the dynamic body query, registering write accessors
  2. snacktime


    Apr 15, 2013
    Less well known but better solution is just create your own physics world and leave ECS out of the picture entirely. You set up the containers, control the building so it only happens when you add/remove bodies. You might not even need an entity for the body here depending.

    You will have to use the same combine and set back approach for the dependency like you do with passing PhysicsWorld to a job.

    This references other stuff not included but has the important bits for the core flow.
    Zec likes this.
  3. Zec


    Jan 3, 2012
    The response is a bit late, but I just wanted to thank you for this. The insights that your sample gave made us realize that the physics world building has a decently easy API to work with, and that the standard physics systems aren't really needed at all when only running with static physics.

    We still keep our physics updating in an ECS system as that works better for our project, but that new system now caches the large arrays of data that the physics is built upon and add/remove colliders or update their positions only when necessary. We now have zero physics systems from Unitys package on our server. We only have our own one single system that manages it all in a quite simple manner. Our solution only works for static physics, but that's all that we needed. It removed all the massive overhead of the existing stateless solution where the input data is built from scratch every frame.

    I exposed one internal method, but other than that I could implement the whole feature without relying on non-public APIs which makes it less risky if we're ever upgrading to new versions.

    Before (pushing our world size to the limit with JobThreads off for better profiler cost visualization):

    After (pushing our world size to the limit with JobThreads off for better profiler cost visualization):

    Don't look at the milliseconds as the two samples are a bit different. What I wanted to show is the relative size of BuildBranchesJob to the whole physics updating. A massive improvement.

    Attached Files: