'Is the render target view the only way to output data from pixel shader in DirectX?

Purpose: I want to render an image in the screen and save it in my disk.

Description: I have a render target view. I have a input shader resource view with its texture (D3D11_USAGE_DYNAMIC). I have a output shader resource view with its texture (D3D11_USAGE_DEFAULT). I have a auxiliar simple texture (D3D11_USAGE_STAGING).

The execution path is the following:

  • Read input image in a texture.
  • Bind the input texture view and output texture view, pixel shader, sampler and vertex shader.
  • Run draw command.
  • Copy output texture to auxiliar texture.
  • Save auxiliar texture in a image. The image is empty.

Question: How can I output an additional texture and still rendering on screen?

Example code

    mWidth = width;
    mHeight = height;

    // Create image texture to hold input image for unormalized values and CPU write/GPU read access
    D3D11_TEXTURE2D_DESC inputImageDesc;
    ZeroMemory(&inputImageDesc, sizeof(D3D11_TEXTURE2D_DESC));
    inputImageDesc.ArraySize = 1;
    inputImageDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    inputImageDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // Needed for cpu write and gpu read
    inputImageDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    inputImageDesc.Width = width;
    inputImageDesc.Height = height;
    inputImageDesc.MipLevels = 1;
    inputImageDesc.SampleDesc.Count = 1;
    inputImageDesc.SampleDesc.Quality = 0;
    inputImageDesc.Usage = D3D11_USAGE_DYNAMIC; // Needed for cpu write and gpu read

    result = engine.device()->CreateTexture2D(&inputImageDesc, nullptr, mInputTexture.GetAddressOf());
    if(result < 0)
    {
        return -1;
    }

    result = engine.device()->CreateShaderResourceView(mInputTexture.Get(), nullptr, mInputView.GetAddressOf());
    if(result < 0)
    {
        return -1;
    }

    // Create image texture for unormalized values and only GPU access
    D3D11_TEXTURE2D_DESC gpuImageDesc;
    ZeroMemory(&gpuImageDesc, sizeof(D3D11_TEXTURE2D_DESC));
    gpuImageDesc.ArraySize = 1;
    gpuImageDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
    gpuImageDesc.CPUAccessFlags = 0; // Needed for gpu read/write (cpu no access)
    gpuImageDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    gpuImageDesc.Width = width;
    gpuImageDesc.Height = height;
    gpuImageDesc.MipLevels = 1;
    gpuImageDesc.SampleDesc.Count = 1;
    gpuImageDesc.SampleDesc.Quality = 0;
    gpuImageDesc.Usage = D3D11_USAGE_DEFAULT; // Needed for gpu read/write (cpu no access)

    result = engine.device()->CreateTexture2D(&gpuImageDesc, nullptr, mOutputGpuTexture.GetAddressOf());
    if(result < 0)
    {
        return -1;
    }

    result = engine.device()->CreateShaderResourceView(mOutputGpuTexture.Get(), nullptr, mOutputView.GetAddressOf());
    if(result < 0)
    {
        return -1;
    }

    // Create image texture for unormalized values and only CPU read access
    D3D11_TEXTURE2D_DESC cpuImageDesc;
    ZeroMemory(&cpuImageDesc, sizeof(D3D11_TEXTURE2D_DESC));
    mOutputGpuTexture->GetDesc(&cpuImageDesc);
    cpuImageDesc.BindFlags       = 0;
    cpuImageDesc.MiscFlags       = 0;
    cpuImageDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; // Needed for cpu read
    cpuImageDesc.Usage = D3D11_USAGE_STAGING; // Needed for cpu read

    result = engine.device()->CreateTexture2D(&cpuImageDesc, nullptr, mOutputCpuTexture.GetAddressOf());
    if(result < 0)
    {
        return -1;
    }
struct PixelInput
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
    float2 coord : TEXCOORDIN;
    float2 coordOut : TEXCOORDOUT;
};

Texture2D<float4> gInputTexture : register(t0);
SamplerState gSampleType : register(s0);
RWTexture2D<float4> gOutputTexture : register(t1);

float4 main(PixelInput input) : SV_TARGET
{
    gOutputTexture[input.coordOut] = float4(1.0,0.0,0.0,1.0);

    float4 inputPixel = float4(0.0, 0.0, 0.0, 1.0);
    inputPixel.rgb = gInputTexture.Sample(gSampleType, input.coord).rgb;
    return inputPixel;
}

    engine.context()->CopyResource(mOutputCpuTexture.Get(), mOutputGpuTexture.Get());
    D3D11_MAPPED_SUBRESOURCE mappedImgData;
    ZeroMemory(&mappedImgData, sizeof(D3D11_MAPPED_SUBRESOURCE));
    int32_t result = engine.context()->Map(mOutputCpuTexture.Get(), 0, D3D11_MAP_READ, 0, &mappedImgData);
    if(result < EC_SUCCESS)
    {
        ERROR_MSG(result);
        return EC_COULD_NOT_MAP_RESOURCE_TEXTURE;
    }

    // Copy the less bytes possible, avoiding out of bounds.
    const uint32_t bytesPerRow = std::min(rowPitch, mappedImgData.RowPitch);

    uint8_t* textureData = reinterpret_cast<uint8_t*>(mappedImgData.pData);
    for(uint32_t i = 0; i < height; ++i)
    {
        memcpy(dst, textureData, bytesPerRow);
        textureData += mappedImgData.RowPitch;
        dst += rowPitch;
    }
    engine.context()->Unmap(mOutputCpuTexture.Get(), 0);


Solution 1:[1]

What I did to fix this was add a compute shader as intermediary, in that way I read a RWTexture (DEFAULT) with another texture (STAGING) and also another one to read the back buffer.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Esmeralda Quintero