在 directx12 中将深度缓冲区值读回 CPU
Reading depth buffer values back to CPU in directx12
我想将深度缓冲区的内容复制回 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, 0, 1, 0, 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 我可以看到在 renderpass 完成后正确的值存储在深度缓冲区中。
现在我想读回 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),
D3D12_HEAP_FLAG_NONE,
&readbackBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
__uuidof(readbackBuffer),
&readbackBuffer));
使深度缓冲区进入正确状态。
{
D3D12_RESOURCE_BARRIER outputBufferResourceBarrier
{
CD3DX12_RESOURCE_BARRIER::Transition(
m_depthStencil.Get(),
D3D12_RESOURCE_STATE_DEPTH_WRITE,
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 资源格式相同的 'readback' 缓冲区。相反,您只需创建一个与源字节数相同的一维缓冲区。
'trick'就是你需要和原资源有相同的pitch。
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, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
UINT64 totalResourceSize = 0;
UINT64 fpRowPitch = 0;
UINT fpRowCount = 0;
m_device->GetCopyableFootprints(
&depthBufferDesc,
0,
1,
0,
nullptr,
&fpRowCount,
&fpRowPitch,
&totalResourceSize);
// Round up the srcPitch to multiples of 256
UINT64 srcPitch = (fpRowPitch + 255) & ~0xFFu;
请参阅 DX12 的 DirectX 工具包中的 ScreenGrab。
I also filed an edit for that Microsoft Docs page. It uses an example of a buffer reading back to a buffer, but didn't mention the important detail that the read-back resource is always a buffer.
我想将深度缓冲区的内容复制回 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, 0, 1, 0, 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 我可以看到在 renderpass 完成后正确的值存储在深度缓冲区中。
现在我想读回 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),
D3D12_HEAP_FLAG_NONE,
&readbackBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
__uuidof(readbackBuffer),
&readbackBuffer));
使深度缓冲区进入正确状态。
{
D3D12_RESOURCE_BARRIER outputBufferResourceBarrier
{
CD3DX12_RESOURCE_BARRIER::Transition(
m_depthStencil.Get(),
D3D12_RESOURCE_STATE_DEPTH_WRITE,
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 资源格式相同的 'readback' 缓冲区。相反,您只需创建一个与源字节数相同的一维缓冲区。
'trick'就是你需要和原资源有相同的pitch。
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, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
UINT64 totalResourceSize = 0;
UINT64 fpRowPitch = 0;
UINT fpRowCount = 0;
m_device->GetCopyableFootprints(
&depthBufferDesc,
0,
1,
0,
nullptr,
&fpRowCount,
&fpRowPitch,
&totalResourceSize);
// Round up the srcPitch to multiples of 256
UINT64 srcPitch = (fpRowPitch + 255) & ~0xFFu;
请参阅 DX12 的 DirectX 工具包中的 ScreenGrab。
I also filed an edit for that Microsoft Docs page. It uses an example of a buffer reading back to a buffer, but didn't mention the important detail that the read-back resource is always a buffer.