在 directx12 中将深度缓冲区值读回 CPU

如何解决在 directx12 中将深度缓冲区值读回 CPU

我想将深度缓冲区的内容复制回 cpu 以便能够读取它们。

我通过以下方式创建深度缓冲区(视图):

// Create the depth stencil view.
{
    D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
    depthStencilDesc.Format = dxgi_FORMAT_D32_FLOAT;
    depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
    depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;

    D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
    depthOptimizedClearValue.Format = dxgi_FORMAT_D32_FLOAT;
    depthOptimizedClearValue.DepthStencil.Depth = 0.0f;
    depthOptimizedClearValue.DepthStencil.Stencil = 0;

    ThrowIfFailed(m_device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE,&CD3DX12_RESOURCE_DESC::Tex2D(dxgi_FORMAT_D32_FLOAT,m_width,m_height,1,D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL),D3D12_RESOURCE_STATE_DEPTH_WRITE,&depthOptimizedClearValue,IID_PPV_ARGS(&m_depthStencil)
    ));

    NAME_D3D12_OBJECT(m_depthStencil);

    m_device->CreateDepthStencilView(m_depthStencil.Get(),&depthStencilDesc,m_dsvHeap->GetcpuDescriptorHandleForHeapStart());
}

对于管道,我对深度缓冲区使用以下描述:

D3D12_DEPTH_STENCIL_DESC depthDesc;
ZeroMemory(&depthDesc,sizeof(D3D12_DEPTH_STENCIL_DESC));
depthDesc.DepthEnable = TRUE;
depthDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
depthDesc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
depthDesc.StencilEnable = FALSE;

到目前为止一切正常,使用 NSight 我可以看到在渲染通道完成后,正确的值存储在深度缓冲区中。

现在我想将值读回 cpu,在那里遇到问题,如果我使用: D3D12_HEAP_PROPERTIES readbackHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK) };

堆类型回读我无法分配与深度缓冲区具有相同描述的缓冲区。但是,如果我使用 CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),我以后将无法“映射”内存(根据 microsoft 文档 https://docs.microsoft.com/en-us/windows/win32/direct3d12/readback-data-using-heaps

其余的复制我会与记录的例子类似:

D3D12_RESOURCE_DESC desc;
ZeroMemory(&desc,sizeof(desc));
desc = m_depthStencil->GetDesc();

//D3D12_HEAP_PROPERTIES readbackHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK) };
//D3D12_RESOURCE_DESC readbackBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(500 * 500) };

D3D12_RESOURCE_DESC readbackBufferDesc = m_depthStencil->GetDesc();

ComPtr<ID3D12Resource> readbackBuffer;
ThrowIfFailed(m_device->CreateCommittedResource(
//&readbackHeapProperties,&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),&readbackBufferDesc,D3D12_RESOURCE_STATE_copY_DEST,nullptr,__uuidof(readbackBuffer),&readbackBuffer));

使深度缓冲区进入正确的状态。

{
    D3D12_RESOURCE_BARRIER outputBufferResourceBarrier
    {
        CD3DX12_RESOURCE_BARRIER::Transition(
            m_depthStencil.Get(),D3D12_RESOURCE_STATE_copY_SOURCE)
    };
    m_commandList->ResourceBarrier(1,&outputBufferResourceBarrier);
}

m_commandList->copyResource(readbackBuffer.Get(),m_depthStencil.Get());

ThrowIfFailed(m_commandList->Close());

ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists),ppCommandLists);



D3D12_RANGE readbackBufferRange{ 0,500 * 500 }; // outputbuffer size = 500x500 = window size?
FLOAT* pReadbackBufferData{};
ThrowIfFailed(
    readbackBuffer->Map
    (
        0,&readbackBufferRange,reinterpret_cast<void**>(&pReadbackBufferData)
    )
);

D3D12_RANGE emptyRange{ 0,0 };
readbackBuffer->Unmap
(
    0,&emptyRange
);

将深度缓冲区转换为原始状态。

{
    D3D12_RESOURCE_BARRIER outputBufferResourceBarrier
    {
        CD3DX12_RESOURCE_BARRIER::Transition(
            m_depthStencil.Get(),D3D12_RESOURCE_STATE_copY_SOURCE,D3D12_RESOURCE_STATE_DEPTH_WRITE)
    };
    m_commandList->ResourceBarrier(1,&outputBufferResourceBarrier);
}

如果有任何帮助,我将不胜感激!

解决方法

在 DirectX 12 中,您不会创建与 GPU 资源格式相同的“回读”缓冲区。相反,您只需创建一个与源字节数相同的一维缓冲区。

“技巧”是您需要与原始资源具有相同的音调。

CD3DX12_HEAP_PROPERTIES readBackHeapProperties(D3D12_HEAP_TYPE_READBACK);

// Readback resources must be buffers
D3D12_RESOURCE_DESC bufferDesc = {};
bufferDesc.DepthOrArraySize = 1;
bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
bufferDesc.Height = 1;
bufferDesc.Width = srcPitch * m_height; // <<----
bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferDesc.MipLevels = 1;
bufferDesc.SampleDesc.Count = 1;

srcPitch 取自原始资源:

auto depthBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT,m_width,m_height,1,D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);

UINT64 totalResourceSize = 0;
UINT64 fpRowPitch = 0;
UINT fpRowCount = 0;
m_device->GetCopyableFootprints(
        &depthBufferDesc,nullptr,&fpRowCount,&fpRowPitch,&totalResourceSize);

// Round up the srcPitch to multiples of 256
UINT64 srcPitch = (fpRowPitch + 255) & ~0xFFu;

请参阅 DX12 的 DirectX 工具包中的 ScreenGrab

我还对该 Microsoft Docs 页面进行了编辑。它使用了一个缓冲区读回缓冲区的例子,但没有提到回读资源总是一个缓冲区的重要细节。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?