Search Unity

Resolved Updating TerrainCollider using ForEach().ScheduleParallel() works, fails when using IJobEntityBatch

Discussion in 'Physics for ECS' started by gtzpower, Oct 19, 2021.

  1. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    I am having an issue where I can use Entities.ForEach().ScheduleParallel() to update terrain colliders in a multi-threaded fashion (when I add a large DynamicBuffer to entities to break up the chunk), but I cannot get IJobEntityBatch to work due to "The BlobAssetReference is not valid." errors.

    I have attached a repro project. Here is the system code directly:
    Code (CSharp):
    1.  
    2. using System.Diagnostics;
    3. using Unity.Burst;
    4. using Unity.Collections;
    5. using Unity.Entities;
    6. using Unity.Mathematics;
    7. using Unity.Physics;
    8. using Unity.Physics.Systems;
    9.  
    10. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    11. [UpdateBefore(typeof(BuildPhysicsWorld))]
    12. public class TerrainColliderUpdateSystem : SystemBase
    13. {
    14.    EntityQuery query;
    15.  
    16.    private struct UpdateTerrainColliderJob : IJobEntityBatch
    17.    {
    18.        [ReadOnly] public ComponentTypeHandle<PhysicsCollider> PhysicsColliderTypeHandle;
    19.        public BufferTypeHandle<HeightElement> HeightsTypeHandle;
    20.  
    21.        [BurstCompile]
    22.        public void Execute(ArchetypeChunk batchInChunk, int batchIndex)
    23.        {
    24.            var physicsColliders = batchInChunk.GetNativeArray(PhysicsColliderTypeHandle);
    25.            var heightsBuffers = batchInChunk.GetBufferAccessor(HeightsTypeHandle);
    26.  
    27.            for (int i = 0; i < batchInChunk.Count; i++)
    28.            {
    29.                var collider = physicsColliders[i];
    30.                var heightsBuffer = heightsBuffers[i];
    31.  
    32.                var colliders = Unity.Physics.TerrainCollider.Create(
    33.                        heightsBuffer.Reinterpret<float>().AsNativeArray(),
    34.                        new int2(ReproSetup.Resolution),
    35.                        new float3(1),
    36.                        Unity.Physics.TerrainCollider.CollisionMethod.Triangles
    37.                );
    38.  
    39.                collider.Value.Dispose();
    40.                collider.Value = colliders;
    41.            }
    42.        }
    43.    }
    44.  
    45.    protected override void OnCreate()
    46.    {
    47.        query = GetEntityQuery(typeof(PhysicsCollider), typeof(HeightElement));
    48.    }
    49.  
    50.    protected override void OnUpdate()
    51.    {
    52.        var mode = ReproSetup.Mode;
    53.        Dependency.Complete();
    54.        var stopWatch = new Stopwatch();
    55.        stopWatch.Start();
    56.  
    57.  
    58.        if (mode == SystemMode.ForEach)
    59.        {
    60.            Entities
    61.                    .WithBurst()
    62.                    .ForEach((ref PhysicsCollider collider,
    63.                            ref DynamicBuffer<HeightElement> heightsBuffer) =>
    64.                    {
    65.                        var colliders = Unity.Physics.TerrainCollider.Create(
    66.                                            heightsBuffer.Reinterpret<float>().AsNativeArray(),
    67.                                            new int2(ReproSetup.Resolution),
    68.                                            new float3(1),
    69.                                            Unity.Physics.TerrainCollider.CollisionMethod.Triangles
    70.                                    );
    71.  
    72.                        collider.Value.Dispose();
    73.                        collider.Value = colliders;
    74.                    }).ScheduleParallel();
    75.        }
    76.        else
    77.        {
    78.            var job = new UpdateTerrainColliderJob
    79.            {
    80.                PhysicsColliderTypeHandle = GetComponentTypeHandle<PhysicsCollider>(),
    81.                HeightsTypeHandle = GetBufferTypeHandle<HeightElement>(),
    82.            };
    83.  
    84.            // Split the chunk up for parallel processing
    85.            Dependency = job.ScheduleParallel(query, 4, Dependency);
    86.        }
    87.  
    88.  
    89.        Dependency.Complete();
    90.        stopWatch.Stop();
    91.        UnityEngine.Debug.Log($"System Execution Time: {stopWatch.ElapsedTicks / 10000.0f}");
    92.    }
    93. }
    94.  
    If using the repro project, in the SampleScene, the "ReproSetup" game object has a selector to run as "For Each" or "Job". With "For Each" selected, the workload runs successfully. You can even see that the frametime reduces significantly if you set the InternalCapacity attribute on the DummyElement.cs to 3000 (just breaks up the chunk of entities). So multi-threaded shape changes do appear to work. However, with "Job" selected, we get the following error:
    Code (CSharp):
    1. InvalidOperationException: The BlobAssetReference is not valid. Likely it has already been unloaded or released.
    2. Unity.Entities.BlobAssetReferenceData.ValidateNonBurst () (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/Blobs.cs:260)
    3. Unity.Entities.BlobAssetReferenceData.ValidateNotNull () (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/Blobs.cs:277)
    4. Unity.Entities.BlobAssetReference`1[T].get_Value () (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/Blobs.cs:365)
    5. Unity.Physics.Broadphase+PrepareStaticBodyDataJob.ExecuteImpl (System.Int32 index, System.Single aabbMargin, Unity.Collections.NativeArray`1[T] rigidBodies, Unity.Collections.NativeArray`1[T] aabbs, Unity.Collections.NativeArray`1[T] points, Unity.Collections.NativeArray`1[T] filtersOut, Unity.Collections.NativeArray`1[T] respondsToCollisionOut) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Collision/World/Broadphase.cs:831)
    6. Unity.Physics.Broadphase+PrepareStaticBodyDataJob.Execute (System.Int32 index) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Collision/World/Broadphase.cs:819)
    7. Unity.Jobs.IJobParallelForDeferExtensions+JobParallelForDeferProducer`1[T].Execute (T& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.jobs@0.8.0-preview.23/Unity.Jobs/IJobParallelForDefer.cs:62)
    8.  
    This is the first error that appears in the log, there are many others afterwards, but I assume they are a result of this error. The errors keep coming when using Unity Physics. When using Havok Physics, the editor just crashes.

    Any thoughts on how I can parallelize this workload other than by loading the entities up with useless data to get the chunks broken up?
     

    Attached Files:

  2. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    I figured it out. I just needed to add
    Code (CSharp):
    1. physicsColliders[i] = collider;
    below line 40 in the system code above. I guess the ForEach uses a reference whereas the Job uses a copy.