Search Unity

[Solved] Dynamic buffer nullreferenceexception error

Discussion in 'Data Oriented Technology Stack' started by RKFabio, Sep 9, 2019.

  1. RKFabio

    RKFabio

    Joined:
    Feb 9, 2016
    Posts:
    10
    I'm using a set of dynamic buffers on an entity to store mesh data until it's turned into a mesh array later in the frame by a different system. But for some reason, the triangles dynamic buffer throws a nullreferenceexception for the atomic safety handle whenever I try to add to it, through the write access check. I already submitted a bug report about a week ago, has anyone else had this? I checked through the code and tried to see if I'm doing something wrong, but from everything I can see the dynamic buffer is treated exactly the same as the other dynamic buffers in the script. I even changed it from int to float3 like the others and that didn't change anything.
     
  2. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    Could you share some code?
    I'm using dynamic buffers to batch meshes without any problem for a while now.
     
  3. RKFabio

    RKFabio

    Joined:
    Feb 9, 2016
    Posts:
    10
    Here's the part of the job where I declare the buffers going in:
    Code (CSharp):
    1. public void Execute(
    2.     Entity cube,
    3.     int index,
    4.     [ReadOnly] DynamicBuffer<Block> blocks,
    5.     [WriteOnly] DynamicBuffer<Vertex> vertices,
    6.     [WriteOnly] DynamicBuffer<UV> uv,
    7.     [WriteOnly] DynamicBuffer<Normal> normals,
    8.     [WriteOnly] DynamicBuffer<Triangle> triangles,
    9.     ref Translation translation
    10.     )
    and here's where I'm trying to add to the triangle buffer:
    Code (CSharp):
    1. int length = vertices.Length;
    2. triangles.Add(length - 3);
    3. triangles.Add(length - 4);
    4. triangles.Add(length - 1);
    5. triangles.Add(length - 4);
    6. triangles.Add(length - 2);
    7. triangles.Add(length - 1);
    here's the full job:
    Code (CSharp):
    1.  
    2. [RequireComponentTag(typeof(CubeTag), typeof(UpdateMeshTag))]
    3. public struct UpdateMeshJob : IJobForEachWithEntity_EBBBBBC<Block, Vertex, UV, Normal, Triangle, Translation>
    4.         {
    5.             [ReadOnly]
    6.             public NativeArray<BlockMaterial> block_materials;
    7.  
    8.             void AddFace(float3 forward, float3 block_position, short block, ref DynamicBuffer<Vertex> vertices, DynamicBuffer<UV> uv, DynamicBuffer<Normal> normals, DynamicBuffer<Triangle> triangles)
    9.             {
    10.                 float3 upward = forward.y != 0 ? new float3(0, 0, 1) : new float3(0, 1, 0);
    11.                 Quaternion dir = Quaternion.LookRotation(forward, upward);
    12.                 Vector3[] face_vertices = new Vector3[4] { new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0.5f, 0.5f, 0.5f), new Vector3(-0.5f, -0.5f, 0.5f), new Vector3(0.5f, -0.5f, 0.5f) };
    13.                 for (int i = 0; i < 4; i++)
    14.                 {
    15.                     face_vertices[i] = dir * face_vertices[i];
    16.                     face_vertices[i] += (Vector3)block_position;
    17.                 }
    18.                 vertices.Add(face_vertices[0]);
    19.                 vertices.Add(face_vertices[1]);
    20.                 vertices.Add(face_vertices[2]);
    21.                 vertices.Add(face_vertices[3]);
    22.  
    23.                 byte mat = block_materials[block][GetDirectionId(forward)];
    24.                 uv.Add(new float3(1, 1, mat));
    25.                 uv.Add(new float3(0, 1, mat));
    26.                 uv.Add(new float3(1, 0, mat));
    27.                 uv.Add(new float3(0, 0, mat));
    28.  
    29.                 var normal = dir * new float3(0, 0, 1);
    30.                 for (int i = 0; i < 4; i++)
    31.                     normals.Add(normal);
    32.  
    33.                 int length = vertices.Length;
    34.                 triangles.Add(length - 3);
    35.                 triangles.Add(length - 4);
    36.                 triangles.Add(length - 1);
    37.                 triangles.Add(length - 4);
    38.                 triangles.Add(length - 2);
    39.                 triangles.Add(length - 1);
    40.             }
    41.  
    42.  
    43.  
    44.             [ReadOnly] DynamicBuffer<Block> block_buffer;
    45.  
    46.             byte GetDirectionId(float3 direction)
    47.             {
    48.                 for (int i = 0; i < 3; i++)
    49.                 {
    50.                     if (direction[i] != 0)
    51.                         return (byte)((i * 2) + (direction[i] / 2) + 1.5f);
    52.                 }
    53.                 throw new Exception("float3 direction not correct!");
    54.             }
    55.             float3 GetDirection(byte direction_id)
    56.             {
    57.                 if (direction_id < 1 || direction_id > 6)
    58.                     throw new Exception("Invalid direction id!");
    59.                 var dir = new float3();
    60.                 dir[(int)floor((direction_id - 1) / 2)] = direction_id % 2 == 0 ? 1 : -1;
    61.                 return dir;
    62.             }
    63.             short GetBlockFromPosition(float3 block_position)
    64.             {
    65.                 if (all(block_position >= 0) && all(block_position <= 7))
    66.                 {
    67.                     return block_buffer[(int)(block_position.x + (block_position.y * 64) + (block_position.z * 8))];
    68.                 }
    69.                 else
    70.                 {
    71.                     return 0;
    72.                 }
    73.             }
    74.  
    75.             public EntityCommandBuffer.Concurrent ecb;
    76.  
    77.             public void Execute(
    78.                 Entity cube,
    79.                 int index,
    80.                 [ReadOnly] DynamicBuffer<Block> blocks,
    81.                 [WriteOnly] DynamicBuffer<Vertex> vertices,
    82.                 [WriteOnly] DynamicBuffer<UV> uv,
    83.                 [WriteOnly] DynamicBuffer<Normal> normals,
    84.                 [WriteOnly] DynamicBuffer<Triangle> triangles,
    85.                 ref Translation translation
    86.                 )
    87.             {
    88.                 block_buffer = blocks;
    89.  
    90.                
    91.  
    92.                 for (int block_index = 0; block_index < blocks.Length; block_index++)
    93.                 {
    94.                     float3 block_position = new float3(block_index % 8, floor(block_index / 64f), floor(block_index % 64 / 8f));
    95.                     short block = blocks[block_index];
    96.                     if (block > -1)
    97.                     {
    98.                         //Pattern: Left, Right, Bottom, Top, Back, Front
    99.  
    100.                         float3 float3_direction;
    101.                         for (byte direction = 1; direction < 7; direction++)
    102.                         {
    103.                             float3_direction = GetDirection(direction);
    104.                             if (GetBlockFromPosition(block_position + float3_direction) == -1)
    105.                                 AddFace(float3_direction, block_position, block, ref vertices, uv, normals, triangles);
    106.                         }
    107.                     }
    108.                 }
    109.                 ecb.RemoveComponent<UpdateMeshTag>(index, cube);
    110.                 //ecb.AddComponent<UpdatedMeshTag>(index, cube);
    111.             }
    112.         }
    113.  

    I'm fairly certain there's some kind of bug in the unity code, but I also have no idea why that would be since all the other buffers work perfectly fine. Also, if you have any tips to optimize/clean up my code, they're very welcome.
     
  4. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    417
    I couldn't replicate that problem writing to that many DynamicBuffers on a similar job.
    Could you also post the complete error? Also what versions of Unity/Entities are you using?
    I would avoid adding/removing UpdateMeshTag all the time you need to update the mesh, that way you could burst compile your job.
    To do so you would rather check if your DynamicBuffer<Block> has changed.
    IJobForEach has an attribute [ChangedFilter] that will filter out the changed components and would be great for this situation but it seems that it is not working with DynamicBuffers
    Transforming your job into an IJobChunk is another option that allows you to check if a ArchetypeChunkBufferType has changed on a per chunk basis.

    EDIT:
    [ChangedFilter] actually works with DynamicBuffers
     
    Last edited: Sep 11, 2019
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,428
    Show your schedule code
     
  6. RKFabio

    RKFabio

    Joined:
    Feb 9, 2016
    Posts:
    10
    I updated to the latest beta version of 2019.3, didn't fix the bug. The full error is:

    NullReferenceException: Object reference not set to an instance of an object
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrow (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <f206f586b3cb43239fcbb553ea9e7c16>:0)
    Unity.Entities.DynamicBuffer`1[T].CheckWriteAccess () (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/Iterators/DynamicBuffer.cs:106)
    Unity.Entities.DynamicBuffer`1[T].Add (T elem) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/Iterators/DynamicBuffer.cs:200)
    JobMeshSystem.CubeMeshSystem+UpdateMeshJob.AddFace (Unity.Mathematics.float3 forward, Unity.Mathematics.float3 block_position, System.Int16 block, Unity.Entities.DynamicBuffer`1[JobMeshSystem.Vertex]& vertices, Unity.Entities.DynamicBuffer`1[T] uv, Unity.Entities.DynamicBuffer`1[T] normals, Unity.Entities.DynamicBuffer`1[T] triangles) (at Assets/Systems/CubeMeshSystem.cs:87)
    JobMeshSystem.CubeMeshSystem+UpdateMeshJob.Execute (Unity.Entities.Entity cube, System.Int32 index, Unity.Entities.DynamicBuffer`1[T] blocks, Unity.Entities.DynamicBuffer`1[T] vertices, Unity.Entities.DynamicBuffer`1[T] uv, Unity.Entities.DynamicBuffer`1[T] normals, Unity.Entities.DynamicBuffer`1[T] triangles, Unity.Transforms.Translation& translation) (at Assets/Systems/CubeMeshSystem.cs:160)
    Unity.Entities.JobForEachExtensions+JobStruct_Process_EBBBBBC`7[T,T0,T1,T2,T3,T4,T5].ExecuteChunk (Unity.Entities.JobForEachExtensions+JobStruct_Process_EBBBBBC`7[T,T0,T1,T2,T3,T4,T5]& jobData, System.IntPtr bufferRangePatchData, System.Int32 begin, System.Int32 end, Unity.Entities.ArchetypeChunk* chunks, System.Int32* entityIndices) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/IJobForEach.gen.cs:7715)
    Unity.Entities.JobForEachExtensions+JobStruct_Process_EBBBBBC`7[T,T0,T1,T2,T3,T4,T5].Execute (Unity.Entities.JobForEachExtensions+JobStruct_Process_EBBBBBC`7[T,T0,T1,T2,T3,T4,T5]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/IJobForEach.gen.cs:7658)
    Unity.Jobs.JobHandle:ScheduleBatchedJobsAndComplete(JobHandle&)
    Unity.Jobs.JobHandle:Complete()
    Unity.Entities.EntityCommandBufferSystem:FlushPendingBuffers(Boolean) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:1217)
    Unity.Entities.EntityCommandBufferSystem:OnUpdate() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:1211)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:800)
    Unity.Entities.ComponentSystemBase:Update() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:284)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystemGroup.cs:602)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:800)
    Unity.Entities.ComponentSystemBase:Update() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ComponentSystem.cs:284)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/ScriptBehaviourUpdateOrder.cs:144)

    And the code where I schedule the job is:
    Code (CSharp):
    1.  
    2. protected override JobHandle OnUpdate(JobHandle inputDeps)
    3. {
    4. if (block_materials.IsCreated)
    5. {
    6. var job = new UpdateMeshJob() { block_materials = block_materials, ecb = ecbs.CreateCommandBuffer().ToConcurrent() }.Schedule(this, inputDeps);
    7. ecbs.AddJobHandleForProducer(job);
    8. return job;
    9. }
    10. return inputDeps;
    11. }
    I'll look into the change filter, that'll probably help a lot. Thanks for the suggestion!

    I still haven't come up with any ideas for why the dynamicbuffer is throwing this error. It can't be that it's initializing wrong, because if I do add commands from outside a job it works fine. Somehow it's the job that's causing it to not have a safety handle properly.

    I figured out what the problem is. If I remove the [ReadOnly] tag from the block dynamic buffer, it fixes the problem. I already tried removing the writeonly tags a while ago, seeing as those were more likely to be a problem, but I just remembered that I already have had problems related to [readonly] tags used on any components that are being called alongside dynamic buffers. Seems I just have to go without readonly optimizations and it'll be fine.

    Thanks for the help!