From 347d08463b4ae82616139531f036bd3b41114fa8 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 12 Apr 2026 11:49:12 +0800 Subject: [PATCH] Fix D3D12 descriptor set staging for shader tables --- engine/src/RHI/D3D12/D3D12CommandList.cpp | 8 ++- engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp | 7 +++ engine/src/RHI/D3D12/D3D12Device.cpp | 9 +++ .../RHI/D3D12/unit/test_backend_specific.cpp | 59 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/engine/src/RHI/D3D12/D3D12CommandList.cpp b/engine/src/RHI/D3D12/D3D12CommandList.cpp index b5aab708..b51119b2 100644 --- a/engine/src/RHI/D3D12/D3D12CommandList.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandList.cpp @@ -348,10 +348,12 @@ void D3D12CommandList::SetGraphicsDescriptorSets( } D3D12DescriptorHeap* heap = d3d12Set->GetHeap(); - if (heap == nullptr || !heap->IsShaderVisible()) { + if (heap == nullptr) { continue; } + // Descriptor sets are staged in CPU-visible heaps and copied into this + // command list's shader-visible heaps immediately before binding. if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { const bool hasSrvTable = d3d12Layout->UsesSetLayouts() ? d3d12Layout->HasShaderResourceTable(setIndex) @@ -581,10 +583,12 @@ void D3D12CommandList::SetComputeDescriptorSets( } D3D12DescriptorHeap* heap = d3d12Set->GetHeap(); - if (heap == nullptr || !heap->IsShaderVisible()) { + if (heap == nullptr) { continue; } + // Descriptor sets are staged in CPU-visible heaps and copied into this + // command list's shader-visible heaps immediately before binding. if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { const bool hasSrvTable = d3d12Layout->UsesSetLayouts() ? d3d12Layout->HasShaderResourceTable(setIndex) diff --git a/engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp b/engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp index a44156e5..408ce0b0 100644 --- a/engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp +++ b/engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp @@ -77,6 +77,9 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::GetCPUDescriptorHandleForHeapSt } D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::GetGPUDescriptorHandleForHeapStart() const { + if (!m_shaderVisible || m_descriptorHeap == nullptr) { + return D3D12_GPU_DESCRIPTOR_HANDLE{0}; + } return m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(); } @@ -89,6 +92,10 @@ CPUDescriptorHandle D3D12DescriptorHeap::GetCPUDescriptorHandle(uint32_t index) } GPUDescriptorHandle D3D12DescriptorHeap::GetGPUDescriptorHandle(uint32_t index) { + if (!m_shaderVisible || m_descriptorHeap == nullptr) { + return GPUDescriptorHandle{0}; + } + D3D12_GPU_DESCRIPTOR_HANDLE handle = m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(); handle.ptr += index * m_descriptorSize; GPUDescriptorHandle result; diff --git a/engine/src/RHI/D3D12/D3D12Device.cpp b/engine/src/RHI/D3D12/D3D12Device.cpp index 760845d8..9227932a 100644 --- a/engine/src/RHI/D3D12/D3D12Device.cpp +++ b/engine/src/RHI/D3D12/D3D12Device.cpp @@ -51,6 +51,10 @@ bool HasShaderPayload(const ShaderCompileDesc& desc) { return !desc.source.empty() || !desc.fileName.empty() || !desc.compiledBinary.empty(); } +bool UsesTransientShaderVisibleDescriptorHeap(DescriptorHeapType type) { + return type == DescriptorHeapType::CBV_SRV_UAV || type == DescriptorHeapType::Sampler; +} + bool ShouldTraceVolumetricShaderCompile(const ShaderCompileDesc& desc) { const std::string fileName = NarrowAscii(desc.fileName); if (fileName.find("volumetric") != std::string::npos) { @@ -1279,6 +1283,11 @@ RHIDescriptorPool* D3D12Device::CreateDescriptorPool(const DescriptorPoolDesc& d auto* pool = new D3D12DescriptorHeap(); DescriptorPoolDesc poolDesc = desc; poolDesc.device = m_device.Get(); + if (UsesTransientShaderVisibleDescriptorHeap(poolDesc.type)) { + // Descriptor sets are staged in CPU-visible heaps and copied into the + // command list's transient shader-visible heaps before each bind. + poolDesc.shaderVisible = false; + } if (pool->Initialize(poolDesc)) { return pool; } diff --git a/tests/RHI/D3D12/unit/test_backend_specific.cpp b/tests/RHI/D3D12/unit/test_backend_specific.cpp index e500dddb..bb2347c3 100644 --- a/tests/RHI/D3D12/unit/test_backend_specific.cpp +++ b/tests/RHI/D3D12/unit/test_backend_specific.cpp @@ -1,5 +1,6 @@ #include "fixtures/D3D12TestFixture.h" +#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h" #include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h" #include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h" #include "XCEngine/RHI/RHIDescriptorPool.h" @@ -54,6 +55,64 @@ TEST_F(D3D12TestFixture, DescriptorSet_MixedBindings_AssignDescriptorIndicesByTy delete pool; } +TEST_F(D3D12TestFixture, DescriptorSet_TableBindingsUseCpuVisibleStagingHeaps) { + DescriptorPoolDesc texturePoolDesc = {}; + texturePoolDesc.type = DescriptorHeapType::CBV_SRV_UAV; + texturePoolDesc.descriptorCount = 2; + texturePoolDesc.shaderVisible = true; + + RHIDescriptorPool* texturePool = GetDevice()->CreateDescriptorPool(texturePoolDesc); + ASSERT_NE(texturePool, nullptr); + + DescriptorSetLayoutBinding textureBinding = {}; + textureBinding.binding = 0; + textureBinding.type = static_cast(DescriptorType::SRV); + textureBinding.count = 1; + + DescriptorSetLayoutDesc textureLayoutDesc = {}; + textureLayoutDesc.bindings = &textureBinding; + textureLayoutDesc.bindingCount = 1; + + RHIDescriptorSet* textureSet = texturePool->AllocateSet(textureLayoutDesc); + ASSERT_NE(textureSet, nullptr); + auto* textureD3D12Set = static_cast(textureSet); + ASSERT_NE(textureD3D12Set->GetHeap(), nullptr); + EXPECT_FALSE(textureD3D12Set->GetHeap()->IsShaderVisible()); + + DescriptorPoolDesc samplerPoolDesc = {}; + samplerPoolDesc.type = DescriptorHeapType::Sampler; + samplerPoolDesc.descriptorCount = 1; + samplerPoolDesc.shaderVisible = true; + + RHIDescriptorPool* samplerPool = GetDevice()->CreateDescriptorPool(samplerPoolDesc); + ASSERT_NE(samplerPool, nullptr); + + DescriptorSetLayoutBinding samplerBinding = {}; + samplerBinding.binding = 0; + samplerBinding.type = static_cast(DescriptorType::Sampler); + samplerBinding.count = 1; + + DescriptorSetLayoutDesc samplerLayoutDesc = {}; + samplerLayoutDesc.bindings = &samplerBinding; + samplerLayoutDesc.bindingCount = 1; + + RHIDescriptorSet* samplerSet = samplerPool->AllocateSet(samplerLayoutDesc); + ASSERT_NE(samplerSet, nullptr); + auto* samplerD3D12Set = static_cast(samplerSet); + ASSERT_NE(samplerD3D12Set->GetHeap(), nullptr); + EXPECT_FALSE(samplerD3D12Set->GetHeap()->IsShaderVisible()); + + textureSet->Shutdown(); + delete textureSet; + texturePool->Shutdown(); + delete texturePool; + + samplerSet->Shutdown(); + delete samplerSet; + samplerPool->Shutdown(); + delete samplerPool; +} + TEST_F(D3D12TestFixture, DescriptorSet_MultipleConstantBuffersUploadIndependently) { DescriptorPoolDesc poolDesc = {}; poolDesc.type = DescriptorHeapType::CBV_SRV_UAV;