0
\$\begingroup\$

I'm trying to read pixel data of a D3D12 render target (ID3D12Resource) for h264 encoding/streaming, I tried asking on DirectX Discord and also GPT and still I got no chance and a working solution.

I am making a unity3D plugin and I have access to following components from unity :

static ID3D12Device*                    s_D3D12Device;
static ID3D12CommandQueue*              s_CommandQueue;
static ID3D12Fence*                     s_FrameFence;

I also have a pointer to ID3D12Resource which I got using Texture.GetNativeTexturePtr and I call the native OnRenderFinished from unity on HDRP RenderPipelineManager.endCameraRendering

My result data is :

struct PIXEL 
{
    UINT8 r, g, b, a;
    PIXEL(UINT8 red, UINT8 green, UINT8 blue, UINT8 alpha) : r(red), g(green), b(blue), a(alpha) {}
};

static PIXEL*   RenderPixelData = nullptr;

I know how to do this in D3D11 but I can't find any correct way to perform this in D3D12, I will be really thankful if someone help me with this.

void OnRenderFinished()
{
    if (!s_RenderTexture) { u2merr("Error At Line %d", __LINE__); return; }
    if (!RenderPixelData) { u2merr("Error At Line %d", __LINE__); return; }

    HRESULT res = 0;

    size_t pixelDataSize = RenderTextureDesc.Width * RenderTextureDesc.Height * sizeof(PIXEL);
    UINT width = RenderTextureDesc.Width; UINT height = RenderTextureDesc.Height;

    // How to Read Data from s_RenderTexture

    printf("> ");
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

The steps you want are roughly:

  1. Create a D3D12_RESOURCE_STATE_COPY_DEST resource on a D3D12_HEAP_TYPE_READBACK heap.
  2. Transition the render target to D3D12_RESOURCE_STATE_COPY_SOURCE.
  3. Use CopyTextureRegion() to copy to your new resource.
  4. Use a fence to wait for the copy to complete.
  5. Read from the D3D12_HEAP_TYPE_READBACK texture using Map().

You might find the ScreenGrab code under https://github.com/microsoft/DirectXTex useful, as it's essentially doing what you want to do.

\$\endgroup\$
4
  • \$\begingroup\$ Their method is not fast, It takes ~10ms for a 2K render in D3D11 it only takes ~2ms \$\endgroup\$
    – Cyclone
    Jun 5, 2023 at 5:18
  • \$\begingroup\$ For better performance, I'd suggest starting by double buffering the readback textures. That way you can avoid waiting for the GPU by adding a frame of latency. \$\endgroup\$
    – Adam
    Jun 5, 2023 at 22:17
  • \$\begingroup\$ can you give me some links to start on this? also I accepted your solution as it may useful for other cases. \$\endgroup\$
    – Cyclone
    Jun 8, 2023 at 8:31
  • 1
    \$\begingroup\$ It should be as simple as creating two D3D12_RESOURCE_STATE_COPY_DEST textures, two fences, and alternating between them each frame. That way, while you're waiting for the GPU to finish rendering and copying the result to one buffer, you can read back the result from buffer that it filled in on the previous frame. \$\endgroup\$
    – Adam
    Jun 8, 2023 at 21:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .