Search Unity

"Container declared as [WriteOnly]" error. Concurrent command buffer and SharedComponentData access.

Discussion in 'Entity Component System' started by illinar, Dec 15, 2018.

  1. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    "The native container has been declared as [WriteOnly] in the job, but you are reading from it."

    Just in case, I went nuts with [ReadOnly], nothing changed. I don't understand which container there is [WriteOnly].

    Code (CSharp):
    1. [UpdateAfter(typeof(FixedUpdate))]
    2. public class ProjectileRaycastSystem : JobComponentSystem
    3. {
    4.     private struct Data
    5.     {
    6.         [ReadOnly] public readonly SharedComponentDataArray<RaycastSettings> RaycastSettings;
    7.         [ReadOnly] public readonly ComponentDataArray<Projectile> Projectile;
    8.         [ReadOnly] public readonly ComponentDataArray<LocalToWorld> LocalToWorld;
    9.         [ReadOnly] public readonly ComponentDataArray<PreviousFixedPosition> PreviousFixedPosition;
    10.         [ReadOnly] public readonly EntityArray Entities;
    11.         [ReadOnly] public readonly int Length;
    12.     }
    13.     [Inject] [ReadOnly] Data _data;
    14.  
    15.     [RequireComponentTag(typeof(Projectile))]
    16.     struct PrepareRaycasts : IJobParallelFor
    17.     {
    18.         [ReadOnly] public Data Data;
    19.         public EntityCommandBuffer.Concurrent entityCommandBuffer;
    20.  
    21.         public void Execute(int i)
    22.         {
    23.             var raycast = new Raycast();
    24.             raycast.Direction = math.rotate(Data.LocalToWorld[i].Value, Data.RaycastSettings[i].LocalDirection);
    25.             raycast.Origin = Data.LocalToWorld[i].Value.Position();
    26.             raycast.Distance = math.length(raycast.Origin - Data.PreviousFixedPosition[i].Value);
    27.             raycast.LayerMask = Data.RaycastSettings[i].LayerMask;
    28.             // entityCommandBuffer.AddComponent<Raycast>(0, Data.Entities[i], raycast);
    29.         }
    30.     }
    31.  
    32.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    33.     {
    34.         var job = new PrepareRaycasts { Data = _data, entityCommandBuffer = new EntityCommandBuffer.Concurrent() };
    35.         return job.Schedule<PrepareRaycasts>(_data.Length, 0, inputDeps);
    36.     }
    37. }
    38.  
    it doesn't like these three lines:
    Code (CSharp):
    1. raycast.Direction = math.rotate(Data.LocalToWorld[i].Value, Data.RaycastSettings[i].LocalDirection);
    2. raycast.Origin = Data.LocalToWorld[i].Value.Position();
    3. raycast.Distance = math.length(raycast.Origin - Data.PreviousFixedPosition[i].Value);
     
    Last edited: Dec 15, 2018
  2. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    I think I shouldn't use ComponentDataArray inside a job, it doesn't seem to Burst compile. Should it compile?
    If I use NativeArray instead, how do I copy data from CDA to NA? Is there anything like this:

    Code (CSharp):
    1. var job = new PrepareRaycasts
    2. {
    3.       RaycastSettings = _data.RaycastSettings.ToNativeArray()
    4. }
    Rewriting with chunk iteration.
     
    Last edited: Dec 15, 2018
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Your problem is
    [ReadOnly] public readonly SharedComponentDataArray<RaycastSettings> RaycastSettings;
    You try to read SCD on 24 line.
    You can't read SCD directly inside job.
     
  4. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Thanks for the reply but that does not appear to be the case. Still doesn't work after removing the SCD.

    Code (CSharp):
    1. [UpdateAfter(typeof(FixedUpdate))]
    2. public class ProjectileRaycastSystem3 : JobComponentSystem
    3. {
    4.     private struct Data
    5.     {
    6.         [ReadOnly] public readonly ComponentDataArray<Projectile> Projectile;
    7.         [ReadOnly] public readonly ComponentDataArray<LocalToWorld> LocalToWorld;
    8.         [ReadOnly] public readonly ComponentDataArray<PreviousFixedPosition> PreviousFixedPosition;
    9.         [ReadOnly] public readonly EntityArray Entities;
    10.         [ReadOnly] public readonly int Length;
    11.     }
    12.     [Inject] [ReadOnly] Data _data;
    13.  
    14.     struct PrepareRaycasts : IJobParallelFor
    15.     {
    16.         [ReadOnly] public Data Data;
    17.         public EntityCommandBuffer.Concurrent entityCommandBuffer;
    18.  
    19.         public void Execute(int i)
    20.         {
    21.             var raycast = new Raycast();
    22.             raycast.Direction = math.rotate(Data.LocalToWorld[i].Value, new float3(0f, 0f, 1f));// Data.RaycastSettings[i].LocalDirection);
    23.             raycast.Origin = Data.LocalToWorld[i].Value.Position();
    24.             raycast.Distance = math.length(raycast.Origin - Data.PreviousFixedPosition[i].Value);
    25.             // entityCommandBuffer.AddComponent<Raycast>(0, Data.Entities[i], raycast);
    26.         }
    27.     }
    28.  
    29.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    30.     {
    31.         var job = new PrepareRaycasts
    32.         {
    33.             Data = _data,
    34.             entityCommandBuffer = new EntityCommandBuffer.Concurrent()
    35.         };
    36.         return job.Schedule<PrepareRaycasts>(_data.Length, 0, inputDeps);
    37.     }
    38. }
    I still doubt that it should compile with Burst, but I tried. With Burst Unity freezes after the first same error message. Without Burst it throws the error message every frame.
     
    Last edited: Dec 15, 2018
  5. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    And this is slightly off topic, but I wrote another version of this system with chunk iteration. It compiles and generally works, but I don't know how to get an Entity. Or access SCD without losing the Burst support.

    Code (CSharp):
    1. using Unity.Entities;
    2. [UpdateAfter(typeof(FixedUpdate))]
    3. public class ProjectileRaycastSystem : JobComponentSystem
    4. {
    5.     private ComponentGroup group;
    6.  
    7.     protected override void OnCreateManager()
    8.     {
    9.         this.group = GetComponentGroup(typeof(PreviousFixedPosition), typeof(LocalToWorld), typeof(RaycastSettings), typeof(Projectile));
    10.     }
    11.  
    12.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    13.     {
    14.         NativeArray<ArchetypeChunk> chunks = this.group.CreateArchetypeChunkArray(Allocator.TempJob);
    15.         Job job = new Job()
    16.         {
    17.             PreviousFixedPositioType = GetArchetypeChunkComponentType<PreviousFixedPosition>(true),
    18.             RaycastSettingsType = GetArchetypeChunkComponentType<RaycastSettings>(true),
    19.             LocalToWorldType = GetArchetypeChunkComponentType<LocalToWorld>(true),
    20.             Chunks = chunks
    21.         };
    22.  
    23.         return job.Schedule(chunks.Length, 0, inputDeps);
    24.     }
    25.  
    26.     [BurstCompile]
    27.     private struct Job : IJobParallelFor
    28.     {
    29.         [ReadOnly] public ArchetypeChunkComponentType<PreviousFixedPosition> PreviousFixedPositioType;
    30.         [ReadOnly] public ArchetypeChunkComponentType<RaycastSettings> RaycastSettingsType;
    31.         [ReadOnly] public ArchetypeChunkComponentType<LocalToWorld> LocalToWorldType;
    32.         [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<ArchetypeChunk> Chunks;
    33.         [ReadOnly] public EntityCommandBuffer commandBuffer;
    34.  
    35.         public void Execute(int index)
    36.         {
    37.             Process(Chunks[index]);
    38.         }
    39.  
    40.         private void Process(ArchetypeChunk chunk)
    41.         {
    42.             var previousFixedPositionArray = chunk.GetNativeArray(PreviousFixedPositioType);
    43.             var raycastSettingsIndexArray = chunk.GetNativeArray(RaycastSettingsType);
    44.             var localToWorldArray = chunk.GetNativeArray(LocalToWorldType);
    45.             // var entities = chunk.getE
    46.  
    47.             for (int i = 0; i < chunk.Count; ++i)
    48.             {
    49.                 var raycast = new Raycast();
    50.  
    51.                 raycast.LayerMask = raycastSettingsIndexArray[i].LayerMask;
    52.                 raycast.Direction = math.rotate(localToWorldArray[i].Value, raycastSettingsIndexArray[i].LocalDirection);
    53.                 raycast.Origin = localToWorldArray[i].Value.Position();
    54.                 raycast.Distance = math.length(raycast.Origin - previousFixedPositionArray[i].Value);
    55.                 // entityCommandBuffer.AddComponent<Raycast>(0, Data.Entities[i], raycast);
    56.             }
    57.         }
    58.     }
    59. }
     
    Last edited: Dec 15, 2018
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    ArchetypeChunkEntityType
    Use shared component index
     
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    FYI.
    This buffer never playbacks, because he is not in m_Buffers array and not be handled automatically, you must use injected barriers, or call playback manually.
     
  8. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    How? EntityManager.GetAllUnique.... returns a List<>. I was able to use it, but not with Burst. AM I supposed to manually read all CSD and write them into NativeArray before I can use them?
     
    Last edited: Dec 15, 2018
  9. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Let this be an example for whoever needs one. Fully operational concurrent chunk iteration with a command buffer.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Burst;
    3. using Unity.Jobs;
    4. using Unity.Collections;
    5. using Unity.Transforms;
    6. using Unity.Mathematics;
    7. using UnityEngine;
    8. using UnityEngine.Experimental.PlayerLoop;
    9. using System.Collections.Generic;
    10.  
    11. [UpdateAfter(typeof(FixedUpdate))]
    12. public class ProjectileRaycastSystem : JobComponentSystem
    13. {
    14.     [UpdateAfter(typeof(ProjectileRaycastSystem))]
    15.     class Barrier : BarrierSystem { }
    16.     [Inject] Barrier barrier;
    17.  
    18.     private ComponentGroup group;
    19.  
    20.     protected override void OnCreateManager()
    21.     {
    22.         this.group = GetComponentGroup(typeof(PreviousFixedPosition), typeof(LocalToWorld), typeof(RaycastSettings), typeof(Projectile));
    23.     }
    24.  
    25.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    26.     {
    27.         NativeArray<ArchetypeChunk> chunks = this.group.CreateArchetypeChunkArray(Allocator.TempJob);
    28.         Job job = new Job()
    29.         {
    30.             PreviousFixedPositioType = GetArchetypeChunkComponentType<PreviousFixedPosition>(true),
    31.             RaycastSettingsType = GetArchetypeChunkComponentType<RaycastSettings>(true),
    32.             LocalToWorldType = GetArchetypeChunkComponentType<LocalToWorld>(true),
    33.             EntityType = GetArchetypeChunkEntityType(),
    34.             Chunks = chunks,
    35.             commandBuffer = barrier.CreateCommandBuffer().ToConcurrent()
    36.         };
    37.  
    38.         return job.Schedule(chunks.Length, 0, inputDeps);
    39.     }
    40.  
    41.     [BurstCompile]
    42.     private struct Job : IJobParallelFor
    43.     {
    44.         [ReadOnly] public ArchetypeChunkComponentType<PreviousFixedPosition> PreviousFixedPositioType;
    45.         [ReadOnly] public ArchetypeChunkComponentType<RaycastSettings> RaycastSettingsType;
    46.         [ReadOnly] public ArchetypeChunkComponentType<LocalToWorld> LocalToWorldType;
    47.         [ReadOnly] public ArchetypeChunkEntityType EntityType;
    48.         [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<ArchetypeChunk> Chunks;
    49.         [ReadOnly] public EntityCommandBuffer.Concurrent commandBuffer;
    50.  
    51.         public void Execute(int index)
    52.         {
    53.             var chunk = Chunks[index];
    54.             var previousFixedPositionArray = chunk.GetNativeArray(PreviousFixedPositioType);
    55.             var raycastSettingsIndexArray = chunk.GetNativeArray(RaycastSettingsType);
    56.             var localToWorldArray = chunk.GetNativeArray(LocalToWorldType);
    57.             var entityArray = chunk.GetNativeArray(EntityType);
    58.  
    59.             for (int i = 0; i < chunk.Count; ++i)
    60.             {
    61.                 var raycast = new Raycast();
    62.                 raycast.LayerMask = raycastSettingsIndexArray[i].LayerMask;
    63.                 raycast.Origin = localToWorldArray[i].Value.Position() - previousFixedPositionArray[i].Value;
    64.                 raycast.Direction = raycast.Origin - previousFixedPositionArray[i].Value;
    65.                 raycast.Distance = math.length(raycast.Distance);
    66.                 commandBuffer.AddComponent<Raycast>(index, entityArray[i], raycast);
    67.             }
    68.         }
    69.     }
    70. }
    The only thing missing is SharedComponentData access.
     
  10. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    In bed but from memory i don't think you can access shared component data in a burst job, only the index

    If you need it and can't refactor (which you should do), you should get the shared component in the system update and create seperate jobs for each shared component value.
     
  11. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    On main thread you get list of indices and values, in any case it must be short, and you can do things based on some index value.
     
  12. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Separate jobs for each CSD value? Not sure if it makes sense. Instead, I could schedule a job without burst that would copy SCD into a native array to use it in the main Burst job. (I actually don't know how but I was able to access List<> from a regular job.)

    But copying all SCD actually should be easy, because there are usually not a lot of values, that's true.

    Thanks for the answers, I think everything should work now.

    Except the initial issue is not solved, and the injection based system still throws the error. But I kinda like chunk iteration now despite all the extra code.
     
  13. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Also, is there a way to add a component to all entities in the chunk? Feels like there should be and that it should save some performance.
     
    deus0 likes this.