Search Unity

[SOLVED] Atomic Interlocked operations on NativeArray

Discussion in 'Entity Component System' started by korzen303, Mar 22, 2018.

  1. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    Hi,

    I have got a jobified fluid simulation. Nearest neighbor search is accelerated via uniform grid. Each fluid particle writes its id to the grid cell. As many particles can write to the same cell at the same time, I need to atomically increment the particles counter for the given cell.

    Normally, i would simply do it like this:
    Code (CSharp):
    1.  
    2. public NativeArray<int> particlesInCellCount;
    3. [...]
    4. Interlocked.Increment(ref particlesInCellCount[cellId]);
    5.  
    but I get Error CS0206 A property or indexer may not be passed as an out or ref parameter

    Any ideas how to make it to work with NativeContainers?

    Just to add that atomic increment works very well for this use case also on GPUs (both ComputeShader and CUDA)
     
  2. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    So first off assuming nothing changes particlesInCellCount[cellId] via any other way then that code, it should be fine barring the ref issue. But it's not actually atomic because you have two operations there. So that can potentially come back and bite you later on.

    Personally I'd go with a via a flow where you write then query, or divide up the grid so you have threads operating on discrete parts of it using the supported parallel for stuff.
     
  3. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    417
    The `NativeArray<T>` indexer is providing a by value and not by ref. With the upcoming transition of Unity to compile using the latest C# 7.x features, we should be able to provide a proper by `ref` indexer. We have a plan to provide this as part of 2018.2

    That being said, you can likely workaround this by using unsafe pointer with the native array, like:


    Interlocked.Increment(ref ((int*)particlesInCellCount.GetUnsafePtr())[cellId])
     
  4. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    Thanks @xoofx!

    Unfortunately, I am not able to test it out as I am getting:
    Code (CSharp):
    1.  
    2. ReadWriteBuffers are restricted to only read & write the element at the job index. You can use double buffering strategies to avoid race conditions due to reading & writing in parallel to the same elements from a job.
    3.  
    I understand why I am getting this, but shouldn't it be a warning, a hint rather than an error?
    The atomic increment should prevent the race conditions.
    [EDIT] How can I switch off this check in the editor[/EDIT]


    Here is a complete code:
    Code (CSharp):
    1.  
    2. public struct UpdateGridJob : IJobParallelFor
    3. {
    4.     public int gridSizeX;
    5.     public int gridSizeY;
    6.     public int gridSizeZ;
    7.     public int maxParticlesInCell;
    8.     public Matrix4x4 mat;
    9.     [ReadOnly]
    10.     public NativeArray<Vector4> positions;
    11.     public NativeArray<int> particlesInCellCount;
    12.     public NativeArray<int> particlesInCellIds;
    13.     public void Execute(int i)
    14.     {
    15.         Vector4 pos = mat.MultiplyPoint(positions[I]);
    16.         int x = (int)pos.x;
    17.         int y = (int)pos.y;
    18.         int z = (int)pos.z;
    19.  
    20.         int cellId = x + y * gridSizeX + z * gridSizeX * gridSizeY;
    21.         if (particlesInCellCount[cellId] < maxParticlesInCell)
    22.         {
    23.             particlesInCellIds[cellId * maxParticlesInCell + particlesInCellCount[cellId]] = i;
    24.             //particlesInCellCount[cellId]++;
    25.              unsafe
    26.             {
    27.                 particlesInCellCount[cellId] = Interlocked.Increment(
    28.                                 ref ((int*)particlesInCellCount.GetUnsafePtr())[cellId]);
    29.             }
    30.         }
    31.     }
    32. }
    33.  
     
    Last edited: Mar 23, 2018
  5. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    OK, this is explained here: How to write to another index with the jobsystem?

    We can use [NativeDisableContainerSafetyRestriction] or [NativeDisableParallelForRestriction] attributes to disable the checks and take care of synchronization manually
     
    Last edited: Mar 23, 2018