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.
  2. Dismiss Notice

Invalid BlobAssetReference with CollisionWorld.CastRay and Fixed Time Step

Discussion in 'Physics for ECS' started by alfiare, Dec 18, 2020.

  1. alfiare

    alfiare

    Joined:
    Feb 10, 2017
    Posts:
    30
    In my game I am doing a raycast every frame using BuildPhysicsWorld.PhysicsWorld.CollisionWorld.CastRay(...). I do this since I have a need to know all the time what's under the users mouse cursor for highlighting, information etc. The game is voxel based and so with modifications I regenerate meshes and remake mesh colliders which then replace the PhysicsCollider on the entity for that section. I have found that when I replace the PhysicsCollider on an entity it seems that the next frame when I run CastRay I get the below exception. This goes away when I add world.GetExistingSystem<FixedStepSimulationSystemGroup>().FixedRateManager = null; in my world startup. So I'm guessing what's happening is that when the collider is replaced on the entity the BlobAssetReference of the old collider is being released, but since physics is running at fixed rate the CollisionWorld isn't rebuilt before I hit CastRay again, and therfore it ends up trying to access the old BlobAssetReference.

    So questions is...am I doing this wrong? Do I need to be only doing things like CastRay within FixedStepSimulationSystemGroup and cannot do that every frame? Maybe there's some way to know that the CollisionWorld is "dirty" and therefore shouldn't be used until rebuilt? Is this maybe a bug that will be fixed someday and I shouldn't worry about it?


    Exception:
    InvalidOperationException: The BlobAssetReference is not valid. Likely it has already been unloaded or released.
    Unity.Entities.BlobAssetReferenceData.ValidateNonBurst () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Blobs.cs:254)
    Unity.Entities.BlobAssetReferenceData.ValidateNotNull () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Blobs.cs:270)
    Unity.Entities.BlobAssetReference`1[T].get_Value () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Blobs.cs:358)
    Unity.Physics.RigidBody.CastRay[T] (Unity.Physics.RaycastInput input, T& collector) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/RigidBody/RigidBody.cs:69)
    Unity.Physics.Broadphase+BvhLeafProcessor.RayLeaf[T] (Unity.Physics.RaycastInput input, System.Int32 rigidBodyIndex, T& collector) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/World/Broadphase.cs:592)
    Unity.Physics.BoundingVolumeHierarchy.Raycast[TProcessor,TCollector] (Unity.Physics.RaycastInput input, TProcessor& leafProcessor, TCollector& collector) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/Geometry/BoundingVolumeHierarchy.cs:370)
    Unity.Physics.Broadphase.CastRay[T] (Unity.Physics.RaycastInput input, Unity.Collections.NativeArray`1[T] rigidBodies, T& collector) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/World/Broadphase.cs:503)
    Unity.Physics.CollisionWorld.CastRay[T] (Unity.Physics.RaycastInput input, T& collector) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/World/CollisionWorld.cs:199)
    Unity.Physics.QueryWrappers.RayCast[T] (T& target, Unity.Physics.RaycastInput input, Unity.Physics.RaycastHit& closestHit) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/Queries/Collidable.cs:105)
    Unity.Physics.CollisionWorld.CastRay (Unity.Physics.RaycastInput input, Unity.Physics.RaycastHit& closestHit) (at Library/PackageCache/com.unity.physics@0.5.1-preview.2/Unity.Physics/Collision/World/CollisionWorld.cs:195)
    Stratheon.Client.ActionsSystem.SelectionTracker.GetEntityUnderCursor () (at Assets/Assemblies/Stratheon.Client/ActionsSystem/SelectionTracker.cs:245)
    Stratheon.Client.ActionsSystem.SelectionTracker.HighlightMouseOverEntity () (at Assets/Assemblies/Stratheon.Client/ActionsSystem/SelectionTracker.cs:189)
    Stratheon.Client.ActionsSystem.Actions.DefaultAction.Update (Unity.Jobs.JobHandle inputDeps) (at Assets/Assemblies/Stratheon.Client/ActionsSystem/Actions/DefaultAction.cs:37)
    Stratheon.Client.ActionsSystem.ActionManager.UpdateCurrentAction (Unity.Jobs.JobHandle inputDeps) (at Assets/Assemblies/Stratheon.Client/ActionsSystem/ActionManager.cs:108)
    Stratheon.Client.ECS.Systems.ActionSystem.InternalOnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Assemblies/Stratheon.Client/ECS/Systems/ActionSystem.cs:57)
    Stratheon.Shared.ECS.Systems.BaseZoneSystem.OnUpdate () (at Assets/Assemblies/Stratheon.Shared/ECS/Systems/BaseZoneSystem.cs:48)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/SystemBase.cs:413)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:435)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:440)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:387)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:435)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:387)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)
     
  2. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Hi @alfiare , I'll point out some things that might be causing problems:
    1. When querying PhysicsWorld, you want to ensure that your job is finished before next BuildPhysicsWorld starts, by supplying your job handle to m_BuildPhysicsWorld.AddInputDependencyToComplete().
    2. If you want to change ECS data, you need to do it after all jobs from ExportPhysicsWorld are done, which means using m_ExportPhysicsWorld.GetOutputDependency() as input dependency for your jobs (or completing it at in your system.Update()).
    3. Note that systems in SimulationSystemGroup have variable time step, while systems in FixedStepSimulationSystemGroup have fixed time step. So, your system might update multiple times after a physics step and thus you might be querying a stale PhysicsWorld (if you change it in your first update).
    4. If you switch PhysicsCollider on Entity (ECS data), a RigidBody in PhysicsWorld may point to deallocated memory, since ECS owns that data and can destroy it despite the fact that RigidBody needs it. So, you'll want to allow physics to run after doing such a thing, so that PhysicsWorld can pick up the ECS data you changed. I'm intentionally leaving this open to different solutions, you could do this in FixedStepSimulationSystemGroup or some other way.

    Not sure which of the problems you hit exactly, but please take all of the above into account. Let me know if this helps, hoping it will!
     
  3. alfiare

    alfiare

    Joined:
    Feb 10, 2017
    Posts:
    30
    Thanks @milos85miki that is very helpful, let me just get a little into the structure of the ECS world I'm in...

    InitializationSystemGroup
    - Some other systems
    SimulationSystemGroup
    FixedTimeStepSimulationSystemGroup
    - The physics systems, ending with EndFixedStepSimulationEntityCommandBufferSystem
    TransformSystemGroup
    MyStartSimulationSystemGroup
    Some Unrelated Stuff
    MyEndSimulationSystemGroup
    - The system that performs the CastRay is here (lets call it CastRaySystem for reference), happens main thread
    PresentationSystemGroup
    - The system that alters the PhysicsCollider is here, happens main thread


    I thought (and I could be wrong on this), that the physics systems all conclude with the EndFixedStepSimulationEntityCommandBufferSystem, assuming that's true then my CastRaySystem should always be after any jobs for the physics systems are done (when the fire by fixed time step), and the CastRay it does is main thread (non-job) so it won't bleed into the next run of the physics systems. So I think for #1 and #2 you mentioned I should be OK.

    I think your #3 and #4 are what I'm hitting, which makes it seem like you really can't safely query the physics world outside the FixedTimeStepSimulationSystemGroup unless you never change it. As in to safely switch a PhysicsCollider I would need to make the switch, then make sure to not do anything with the PhysicsWorld until I know the FixedTimeStepSimulationSystemGroup has run again (I guess I could add a system to that group to track that).

    Maybe this could be done by cloning the collision world whenever the FixedTimeStepSimulationSystemGroup is run, but not sure that would clone the actual blob assets (I don't think it does?). That seems kind of high over head for it.

    I'm planning to make the fixed time step for the physics world much less than 50/60fps, probably more like 10fps since I'm not using physics simulation really, just colliders and raycasting.
     
  4. alfiare

    alfiare

    Joined:
    Feb 10, 2017
    Posts:
    30
    I'm thinking that with this probably the best thing for me is to simply disable fixed time step all together, in general fixed time step is more to allow for physics to run consistently at some rate when the normal frame rate dips below that. Which really isn't my use case at all, I don't need it to ever run faster than the frame rate. Then with fixed time step disabled I don't have to complicate things by trying to buffer inputs across frames and worry about restricting when I use the collision world etc.
     
    petarmHavok likes this.
  5. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Yes, @alfiare , you're right about most things.
    If you don't need collisions, it's probably easier to not enforce fixed time step on physics.
    After physics is done, you can do casts whenever you'd like, until you change the relevant data (like PhysicsCollider in ECS data). From that point onward, you should not cast until PhysicsWorld gets rebuilt in next physics step.

    Please note 1 subtle thing: systems are chained via UpdateAfter and UpdateBefore attributes, but that only affects the order of their OnUpdate() executions. If some system spawns a job on a worker thread, it is not guaranteed that the next system's OnUpdate() or jobs will wait for its completion. I'd advise doing m_ExportPhysicsWorld.GetOutputDependency().Complete() before doing casts from the main thread in your system.OnUpdate(), just to be 100% sure.
    Edit: When 2 jobs read/write the same ECS data dependency between them is created implicitly, but that's not the case here.
     
    Last edited: Dec 21, 2020