diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h b/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h index bbd2bf46..778e33ea 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h @@ -17,6 +17,13 @@ class D3D12Device; class D3D12PipelineLayout : public RHIPipelineLayout { public: + struct SetRootParameterMapping { + std::unordered_map constantBufferRootIndices; + uint32_t shaderResourceTableRootIndex = UINT32_MAX; + uint32_t unorderedAccessTableRootIndex = UINT32_MAX; + uint32_t samplerTableRootIndex = UINT32_MAX; + }; + D3D12PipelineLayout(); ~D3D12PipelineLayout() override; @@ -27,6 +34,9 @@ public: void* GetNativeHandle() override { return m_rootSignature.Get(); } ID3D12RootSignature* GetRootSignature() const { return m_rootSignature.Get(); } + bool UsesSetLayouts() const { return m_desc.setLayoutCount > 0 && m_desc.setLayouts != nullptr; } + uint32_t GetSetLayoutCount() const { return m_desc.setLayoutCount; } + bool HasConstantBufferBinding(uint32_t binding) const; uint32_t GetConstantBufferRootParameterIndex(uint32_t binding) const; bool HasShaderResourceTable() const; @@ -35,10 +45,21 @@ public: uint32_t GetUnorderedAccessTableRootParameterIndex() const; bool HasSamplerTable() const; uint32_t GetSamplerTableRootParameterIndex() const; + + bool HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const; + uint32_t GetConstantBufferRootParameterIndex(uint32_t setIndex, uint32_t binding) const; + bool HasShaderResourceTable(uint32_t setIndex) const; + uint32_t GetShaderResourceTableRootParameterIndex(uint32_t setIndex) const; + bool HasUnorderedAccessTable(uint32_t setIndex) const; + uint32_t GetUnorderedAccessTableRootParameterIndex(uint32_t setIndex) const; + bool HasSamplerTable(uint32_t setIndex) const; + uint32_t GetSamplerTableRootParameterIndex(uint32_t setIndex) const; const RHIPipelineLayoutDesc& GetDesc() const { return m_desc; } private: bool InitializeInternal(D3D12Device* device, const RHIPipelineLayoutDesc& desc); + bool InitializeSetAwareRootSignature(); + bool InitializeFlatRootSignature(); ComPtr m_rootSignature; D3D12Device* m_device; @@ -47,6 +68,7 @@ private: uint32_t m_shaderResourceTableRootIndex = UINT32_MAX; uint32_t m_unorderedAccessTableRootIndex = UINT32_MAX; uint32_t m_samplerTableRootIndex = UINT32_MAX; + std::vector m_setRootParameterMappings; std::vector m_setLayouts; std::vector> m_setLayoutBindings; std::vector m_rootParameters; diff --git a/engine/src/RHI/D3D12/D3D12CommandList.cpp b/engine/src/RHI/D3D12/D3D12CommandList.cpp index 8d4aee1b..234d9844 100644 --- a/engine/src/RHI/D3D12/D3D12CommandList.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandList.cpp @@ -197,15 +197,23 @@ void D3D12CommandList::SetGraphicsDescriptorSets( continue; } + const uint32_t setIndex = firstSet + i; D3D12DescriptorSet* d3d12Set = static_cast(descriptorSets[i]); const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings(); for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) { const DescriptorSetLayoutBinding& binding = bindings[bindingIndex]; + const bool hasBinding = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasConstantBufferBinding(setIndex, binding.binding) + : d3d12Layout->HasConstantBufferBinding(binding.binding); + const uint32_t rootParameterIndex = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetConstantBufferRootParameterIndex(setIndex, binding.binding) + : d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding); + if (static_cast(binding.type) == DescriptorType::CBV && - d3d12Layout->HasConstantBufferBinding(binding.binding) && + hasBinding && d3d12Set->UploadConstantBuffer(binding.binding)) { SetGraphicsRootConstantBufferView( - d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding), + rootParameterIndex, d3d12Set->GetConstantBufferGPUAddress(binding.binding)); } } @@ -216,28 +224,49 @@ void D3D12CommandList::SetGraphicsDescriptorSets( } if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { - if (d3d12Set->HasBindingType(DescriptorType::SRV) && d3d12Layout->HasShaderResourceTable()) { + const bool hasSrvTable = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasShaderResourceTable(setIndex) + : d3d12Layout->HasShaderResourceTable(); + const bool hasUavTable = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasUnorderedAccessTable(setIndex) + : d3d12Layout->HasUnorderedAccessTable(); + + if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) { const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); if (gpuHandle.ptr != 0) { - SetGraphicsRootDescriptorTable(d3d12Layout->GetShaderResourceTableRootParameterIndex(), gpuHandle); + SetGraphicsRootDescriptorTable( + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetShaderResourceTableRootParameterIndex(setIndex) + : d3d12Layout->GetShaderResourceTableRootParameterIndex(), + gpuHandle); } } - if (d3d12Set->HasBindingType(DescriptorType::UAV) && d3d12Layout->HasUnorderedAccessTable()) { + if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) { const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); if (gpuHandle.ptr != 0) { - SetGraphicsRootDescriptorTable(d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), gpuHandle); + SetGraphicsRootDescriptorTable( + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetUnorderedAccessTableRootParameterIndex(setIndex) + : d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), + gpuHandle); } } } else if (heap->GetType() == DescriptorHeapType::Sampler && HasSamplerBindings(d3d12Set) && - d3d12Layout->HasSamplerTable()) { + (d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasSamplerTable(setIndex) + : d3d12Layout->HasSamplerTable())) { const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); if (gpuHandle.ptr != 0) { - SetGraphicsRootDescriptorTable(d3d12Layout->GetSamplerTableRootParameterIndex(), gpuHandle); + SetGraphicsRootDescriptorTable( + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetSamplerTableRootParameterIndex(setIndex) + : d3d12Layout->GetSamplerTableRootParameterIndex(), + gpuHandle); } } } @@ -285,15 +314,23 @@ void D3D12CommandList::SetComputeDescriptorSets( continue; } + const uint32_t setIndex = firstSet + i; D3D12DescriptorSet* d3d12Set = static_cast(descriptorSets[i]); const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings(); for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) { const DescriptorSetLayoutBinding& binding = bindings[bindingIndex]; + const bool hasBinding = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasConstantBufferBinding(setIndex, binding.binding) + : d3d12Layout->HasConstantBufferBinding(binding.binding); + const uint32_t rootParameterIndex = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetConstantBufferRootParameterIndex(setIndex, binding.binding) + : d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding); + if (static_cast(binding.type) == DescriptorType::CBV && - d3d12Layout->HasConstantBufferBinding(binding.binding) && + hasBinding && d3d12Set->UploadConstantBuffer(binding.binding)) { m_commandList->SetComputeRootConstantBufferView( - d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding), + rootParameterIndex, d3d12Set->GetConstantBufferGPUAddress(binding.binding)); } } @@ -304,33 +341,48 @@ void D3D12CommandList::SetComputeDescriptorSets( } if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { - if (d3d12Set->HasBindingType(DescriptorType::SRV) && d3d12Layout->HasShaderResourceTable()) { + const bool hasSrvTable = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasShaderResourceTable(setIndex) + : d3d12Layout->HasShaderResourceTable(); + const bool hasUavTable = d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasUnorderedAccessTable(setIndex) + : d3d12Layout->HasUnorderedAccessTable(); + + if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) { const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( - d3d12Layout->GetShaderResourceTableRootParameterIndex(), + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetShaderResourceTableRootParameterIndex(setIndex) + : d3d12Layout->GetShaderResourceTableRootParameterIndex(), gpuHandle); } } - if (d3d12Set->HasBindingType(DescriptorType::UAV) && d3d12Layout->HasUnorderedAccessTable()) { + if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) { const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( - d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetUnorderedAccessTableRootParameterIndex(setIndex) + : d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), gpuHandle); } } } else if (heap->GetType() == DescriptorHeapType::Sampler && HasSamplerBindings(d3d12Set) && - d3d12Layout->HasSamplerTable()) { + (d3d12Layout->UsesSetLayouts() + ? d3d12Layout->HasSamplerTable(setIndex) + : d3d12Layout->HasSamplerTable())) { const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( - d3d12Layout->GetSamplerTableRootParameterIndex(), + d3d12Layout->UsesSetLayouts() + ? d3d12Layout->GetSamplerTableRootParameterIndex(setIndex) + : d3d12Layout->GetSamplerTableRootParameterIndex(), gpuHandle); } } diff --git a/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp b/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp index 4f226c6a..6dbf60f7 100644 --- a/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp +++ b/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp @@ -1,6 +1,8 @@ #include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h" #include "XCEngine/RHI/D3D12/D3D12Device.h" +#include + namespace XCEngine { namespace RHI { @@ -28,6 +30,52 @@ void AccumulateDescriptorCounts(const DescriptorSetLayoutDesc& setLayout, RHIPip } } +uint32_t CountBindingEntriesOfType(const DescriptorSetLayoutDesc& setLayout, DescriptorType type) { + uint32_t count = 0; + for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) { + if (static_cast(setLayout.bindings[bindingIndex].type) == type) { + ++count; + } + } + + return count; +} + +uint32_t SumDescriptorCountOfType(const DescriptorSetLayoutDesc& setLayout, DescriptorType type) { + uint32_t count = 0; + for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) { + const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex]; + if (static_cast(binding.type) == type) { + count += binding.count > 0 ? binding.count : 1u; + } + } + + return count; +} + +std::vector GatherBindingsOfTypeSorted( + const DescriptorSetLayoutDesc& setLayout, + DescriptorType type) { + std::vector bindings; + bindings.reserve(setLayout.bindingCount); + + for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) { + const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex]; + if (static_cast(binding.type) == type) { + bindings.push_back(&binding); + } + } + + std::sort( + bindings.begin(), + bindings.end(), + [](const DescriptorSetLayoutBinding* left, const DescriptorSetLayoutBinding* right) { + return left->binding < right->binding; + }); + + return bindings; +} + } // namespace D3D12PipelineLayout::D3D12PipelineLayout() @@ -88,7 +136,315 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel m_shaderResourceTableRootIndex = UINT32_MAX; m_unorderedAccessTableRootIndex = UINT32_MAX; m_samplerTableRootIndex = UINT32_MAX; + m_setRootParameterMappings.clear(); + if (UsesSetLayouts()) { + if (!InitializeSetAwareRootSignature()) { + return false; + } + } else { + if (!InitializeFlatRootSignature()) { + return false; + } + } + + D3D12_ROOT_SIGNATURE_DESC rootSigDesc = D3D12RootSignature::CreateDesc( + m_rootParameters.empty() ? nullptr : m_rootParameters.data(), + static_cast(m_rootParameters.size()), + nullptr, 0, + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ID3D12Device* d3d12Device = device->GetDevice(); + if (!d3d12Device) { + return false; + } + + ID3DBlob* signature = nullptr; + ID3DBlob* error = nullptr; + + HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); + if (FAILED(hr)) { + if (error) { + error->Release(); + } + return false; + } + + hr = d3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)); + signature->Release(); + + if (FAILED(hr)) { + return false; + } + + return true; +} + +void D3D12PipelineLayout::Shutdown() { + m_rootSignature.Reset(); + m_desc = {}; + m_rootParameters.clear(); + m_descriptorRanges.clear(); + m_constantBufferRootIndices.clear(); + m_shaderResourceTableRootIndex = UINT32_MAX; + m_unorderedAccessTableRootIndex = UINT32_MAX; + m_samplerTableRootIndex = UINT32_MAX; + m_setRootParameterMappings.clear(); + m_setLayouts.clear(); + m_setLayoutBindings.clear(); + m_device = nullptr; +} + +bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t binding) const { + if (UsesSetLayouts()) { + return HasConstantBufferBinding(0, binding); + } + + return m_constantBufferRootIndices.find(binding) != m_constantBufferRootIndices.end(); +} + +uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t binding) const { + if (UsesSetLayouts()) { + return GetConstantBufferRootParameterIndex(0, binding); + } + + auto it = m_constantBufferRootIndices.find(binding); + if (it != m_constantBufferRootIndices.end()) { + return it->second; + } + return UINT32_MAX; +} + +bool D3D12PipelineLayout::HasShaderResourceTable() const { + if (UsesSetLayouts()) { + return HasShaderResourceTable(0); + } + + return m_shaderResourceTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex() const { + if (UsesSetLayouts()) { + return GetShaderResourceTableRootParameterIndex(0); + } + + return m_shaderResourceTableRootIndex; +} + +bool D3D12PipelineLayout::HasUnorderedAccessTable() const { + if (UsesSetLayouts()) { + return HasUnorderedAccessTable(0); + } + + return m_unorderedAccessTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex() const { + if (UsesSetLayouts()) { + return GetUnorderedAccessTableRootParameterIndex(0); + } + + return m_unorderedAccessTableRootIndex; +} + +bool D3D12PipelineLayout::HasSamplerTable() const { + if (UsesSetLayouts()) { + return HasSamplerTable(0); + } + + return m_samplerTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex() const { + if (UsesSetLayouts()) { + return GetSamplerTableRootParameterIndex(0); + } + + return m_samplerTableRootIndex; +} + +bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const { + if (!UsesSetLayouts()) { + return setIndex == 0 && HasConstantBufferBinding(binding); + } + + if (setIndex >= m_setRootParameterMappings.size()) { + return false; + } + + return m_setRootParameterMappings[setIndex].constantBufferRootIndices.find(binding) != + m_setRootParameterMappings[setIndex].constantBufferRootIndices.end(); +} + +uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t setIndex, uint32_t binding) const { + if (!UsesSetLayouts()) { + return setIndex == 0 ? GetConstantBufferRootParameterIndex(binding) : UINT32_MAX; + } + + if (setIndex >= m_setRootParameterMappings.size()) { + return UINT32_MAX; + } + + const auto& rootIndices = m_setRootParameterMappings[setIndex].constantBufferRootIndices; + auto it = rootIndices.find(binding); + if (it != rootIndices.end()) { + return it->second; + } + + return UINT32_MAX; +} + +bool D3D12PipelineLayout::HasShaderResourceTable(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 && HasShaderResourceTable(); + } + + return setIndex < m_setRootParameterMappings.size() && + m_setRootParameterMappings[setIndex].shaderResourceTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 ? GetShaderResourceTableRootParameterIndex() : UINT32_MAX; + } + + return setIndex < m_setRootParameterMappings.size() + ? m_setRootParameterMappings[setIndex].shaderResourceTableRootIndex + : UINT32_MAX; +} + +bool D3D12PipelineLayout::HasUnorderedAccessTable(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 && HasUnorderedAccessTable(); + } + + return setIndex < m_setRootParameterMappings.size() && + m_setRootParameterMappings[setIndex].unorderedAccessTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 ? GetUnorderedAccessTableRootParameterIndex() : UINT32_MAX; + } + + return setIndex < m_setRootParameterMappings.size() + ? m_setRootParameterMappings[setIndex].unorderedAccessTableRootIndex + : UINT32_MAX; +} + +bool D3D12PipelineLayout::HasSamplerTable(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 && HasSamplerTable(); + } + + return setIndex < m_setRootParameterMappings.size() && + m_setRootParameterMappings[setIndex].samplerTableRootIndex != UINT32_MAX; +} + +uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex(uint32_t setIndex) const { + if (!UsesSetLayouts()) { + return setIndex == 0 ? GetSamplerTableRootParameterIndex() : UINT32_MAX; + } + + return setIndex < m_setRootParameterMappings.size() + ? m_setRootParameterMappings[setIndex].samplerTableRootIndex + : UINT32_MAX; +} + +bool D3D12PipelineLayout::InitializeSetAwareRootSignature() { + const uint32_t setCount = m_desc.setLayoutCount; + m_setRootParameterMappings.assign(setCount, SetRootParameterMapping{}); + + uint32_t rootParameterCount = 0; + uint32_t descriptorRangeCount = 0; + for (uint32_t setIndex = 0; setIndex < setCount; ++setIndex) { + const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex]; + rootParameterCount += CountBindingEntriesOfType(setLayout, DescriptorType::CBV); + rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::SRV) > 0 ? 1u : 0u; + rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::UAV) > 0 ? 1u : 0u; + rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::Sampler) > 0 ? 1u : 0u; + + descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::SRV); + descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::UAV); + descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::Sampler); + } + + m_rootParameters.reserve(rootParameterCount); + m_descriptorRanges.reserve(descriptorRangeCount); + + uint32_t rootIndex = 0; + uint32_t nextCBVRegister = 0; + uint32_t nextSRVRegister = 0; + uint32_t nextUAVRegister = 0; + uint32_t nextSamplerRegister = 0; + + for (uint32_t setIndex = 0; setIndex < setCount; ++setIndex) { + const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex]; + SetRootParameterMapping& setMapping = m_setRootParameterMappings[setIndex]; + + const auto cbvBindings = GatherBindingsOfTypeSorted(setLayout, DescriptorType::CBV); + for (const DescriptorSetLayoutBinding* binding : cbvBindings) { + m_rootParameters.push_back(D3D12RootSignature::CreateCBV(nextCBVRegister, ShaderVisibility::All, 0)); + setMapping.constantBufferRootIndices[binding->binding] = rootIndex++; + nextCBVRegister += binding->count > 0 ? binding->count : 1u; + } + + const auto appendDescriptorTable = + [this, &rootIndex]( + const DescriptorSetLayoutDesc& layout, + DescriptorType type, + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + uint32_t& nextRegister, + uint32_t& rootParameterIndex) { + const auto bindings = GatherBindingsOfTypeSorted(layout, type); + if (bindings.empty()) { + return; + } + + const size_t rangeStart = m_descriptorRanges.size(); + for (const DescriptorSetLayoutBinding* binding : bindings) { + const uint32_t descriptorCount = binding->count > 0 ? binding->count : 1u; + m_descriptorRanges.push_back( + D3D12RootSignature::CreateDescriptorRange( + rangeType, + nextRegister, + descriptorCount, + 0)); + nextRegister += descriptorCount; + } + + m_rootParameters.push_back( + D3D12RootSignature::CreateDescriptorTable( + static_cast(bindings.size()), + m_descriptorRanges.data() + rangeStart, + ShaderVisibility::All)); + rootParameterIndex = rootIndex++; + }; + + appendDescriptorTable( + setLayout, + DescriptorType::SRV, + D3D12_DESCRIPTOR_RANGE_TYPE_SRV, + nextSRVRegister, + setMapping.shaderResourceTableRootIndex); + appendDescriptorTable( + setLayout, + DescriptorType::UAV, + D3D12_DESCRIPTOR_RANGE_TYPE_UAV, + nextUAVRegister, + setMapping.unorderedAccessTableRootIndex); + appendDescriptorTable( + setLayout, + DescriptorType::Sampler, + D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, + nextSamplerRegister, + setMapping.samplerTableRootIndex); + } + + return true; +} + +bool D3D12PipelineLayout::InitializeFlatRootSignature() { const RHIPipelineLayoutDesc& normalizedDesc = m_desc; const uint32_t rootParameterCount = @@ -143,87 +499,8 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel rootIndex++; } - D3D12_ROOT_SIGNATURE_DESC rootSigDesc = D3D12RootSignature::CreateDesc( - m_rootParameters.empty() ? nullptr : m_rootParameters.data(), - static_cast(m_rootParameters.size()), - nullptr, 0, - D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); - - ID3D12Device* d3d12Device = device->GetDevice(); - if (!d3d12Device) { - return false; - } - - ID3DBlob* signature = nullptr; - ID3DBlob* error = nullptr; - - HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); - if (FAILED(hr)) { - if (error) { - error->Release(); - } - return false; - } - - hr = d3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)); - signature->Release(); - - if (FAILED(hr)) { - return false; - } - return true; } -void D3D12PipelineLayout::Shutdown() { - m_rootSignature.Reset(); - m_desc = {}; - m_rootParameters.clear(); - m_descriptorRanges.clear(); - m_constantBufferRootIndices.clear(); - m_shaderResourceTableRootIndex = UINT32_MAX; - m_unorderedAccessTableRootIndex = UINT32_MAX; - m_samplerTableRootIndex = UINT32_MAX; - m_setLayouts.clear(); - m_setLayoutBindings.clear(); - m_device = nullptr; -} - -bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t binding) const { - return m_constantBufferRootIndices.find(binding) != m_constantBufferRootIndices.end(); -} - -uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t binding) const { - auto it = m_constantBufferRootIndices.find(binding); - if (it != m_constantBufferRootIndices.end()) { - return it->second; - } - return UINT32_MAX; -} - -bool D3D12PipelineLayout::HasShaderResourceTable() const { - return m_shaderResourceTableRootIndex != UINT32_MAX; -} - -uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex() const { - return m_shaderResourceTableRootIndex; -} - -bool D3D12PipelineLayout::HasUnorderedAccessTable() const { - return m_unorderedAccessTableRootIndex != UINT32_MAX; -} - -uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex() const { - return m_unorderedAccessTableRootIndex; -} - -bool D3D12PipelineLayout::HasSamplerTable() const { - return m_samplerTableRootIndex != UINT32_MAX; -} - -uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex() const { - return m_samplerTableRootIndex; -} - } // namespace RHI } // namespace XCEngine diff --git a/tests/RHI/integration/sphere/main.cpp b/tests/RHI/integration/sphere/main.cpp index dd39f3d0..86a66ce7 100644 --- a/tests/RHI/integration/sphere/main.cpp +++ b/tests/RHI/integration/sphere/main.cpp @@ -44,6 +44,8 @@ struct MatrixBufferData { constexpr float kSphereRadius = 1.0f; constexpr int kSphereSegments = 32; constexpr float kPi = 3.14159265358979323846f; +constexpr uint32_t kSphereDescriptorFirstSet = 1; +constexpr uint32_t kSphereDescriptorSetCount = 4; std::filesystem::path GetExecutableDirectory() { char exePath[MAX_PATH] = {}; @@ -452,10 +454,15 @@ void SphereTest::InitializeSphereResources() { ASSERT_NE(mSamplerSet, nullptr); mSamplerSet->UpdateSampler(0, mSampler); + DescriptorSetLayoutDesc setLayouts[kSphereDescriptorSetCount] = {}; + // Reserve set0 so the integration test exercises non-zero firstSet binding. + setLayouts[1] = constantLayoutDesc; + setLayouts[2] = textureLayoutDesc; + setLayouts[3] = samplerLayoutDesc; + RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.constantBufferCount = 1; - pipelineLayoutDesc.textureCount = 1; - pipelineLayoutDesc.samplerCount = 1; + pipelineLayoutDesc.setLayouts = setLayouts; + pipelineLayoutDesc.setLayoutCount = kSphereDescriptorSetCount; mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc); ASSERT_NE(mPipelineLayout, nullptr); @@ -584,7 +591,7 @@ void SphereTest::RenderFrame() { cmdList->SetPipelineState(mPipelineState); RHIDescriptorSet* descriptorSets[] = { mConstantSet, mTextureSet, mSamplerSet }; - cmdList->SetGraphicsDescriptorSets(0, 3, descriptorSets, mPipelineLayout); + cmdList->SetGraphicsDescriptorSets(kSphereDescriptorFirstSet, 3, descriptorSets, mPipelineLayout); cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList); RHIResourceView* vertexBuffers[] = { mVertexBufferView }; diff --git a/tests/RHI/unit/test_pipeline_layout.cpp b/tests/RHI/unit/test_pipeline_layout.cpp index 9343b208..f8e40426 100644 --- a/tests/RHI/unit/test_pipeline_layout.cpp +++ b/tests/RHI/unit/test_pipeline_layout.cpp @@ -283,10 +283,12 @@ TEST_P(RHITestFixture, PipelineLayout_D3D12InfersBindingClassesFromSetLayouts) { ASSERT_NE(layout, nullptr); auto* d3d12Layout = static_cast(layout); - EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0)); - EXPECT_TRUE(d3d12Layout->HasShaderResourceTable()); - EXPECT_TRUE(d3d12Layout->HasUnorderedAccessTable()); - EXPECT_TRUE(d3d12Layout->HasSamplerTable()); + EXPECT_TRUE(d3d12Layout->UsesSetLayouts()); + EXPECT_EQ(d3d12Layout->GetSetLayoutCount(), 4u); + EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0, 0)); + EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(1)); + EXPECT_TRUE(d3d12Layout->HasUnorderedAccessTable(2)); + EXPECT_TRUE(d3d12Layout->HasSamplerTable(3)); EXPECT_EQ(d3d12Layout->GetDesc().constantBufferCount, 1u); EXPECT_EQ(d3d12Layout->GetDesc().textureCount, 1u); EXPECT_EQ(d3d12Layout->GetDesc().uavCount, 1u); @@ -295,3 +297,68 @@ TEST_P(RHITestFixture, PipelineLayout_D3D12InfersBindingClassesFromSetLayouts) { layout->Shutdown(); delete layout; } + +TEST_P(RHITestFixture, PipelineLayout_D3D12SeparatesOverlappingBindingsAcrossSetSlots) { + if (GetBackendType() != RHIType::D3D12) { + GTEST_SKIP() << "D3D12-specific root parameter verification"; + } + + DescriptorSetLayoutBinding set0Bindings[2] = {}; + set0Bindings[0].binding = 0; + set0Bindings[0].type = static_cast(DescriptorType::CBV); + set0Bindings[0].count = 1; + set0Bindings[1].binding = 0; + set0Bindings[1].type = static_cast(DescriptorType::SRV); + set0Bindings[1].count = 1; + + DescriptorSetLayoutBinding set1Bindings[2] = {}; + set1Bindings[0].binding = 0; + set1Bindings[0].type = static_cast(DescriptorType::CBV); + set1Bindings[0].count = 1; + set1Bindings[1].binding = 0; + set1Bindings[1].type = static_cast(DescriptorType::SRV); + set1Bindings[1].count = 1; + + DescriptorSetLayoutBinding set2Bindings[1] = {}; + set2Bindings[0].binding = 0; + set2Bindings[0].type = static_cast(DescriptorType::Sampler); + set2Bindings[0].count = 1; + + DescriptorSetLayoutDesc setLayouts[3] = {}; + setLayouts[0].bindings = set0Bindings; + setLayouts[0].bindingCount = 2; + setLayouts[1].bindings = set1Bindings; + setLayouts[1].bindingCount = 2; + setLayouts[2].bindings = set2Bindings; + setLayouts[2].bindingCount = 1; + + RHIPipelineLayoutDesc desc = {}; + desc.setLayouts = setLayouts; + desc.setLayoutCount = 3; + + RHIPipelineLayout* layout = GetDevice()->CreatePipelineLayout(desc); + ASSERT_NE(layout, nullptr); + + auto* d3d12Layout = static_cast(layout); + EXPECT_TRUE(d3d12Layout->UsesSetLayouts()); + EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0, 0)); + EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(1, 0)); + EXPECT_FALSE(d3d12Layout->HasConstantBufferBinding(2, 0)); + EXPECT_NE( + d3d12Layout->GetConstantBufferRootParameterIndex(0, 0), + d3d12Layout->GetConstantBufferRootParameterIndex(1, 0)); + + EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(0)); + EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(1)); + EXPECT_FALSE(d3d12Layout->HasShaderResourceTable(2)); + EXPECT_NE( + d3d12Layout->GetShaderResourceTableRootParameterIndex(0), + d3d12Layout->GetShaderResourceTableRootParameterIndex(1)); + + EXPECT_FALSE(d3d12Layout->HasSamplerTable(0)); + EXPECT_FALSE(d3d12Layout->HasSamplerTable(1)); + EXPECT_TRUE(d3d12Layout->HasSamplerTable(2)); + + layout->Shutdown(); + delete layout; +} diff --git a/tests/TEST_SPEC.md b/tests/TEST_SPEC.md index 3dca6efd..cd0511c7 100644 --- a/tests/TEST_SPEC.md +++ b/tests/TEST_SPEC.md @@ -174,6 +174,7 @@ tests/RHI/integration/ - `sphere_opengl.ppm` 5. 两个后端都必须与同一张 `GT.ppm` 做比对。 6. 新测试如果暴露抽象层缺口,应先补 RHI,再补测试。 +7. 至少保留一个场景覆盖 `firstSet != 0` 的描述符绑定路径,当前由 `sphere` 负责验证。 ### 6.3 命名