Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

RenderTexture to DX11 Buffer and vice versa

Discussion in 'Image Effects' started by habolog, Nov 21, 2017.

  1. habolog

    habolog

    Joined:
    May 17, 2017
    Posts:
    8
    Hi, community!
    I'm quite new in Unity and absolutely new in shaders, DirectX and so on. But I have to research and implement some not usual feature.

    What is the task?
    - There is some magic image-processing C-library which deals with DX11 Texture2D.
    - I create such Texture2D in my native plugin
    - on every frame in OnRenderImage I copy source RenderTexture to that DX11 Texture2D
    - magic library process that texture and I copy the result to destination RenderTexture

    That scheme works great! BUT! That magic processing library is upgraded to deals with DX11 Buffer instead of Texture2D! And now I'm confusing how to pass data from source RenderTexture into DX11 Buffer.

    What I've tried?
    - Just change creation of ShaderResourceView from Texture2D to Buffer and use that View in Unity. Failed...
    - I used ComputeShader to copy Texture2D to Buffer. Failed...

    I searched and read dozen forums threads about ComputeShaders, Texture2Ds and I'm still not confident that I understand how all this stuff works and how to sort out this problem.

    Huge respect for those who will guide me with this issue!
     
  2. habolog

    habolog

    Joined:
    May 17, 2017
    Posts:
    8
    Adding code of both approaches.

    1) Create intermediate Unity's texture from DX11 Buffer

    Native plugin's code of creating DX11 ShaderResourceView
    Code (CSharp):
    1. long RenderAPI_D3D11::CreateUnityBuffer(int width, int height)
    2. {
    3.     D3D11_BUFFER_DESC descBuffer;
    4.     ZeroMemory(&descBuffer, sizeof(descBuffer));
    5.     descBuffer.Usage = D3D11_USAGE_DEFAULT;
    6.     descBuffer.ByteWidth = width * height * 4;
    7.     descBuffer.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    8.     descBuffer.CPUAccessFlags = 0;
    9.     descBuffer.StructureByteStride = 4;
    10.     descBuffer.MiscFlags = 0;
    11.  
    12.     HRESULT res = m_Device->CreateBuffer(&descBuffer, NULL, &pUnityBufferLeftEye);
    13.     if (res != S_OK)
    14.         return 1;
    15.    
    16.     D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
    17.     ZeroMemory(&srvDesc, sizeof(srvDesc));
    18.     srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    19.     srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
    20.     srvDesc.Buffer.FirstElement = 0;
    21.     srvDesc.Buffer.NumElements = width * height;
    22.     srvDesc.Buffer.ElementWidth = 4;
    23.     srvDesc.Buffer.ElementOffset = 0;
    24.  
    25.     res = m_Device->CreateShaderResourceView(pUnityBufferLeftEye, &srvDesc, &pUnityShaderResourceViewBufferLeftEye);
    26.     if (res != S_OK)
    27.         return res;
    28.     return res;
    29. }
    Using that SRV in Unity's script to create Texture2D and using that texture:
    Code (CSharp):
    1.  
    2. private IntPtr d3d11tex;
    3. private Texture2D sTex = null;
    4. void OnRenderImage(RenderTexture source, RenderTexture destination)
    5. {
    6.    if(isFirstRun)
    7.    {
    8.       d3d11tex = GetUnityShaderResourceViewBuffer(); //Plugin's API to get pointer to SRV
    9.       sTex = Texture2D.CreateExternalTexture(source.width, source.height, TextureFormat.RGBA32, false, false, d3d11tex);                  
    10.       isFirstRun = false;
    11.    }
    12.  
    13.    Graphics.CopyTexture(source, sTex);
    14.    GL.IssuePluginEvent(GetRenderEventFunc(), 0); //Processing sTex in native code
    15.    Graphics.CopyTexture(sTex, destination);
    16. }
    17.  
    That code crashed Unity Editor!
    If I use ID3D11Texture2D instead of ID3D11Buffer in the same maner, all works great!
    Code (CSharp):
    1. long RenderAPI_D3D11::CreateUnityTexure(int width, int height)
    2. {  
    3.     //2D Texture for Unity
    4.     D3D11_TEXTURE2D_DESC xDesc = { 0 };
    5.     xDesc.Width = width;
    6.     xDesc.Height = height;
    7.     xDesc.MipLevels = xDesc.ArraySize = 1;
    8.     xDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    9.     xDesc.SampleDesc.Count = 1;
    10.     xDesc.Usage = D3D11_USAGE_DEFAULT;
    11.     xDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    12.     xDesc.CPUAccessFlags = 0;
    13.     xDesc.MiscFlags = 0;
    14.    
    15.     HRESULT res = m_Device->CreateTexture2D(&xDesc, NULL, &pUnityTextureLeftEye);    
    16.  
    17.     D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
    18.     ZeroMemory(&srvDesc, sizeof(srvDesc));
    19.  
    20.     srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    21.     srvDesc.Texture2D.MostDetailedMip = 0;
    22.     srvDesc.Texture2D.MipLevels = 1;
    23.     srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    24.  
    25.     res = m_Device->CreateShaderResourceView(pUnityTextureLeftEye, nullptr, &pUnityShaderResourceViewLeftEye);
    26.     return res;
    27. }
    Question for this approach: how to replace using of ID3D11Texture2D to ID3D11Buffer?

    2) Second approach is to use Compute Shader to copy input source RenderTexture to DX11 Buffer, process that buffer and copy back data to destination RenderTexture.

    Unity's code:
    Code (CSharp):
    1. [SerializeField] public ComputeShader dx11CopyShader;
    2. private ComputeBuffer d3d11buffer;
    3. private static String CopyTBKernel = "CopyTextureToBuffer";
    4. private int shaderCopyTextureToBufferKernel;
    5. private static String CopyBTKernel = "CopyBufferToTexture";
    6. private int shaderCopyBufferToTextureKernel;
    7.  
    8. void Start()
    9. {
    10.    //dx11CopyShader = Resources.Load<ComputeShader>("DX11BufferCopyShader"); //Load in Inspector
    11.    shaderCopyTextureToBufferKernel = dx11CopyShader.FindKernel(CopyTBKernel);
    12.    shaderCopyBufferToTextureKernel = dx11CopyShader.FindKernel(CopyBTKernel);
    13. }
    14.  
    15. void OnRenderImage(RenderTexture source, RenderTexture destination)
    16. {
    17.    if(isFirstRun)
    18.    {
    19.       d3d11buffer = new ComputeBuffer(source.width * source.height, 4);
    20.       isFirstRun = false;
    21.    }
    22.    
    23.    dx11CopyShader.SetTexture(shaderCopyTextureToBufferKernel, "_InputTexture", source);
    24.    dx11CopyShader.SetBuffer(shaderCopyTextureToBufferKernel, "_Buffer", d3d11buffer);
    25.    dx11CopyShader.Dispatch(shaderCopyTextureToBufferKernel, source.width / 8, source.height / 8, 1);
    26.  
    27.    dx11CopyShader.SetTexture(shaderCopyBufferToTextureKernel, "_OutputTexture", sTex);
    28.    dx11CopyShader.SetBuffer(shaderCopyBufferToTextureKernel, "_Buffer", d3d11buffer);
    29.    dx11CopyShader.Dispatch(shaderCopyBufferToTextureKernel, source.width / 8, source.height / 8, 1);
    30. }


    Compute shader's code:
    Code (CSharp):
    1. #pragma kernel CopyTextureToBuffer
    2. #pragma kernel CopyBufferToTexture
    3.  
    4. Texture2D<float4> _InputTexture;
    5. RWBuffer<float4> _Buffer;
    6. RWTexture2D<float4> _OutputTexture;
    7.  
    8. [numthreads(32,32,1)]
    9. void CopyTextureToBuffer(uint2 id : SV_DispatchThreadID)
    10. {
    11.     int pos = id.y * 32 + id.x;
    12.     _Buffer[pos] = _InputTexture[id];
    13. }
    14.  
    15. [numthreads(32, 32, 1)]
    16. void CopyBufferToTexture(uint2 id : SV_DispatchThreadID)
    17. {
    18.     int pos = id.y * 32 + id.x;
    19.     _OutputTexture[id] = _Buffer[pos];
    20. }
    The result of that approach is just a black screen (((( I have to note that I'm not familiar with shaders a GPU computation at all! So there is might be many fundamental mistakes.