Search code examples
directxdirectx-12

How to upsample a depth texture under directX 12?


I wrote a graphics application. In my rendering pipeline I need to upsample my rasterization depth buffer to a higher resolution. Here how the upsampled texture is created:

nbBool DX12Renderer::createDepthStencilBuffers(const Math::Uvec2& upsampledBufferSize)
{
    const DXGI_FORMAT format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;//DXGI_FORMAT_D32_FLOAT

    D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
    depthOptimizedClearValue.Format = format;
    depthOptimizedClearValue.DepthStencil.Depth = 1.0f;
    depthOptimizedClearValue.DepthStencil.Stencil = 0;

    HRESULT hr = D3D12Device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Tex2D(format, upsampledBufferSize.x, upsampledBufferSize.y, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL),
        D3D12_RESOURCE_STATE_DEPTH_WRITE,
        &depthOptimizedClearValue,
        IID_PPV_ARGS(&m_depthStencilBuffer)
    );

    Dx12TextureHandle dst;
    dst.buffer = m_depthStencilBuffer;
    dst.format = DXGI_FORMAT_R32_FLOAT;
    createUAV(D3D12_UAV_DIMENSION_TEXTURE2D, dst);// CreateUnorderedAccessView happens here.
}

With the previous code i get two DX12 errors:

  1. D3D12 ERROR: ID3D12Device::CreateUnorderedAccessView: For the resource format D32_FLOAT_S8X24_UINT, when making a D3D view, the format name for the view can't be R32_FLOAT. See documentation for the set of valid view format names for this resource format, determining which how the resource (or part of it) will appear to shader. [ STATE_CREATION ERROR #342: CREATEUNORDEREDACCESSVIEW_INVALIDFORMAT]

  2. D3D12 ERROR: ID3D12Device::CreateUnorderedAccessView: A UnorderedAccessView cannot be created of a Resource that did not specify the D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS Flag. [ STATE_CREATION ERROR #340: CREATEUNORDEREDACCESSVIEW_INVALIDRESOURCE]

A) Fixing error 1
Basically there is a mismatch between the texture format and the view format. I do not necessary need the upsampling format to be DXGI_FORMAT_D32_FLOAT_S8X24_UINT. Using DXGI_FORMAT_D32_FLOAT depth format with a DXGI_FORMAT_R32_FLOAT view is fixing the error.

B) Fixing error 2
I then use the DXGI_FORMAT_D32_FLOAT format. Basically the D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS is not set when creating the texture. So the CreateUnorderedAccessView call fails. Si i tried to set the flag:

HRESULT hr = D3D12Device->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Tex2D(format, upsampledBufferSize.x, upsampledBufferSize.y, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS),
    D3D12_RESOURCE_STATE_DEPTH_WRITE,
    &depthOptimizedClearValue,
    IID_PPV_ARGS(&m_depthStencilBuffer)
);

I then get a new error when calling CreateUnorderedAccessView:
D3D12 ERROR: ID3D12Device::CreateCommittedResource: D3D12_RESOURCE_DESC::Flags cannot have D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set with D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, nor D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS. [ STATE_CREATION ERROR #599: CREATERESOURCE_INVALIDMISCFLAGS]

So how to do it? An agnostic solution would be best since i will later need to have OpenGL support. But a DX12 specific solution would be a good start :)


Solution

  • For the first error, using DXGI_FORMAT_R32_FLOAT is probably a requirement because DXGI_FORMAT_R32_FLOAT_X8X24_UINT UAVs are not required to be supported by driver; and probably are not cast compatible with DXGI_FORMAT_R32_FLOAT(have to be tested with CopyTextureRegion though). For the second error.. you'd have to use intermediate resource for upsampling and then copy it into depth buffer, because depth buffers are mutually exclusive with UAVs.

    So modifying your code to something like this:

    const DXGI_FORMAT dsvFormat = DXGI_FORMAT_D32_FLOAT;
    const DXGI_FORMAT uavFormat = DXGI_FORMAT_R32_FLOAT;
    
    D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
    depthOptimizedClearValue.Format = format;
    depthOptimizedClearValue.DepthStencil.Depth = 1.0f;
    depthOptimizedClearValue.DepthStencil.Stencil = 0;
    
    HRESULT hr = D3D12Device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Tex2D(dsvFormat, upsampledBufferSize.x, 
            upsampledBufferSize.y, 1, 1, 1, 0, 
            D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL),
        D3D12_RESOURCE_STATE_DEPTH_WRITE,
        &depthOptimizedClearValue,
        IID_PPV_ARGS(&m_depthStencilBuffer)
    );
    
    hr = D3D12Device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Tex2D(uavFormat, upsampledBufferSize.x, 
            upsampledBufferSize.y, 1, 1, 1, 0, 
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS),
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
        nullptr,
        IID_PPV_ARGS(&m_intermediateDepthStencilBuffer)
    );
    
    Dx12TextureHandle dst;
    dst.buffer = m_intermediateDepthStencilBuffer;
    dst.format = DXGI_FORMAT_R32_FLOAT;
    createUAV(D3D12_UAV_DIMENSION_TEXTURE2D, dst);//CreateUnorderedAccessView happens here.
    
    ...
    
    // In your command list after upsampling pass, copying upsampled depth from intermediate UAV resource to depth buffer
    const auto intermediatePreCopy = CD3DX12_RESOURCE_BARRIER::Transition(
        m_intermediateDepthStencilBuffer.Get(),
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    command_list->ResourceBarrier(1, &intermediatePreCopy);
    
    const auto depthPreCopy = CD3DX12_RESOURCE_BARRIER::Transition(
        m_depthStencilBuffer.Get(),
        D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_DEST);
    command_list->ResourceBarrier(1, &depthPreCopy);
    
    command_list->CopyResource(m_depthStencilBuffer.Get(), intermediatePreCopy.Get());
    
    const auto intermediatePostCopy = CD3DX12_RESOURCE_BARRIER::Transition(
        m_intermediateDepthStencilBuffer.Get(),
        D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    command_list->ResourceBarrier(1, &intermediatePostCopy);
    
    const auto depthPostCopy = CD3DX12_RESOURCE_BARRIER::Transition(
        m_depthStencilBuffer.Get(),
        D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_DEPTH_WRITE);
    command_list->ResourceBarrier(1, &depthPostCopy);
    
    // command_list->DiscardResource(m_intermediateDepthStencilBuffer.Get(), nullptr);
    
    

    Im not sure but you might also save some performance/avoid unnecessary VRAM writes using ID3D12GraphicsCommandList::DiscardResource for your UAV resource after copying, but there is no guarantees

    How to upsample a depth texture under directX 12?
    So how to do it? An agnostic solution would be best since i will later need to have OpenGL support. But a DX12 specific solution would be a good start :)
    

    Overall usually you'd just take a texture that needs to be resampled/resized, bind it as shader resource view, and render it to RTV texture of new size, using fullscreen rectangle... i think its pretty common and agnostic solution, but i'd think twice if you really need to have resized depth buffer, it shouldn't be way too expensive but depending on what you're doing overheads builds-up pretty quickly