Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

[SOLVED] Atomic Interlocked operations on NativeArray

Discussion in 'Data Oriented Technology Stack' started by korzen303, Mar 22, 2018.

  1. korzen303


    Oct 2, 2012

    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):
    2. public NativeArray<int> particlesInCellCount;
    3. [...]
    4. Interlocked.Increment(ref particlesInCellCount[cellId]);
    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


    Apr 15, 2013
    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


    Unity Technologies

    Nov 5, 2016
    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


    Oct 2, 2012
    Thanks @xoofx!

    Unfortunately, I am not able to test it out as I am getting:
    Code (CSharp):
    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.
    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):
    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;
    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. }
    Last edited: Mar 23, 2018
  5. korzen303


    Oct 2, 2012
    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