Search Unity

Converting a ComputeBuffer To a NativeArray

Discussion in 'Entity Component System' started by GeorgeAdamon, Aug 5, 2019.

  1. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    Hello everyone,

    I am trying to use the ConvertExistingDataToNativeArray function, in order to pass the contents of a ComputeBuffer to a NativeArray<float>, avoiding the ComputeBuffer's GetData(T[]) function.

    My function so far looks like this (performs some calculations on a RenderTexture via a Compute Shader, then gets the results back as a ComputeBuffer):

    Code (CSharp):
    1.  public unsafe void GetCellStates(ref ComputeBuffer buffer, ref NativeArray<float> array, in RenderTexture tex)
    2.     {
    3.         readBackShader.SetTexture(getPixelsKernel,"Input", tex);
    4.         readBackShader.SetBuffer(getPixelsKernel, "Data", buffer);
    5.         readBackShader.Dispatch(getPixelsKernel, CA3DScript.CountX/16, CA3DScript.CountY/8, CA3DScript.CountZ/8);
    6.      
    7.         array = ConvertExistingDataToNativeArray<float>( buffer.GetNativeBufferPtr().ToPointer(), CA3DScript.totalCount * 4, Allocator.Persistent);
    8. }
    The problem is that I get a Null Reference Exception every time I try to do something with the NativeArray afterwards ( Copying to another array, accessing elements, passing it to a Job), although it has a non-zero length and IsCreated = true.

    NullReferenceException: Object reference not set to an instance of an object
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrow (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at C:/buildslave/unity/build/Runtime/Export/Jobs/AtomicSafetyHandle.bindings.cs:147)


    I don't have a good idea as to what might be wrong. Could some Pointer Wizard have a look ? :)

    Thanks in advance!
     
    Last edited: Aug 5, 2019
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    After a
    NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray

    you also need a
    NativeArrayUnsafeUtility.SetAtomicSafetyHandle
     
  3. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    @tertle I'm glad I get the chance thank you for your huge contribution to the forums!
    Based on your suggestions in this thread i modified my code:

    Code (CSharp):
    1.    public unsafe void GetCellStates(out NativeArray<float> array)
    2.     {
    3.         array = ConvertExistingDataToNativeArray<float>( getPixelsBuffer.GetNativeBufferPtr().ToPointer(), CA3DScript.totalCount * 4, Allocator.Invalid);
    4.  
    5. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    6.         SetAtomicSafetyHandle(ref array, AtomicSafetyHandle.Create());
    7. #endif
    8.     }
    However anytime I try to access the NativeArray outside the function that created it, ie in a Parallel Job (as [ReadOnly]), Unity throws a Null Reference exception:


    NullReferenceException: Object reference not set to an instance of an object
    Unity.Collections.LowLevel.Unsafe.UnsafeUtility.ReadArrayElement[T] (System.Void* source, System.Int32 index) (at C:/buildslave/unity/build/Runtime/Export/Unsafe/UnsafeUtilityPatched.cs:48)
    Unity.Collections.NativeArray`1[T].get_Item (System.Int32 index) (at C:/buildslave/unity/build/Runtime/Export/NativeArray/NativeArray.cs:139)
    CA3DInterface+AdditiveBlendJob.Execute (System.Int32 index) (at Assets/CA3DInterface.cs:265)
    Unity.Jobs.IJobParallelForExtensions+ParallelForJobStruct`1[T].Execute (T& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at C:/buildslave/unity/build/Runtime/Jobs/Managed/IJobParallelFor.cs:43)
     
  4. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    GetData is pulling data back to the CPU, from the GPU memory. You can't bypass this step.

    You have 2 options:
    * Use GetData and copy into NativeArray afterwards
    * Use https://docs.unity3d.com/ScriptReference/Rendering.AsyncGPUReadback.html, which also copies the data back from the GPU, but does it asynchronously. That is to say, it's not faster than GetData, but it doesn't block the main thread while it waits. So you can do other stuff and check back on the next frame, if it's ready yet. If you do need to block, this API also has a WaitForCompletion method, which may get you back to somewhere close to GetData performance, but with the bonus that you can get the data as a NativeArray, rather than the extra copy required with GetData.

    In summary, always try to avoid copying data back to the CPU from the GPU in performance critical code, unless you are willing to wait a couple of frames for it to arrive, in which case use the async api.
     
  5. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    Just my 5 cents. New API got introduced with 2019.3 which allows to not just get a NativeArray<> with AsyncGPUReadback, but also fill a NativeArray of your choice with "AsyncGPUReadback.RequestIntoNativeArray(ref NativeArray_1, ComputeBuffer)". I haven't used it myself, but this should allow to fill arrays with any allocation directly.
     
  6. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    richardkettlewell likes this.
  7. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    Hi,

    @richardkettlewell I get the async stuff bu I don't get why there is a SetData<T>(NativeArray<T> data) but no corresponding GetData<T>(NativeArray<T> data)? Could you please clarify?

    Thanks
     
    BeerCanAI and GeorgeAdamon like this.
  8. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    Which part of the scripting API are you referring to?
     
  9. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    ComputBuffer
     
  10. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    It looks like we just haven’t added it yet. We can look at adding it.
     
    GeorgeAdamon likes this.
  11. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    That would be great, removing an unnecessary CopyTo() call.
     
    richardkettlewell likes this.
  12. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    We have now scheduled this addition. Thanks for raising it!
     
    BeerCanAI and pal_trefall like this.
  13. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    Awesome !;)
     
  14. rustinlee

    rustinlee

    Joined:
    May 31, 2014
    Posts:
    20
    Was this ever added?
     
  15. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    No - we discussed it internally and decided we didn't want to encourage use of an API that was (unavoidably) very inefficient.

    You should use the AsyncGPUReadback API instead. With this, if you call complete immediately, it will behave just like GetData, but in your code it is more obvious that you are doing something very slow. With this API you are also encouraged to think about whether you can collect the results later.
     
    Last edited: Aug 6, 2020
    Walter_Hulsebos and marwi like this.