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. Dismiss Notice

Question Compute Shader

Discussion in 'Shaders' started by ahmedandre, Nov 8, 2020.

  1. ahmedandre

    ahmedandre

    Joined:
    Jul 26, 2014
    Posts:
    20
    I'm having difficulty porting a filter that I wrote in C# and It's pretty slow and I would like to convert it to a compute shader.

    Right now I get compilation error and I also don't know how would compute shader work on that algorithm. I'm not sure If doing the average calculation is correct as it is right now ? The original code:


    Code (CSharp):
    1.   public unsafe  void DRSFilter( byte *  src, byte * dst, uint data_delta, uint history_scan_depth)
    2.    {
    3.        if (history_scan_depth > history_capacity)
    4.        {
    5.            history_scan_depth = history_capacity;
    6.        }
    7.  
    8.        for (int i = 0; i < layer_size; i++)
    9.        {
    10.            byte b = src;
    11.            uint avrg = 0;
    12.            for (int k = 0; k < history_scan_depth; k++)
    13.            {
    14.                avrg += history[k * layer_size + i];
    15.  
    16.            }
    17.            average = (byte)(avrg / history_scan_depth);
    18.  
    19.            if (Math.Abs(b - average) < data_delta)
    20.            {
    21.                dst = b;
    22.            }
    23.  
    24.  
    25.            history[layer * layer_size + i] = b;
    26.        }
    27.  
    28.        layer++;
    29.        if (layer == history_scan_depth)
    30.        {
    31.            layer = 0;
    32.        }
    33.    }

    The new trial of compute shader in unity:

    Shader Initialization:
    Code (CSharp):
    1.  
    2.    private const int width = 1280;
    3.    private const int height = 720;
    4.    private int history_capacity = 3;
    5.    private int layer_size = width * height * 6;
    6.    private int layer = 0;
    7.    public int history_scan_depth = 50;
    8.    public int data_delta = 50;
    9.    private int[] history;
    10.    private int[] average;
    11.    public ComputeShader shader;
    12.    private int handleDepthFilter;
    13.    private Texture depthFiltered;
    14.    history = new int[history_capacity * layer_size];
    15.    average = new int[layer_size];
    16.    shader.SetInt("history_scan_depth", history_scan_depth);
    17.    shader.SetInt("data_delta", data_delta);
    18.    shader.SetInt("history_capacity", history_capacity);
    19.    shader.SetInt("layer_size", layer_size);
    20.    shader.SetInt("layer", layer);
    21.  
    22.   ComputeBuffer history_buffer = new ComputeBuffer(history_capacity * layer_size, sizeof(int));
    23.   ComputeBuffer average_buffer = new ComputeBuffer(layer_size, sizeof(int));
    24.  
    25.   history_buffer.SetData(history);
    26.   average_buffer.SetData(average);
    27.  
    28.   shader.SetBuffer(handleDepthFilter, "history_buffer", history_buffer);
    29.   shader.SetBuffer(handleDepthFilter, "average_buffer", average_buffer);
    30.  
    31. _outputTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32);
    32. _outputTexture.enableRandomWrite = true;
    33. _outputTexture.Create()

    Shader Code:

    Code (CSharp):
    1.  
    2.  
    3. #pragma kernel CSMain
    4.  
    5. // Create a RenderTexture with enableRandomWrite flag and set it
    6. // with cs.SetTexture
    7. RWTexture2D<float4> Result;
    8. Texture2D<float4> Texture;
    9. RWStructuredBuffer<int> history_buffer;
    10. RWStructuredBuffer<int> average_buffer;
    11. int history_scan_depth;
    12. int history_capacity;
    13. int data_delta;
    14. int layer;
    15. int layer_size;
    16.  
    17. [numthreads(8,8,1)]
    18. void CSMain (uint3 id : SV_DispatchThreadID)
    19. {
    20.    // TODO: insert actual code here!
    21.    if (history_scan_depth > history_capacity)
    22.    {
    23.       history_scan_depth = history_capacity;
    24.    }
    25.  
    26.    float4 b = Texture[id.xy];
    27.  
    28.  
    29.    uint avrg = 0;
    30.    for (int k = 0; k < history_scan_depth; k++)
    31.    {
    32.       avrg += history_buffer[k * layer_size + id.xy];
    33.  
    34.    }
    35.    average_buffer[id] = (byte)(avrg / history_scan_depth);
    36.  
    37.    if (abs(b - average[id.xy) < data_delta)
    38.    {
    39.       Result[id] = b;
    40.    }
    41.  
    42.  
    43.    history_buffer[layer * layer_size + .xy] = b;
    44.  
    45.  
    46.    _layer++;
    47.    if (_layer == history_scan_depth)
    48.    {
    49.       _layer = 0;
    50.    }
    51. }

    Shader dispatching:

    Code (CSharp):
    1.  
    2. private void ImageUpdated(ref Texture2D zedTextureDepth)
    3.    {
    4.  
    5.      Texture2D copyDepthRW = duplicateTexture(zedTextureDepth);
    6.      shader.SetTexture(handleDepthFilter, "Texture", copyDepthRW);
    7.      shader.SetTexture(handleDepthFilter, "Result", _outputTexture);
    8.      shader.Dispatch(handleDepthFilter, (width) / 8,
    9.          (height) / 8, 1);}
    10.    }
     
  2. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    How many times is DRSFilter() supposed to be called? Because in your C# version you have a loop, but in your CS version the kernel has both the code from inside and outside the loop, so if your CS version is doing that again and again from zero to layer_size. That's very different behavior.

    Please show how you use that code, what kind of data you pass into it. There are several things the C# code references to that are neither local variables nor parameters, so it's hard to tell you what to do.
     
  3. ahmedandre

    ahmedandre

    Joined:
    Jul 26, 2014
    Posts:
    20
    @KokkuHub
    Thanks for your answer.
    actually DRSFilter is called each frame with source and destination textures.
    I have trimmed the loop for i < layer_size, because it crashed the GPU.
    Layer_size is the resolution of the input texture.
    history_scan_depth is how many depth of history usualy its 50

    so basically I call DRSFilter(src, dst, layer_size, history_scan_depth)

    The problem is I don't know how to really convert that algorithm into compute shader. The texture is large 1280*720 == layer_size.

    * This filter holds the history of source data in a few layers. It counts average value *
    * in history for each element. If next value from source is close enough to the average *
    * then this element can be written in destination array. Otherwise the element from *
    * destination array stays with old value. *
    * *
    * data_delta - determines possible difference between new and average values. *
    * history_scan_depth - determines many layers of history should filter keep. *
    * *