Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Feedback [SOLVED]ComputeBuffer and AsyncGPUReadbackRequest

Discussion in 'Shaders' started by komandos332, Jan 4, 2020.

  1. komandos332

    komandos332

    Joined:
    Dec 11, 2016
    Posts:
    3
    Hello everyone.

    I'm trying to findout what is going wrong with my code. The thing i want to do is set texture to ComputeShader,
    save all pixels to array of struct:
    Code (CSharp):
    1. struct MapDataStruct
    2. {
    3.     int x;
    4.     int y;
    5.     float a;
    6. };
    and here is whole code of compute shader:

    Code (CSharp):
    1. struct MapDataStruct
    2. {
    3.     int x;
    4.     int y;
    5.     float a;
    6. };
    7.  
    8. int Width;
    9. RWTexture2D<float4> Input;
    10. RWStructuredBuffer<MapDataStruct> Output;
    11.  
    12. [numthreads(8, 8, 1)]
    13. void MapToStructs(uint3 id : SV_DispatchThreadID)
    14. {
    15.     int index = Width * id.y + id.x;
    16.     Output[index].x = id.x;
    17.     Output[index].y = id.y;
    18.     Output[index].a = Input[id.xy].a;
    19. }
    Next in C# script it looks like this:
    Code (CSharp):
    1.  ComputeBuffer result = new ComputeBuffer ( tex.width * tex.height, this.GetMapStructStride ( ), ComputeBufferType.Structured );
    2.             int kernel = this.MapComputationsShader.FindKernel ( "MapToStructs" );
    3.             this.MapComputationsShader.SetInt ( "Width", tex.width );
    4.             this.MapComputationsShader.SetTexture ( kernel, "Input", ghelp.GetMiniMapScript ( ).renderTexture_Fow );
    5.             this.MapComputationsShader.SetBuffer ( kernel, "Output", result );
    6.             this.MapComputationsShader.Dispatch ( kernel, tex.width / 8, tex.height / 8, 1 );
    7.  
    8.             int dataLength = tex.width * tex.height;
    9.             int oneReadSize = dataLength / 64;
    10.  
    11.             auroraSurfaceFowMap.MapPixelsData = new List<MapDataStruct> ( );
    12.             for ( int currentLine = 0; currentLine < dataLength / oneReadSize; currentLine++ )
    13.             {
    14.                 AsyncGPUReadbackRequest request = AsyncGPUReadback.Request ( result, oneReadSize, oneReadSize * currentLine );
    15.                 while ( !request.done )
    16.                 {
    17.                     yield return null;
    18.                 }
    19.                 List<MapDataStruct> part = new List<MapDataStruct> ( request.GetData<MapDataStruct> ( ).Where ( s => s.a < 1f ).ToArray ( ) );
    20.                 if ( part.Count > 0 )
    21.                 {
    22.                     auroraSurfaceFowMap.MapPixelsData.AddRange ( part );
    23.                     Debug.Log ( $"Added {part.Count} pixels." );
    24.  
    25.                 }
    26.                 yield return null;
    27.             }
    28.             result.Dispose ( );
    Everything was okay when i was using just AsyncGPUReadback.Request ( result ) - without specifying size and offset parameters, but the issue was that there was about 200ms screen freeze when
    Code (CSharp):
    1. request.GetData<MapDataStruct> ( )
    was invoked. Then i just found out that i can tell how big part of data i want to get from the buffer from
    Code (CSharp):
    1. AsyncGPUReadback.Request()
    . In my opinion, this just should work but instead of this im getting always the same amount of pixels from ComputeBuffer
    Code (CSharp):
    1. Debug.Log ( $"Added {part.Count} pixels." );
    upload_2020-1-4_20-3-33.png
     

    Attached Files:

  2. komandos332

    komandos332

    Joined:
    Dec 11, 2016
    Posts:
    3
    Problem solved.

    What was wrong.
    As first, Request() method requires parameters size and offset in bytes. So if i want to read some amount of pixels which are repsented by structure:
    Code (CSharp):
    1. struct MapDataStruct
    2. {
    3.     int x;
    4.     int y;
    5.     float a;
    6. };
    Then easly multiply size (amount of pixels) by size in bytes of this structure, can be done like this:
    Code (CSharp):
    1. public int GetMapStructStride ( )
    2.     {
    3.         unsafe
    4.         {
    5.             return sizeof ( MapDataStruct );
    6.         }
    7.     }

    So after all, instead of:


    Code (CSharp):
    1. AsyncGPUReadback.Request ( result, oneReadSize, oneReadSize * currentLine )
    SHOULD BE:
    Code (CSharp):
    1. Request(result, oneReadSize * dataStride, oneReadSize * currentLine * dataStride)
    This is how whole working code looks like:
    Code (CSharp):
    1.             #region ================================================================ COMPUTE SHADER STUFF
    2.             int dataStride = this.GetMapStructStride ( );
    3.             ComputeBuffer result = new ComputeBuffer ( tex.width * tex.height, dataStride, ComputeBufferType.Structured );
    4.             int kernel = this.MapComputationsShader.FindKernel ( "MapToStructs" );
    5.             this.MapComputationsShader.SetInt ( "Width", tex.width );
    6.             this.MapComputationsShader.SetTexture ( kernel, "Input", ghelp.GetMiniMapScript ( ).renderTexture_Fow );
    7.             this.MapComputationsShader.SetBuffer ( kernel, "Output", result );
    8.             this.MapComputationsShader.Dispatch ( kernel, tex.width / 8, tex.height / 8, 1 );
    9.  
    10.  
    11.             int dataLength = tex.width * tex.height;
    12.             int dataLenghtDivider = 512;
    13.             int oneReadSize = dataLength / dataLenghtDivider;
    14.  
    15.             auroraSurfaceFowMap.MapPixelsData = new List<MapDataStruct> ( );
    16.             for (int currentLine = 0; currentLine < dataLength / oneReadSize; currentLine++ )
    17.             {
    18.                 AsyncGPUReadback.Request(result, oneReadSize * dataStride, oneReadSize * currentLine * dataStride, new Action<AsyncGPUReadbackRequest> ( ( s ) =>
    19.                 {
    20.                     auroraSurfaceFowMap.MapPixelsData.AddRange ( s.GetData<MapDataStruct> ( ).Where ( x => x.a < 1f ) );
    21.                 } ) );
    22.                 yield return null;
    23.             }
    24.             yield return new WaitForSeconds ( 1 );
    25.             result.Release ( );
    26.  
    27.             #endregion
     
    FM-Productions and radiantboy like this.