From 476a56724ffa70823006c8ed4148cf2dd2df59c3 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 26 Mar 2026 12:40:49 +0800 Subject: [PATCH] refactor(rhi): let pipeline layouts own set metadata --- .../XCEngine/RHI/D3D12/D3D12PipelineLayout.h | 2 + .../RHI/OpenGL/OpenGLPipelineLayout.h | 4 + engine/include/XCEngine/RHI/RHITypes.h | 26 ++-- engine/src/RHI/D3D12/D3D12PipelineLayout.cpp | 90 +++++++++++--- .../src/RHI/OpenGL/OpenGLPipelineLayout.cpp | 61 ++++++++++ tests/RHI/unit/test_pipeline_layout.cpp | 113 ++++++++++++++++++ 6 files changed, 270 insertions(+), 26 deletions(-) diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h b/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h index 802941ab..bbd2bf46 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12PipelineLayout.h @@ -47,6 +47,8 @@ private: uint32_t m_shaderResourceTableRootIndex = UINT32_MAX; uint32_t m_unorderedAccessTableRootIndex = UINT32_MAX; uint32_t m_samplerTableRootIndex = UINT32_MAX; + std::vector m_setLayouts; + std::vector> m_setLayoutBindings; std::vector m_rootParameters; std::vector m_descriptorRanges; }; diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h index e9e24f9a..4b82fa9e 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "../RHIPipelineLayout.h" namespace XCEngine { @@ -18,6 +20,8 @@ public: private: RHIPipelineLayoutDesc m_desc = {}; + std::vector m_setLayouts; + std::vector> m_setLayoutBindings; bool m_initialized = false; }; diff --git a/engine/include/XCEngine/RHI/RHITypes.h b/engine/include/XCEngine/RHI/RHITypes.h index f0a73686..9c2e7967 100644 --- a/engine/include/XCEngine/RHI/RHITypes.h +++ b/engine/include/XCEngine/RHI/RHITypes.h @@ -365,7 +365,21 @@ struct RHIRenderPassDesc { bool hasDepthStencil = false; }; +struct DescriptorSetLayoutBinding { + uint32_t binding = 0; + uint32_t type = 0; + uint32_t count = 0; + uint32_t visibility = 0; +}; + +struct DescriptorSetLayoutDesc { + DescriptorSetLayoutBinding* bindings = nullptr; + uint32_t bindingCount = 0; +}; + struct RHIPipelineLayoutDesc { + DescriptorSetLayoutDesc* setLayouts = nullptr; + uint32_t setLayoutCount = 0; uint32_t constantBufferCount = 0; uint32_t textureCount = 0; uint32_t samplerCount = 0; @@ -383,18 +397,6 @@ struct ResourceViewDesc { uint32_t structureByteStride = 0; }; -struct DescriptorSetLayoutBinding { - uint32_t binding = 0; - uint32_t type = 0; - uint32_t count = 0; - uint32_t visibility = 0; -}; - -struct DescriptorSetLayoutDesc { - DescriptorSetLayoutBinding* bindings = nullptr; - uint32_t bindingCount = 0; -}; - struct DescriptorPoolDesc { void* device = nullptr; DescriptorHeapType type = DescriptorHeapType::CBV_SRV_UAV; diff --git a/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp b/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp index 3654dd6d..4f226c6a 100644 --- a/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp +++ b/engine/src/RHI/D3D12/D3D12PipelineLayout.cpp @@ -4,6 +4,32 @@ namespace XCEngine { namespace RHI { +namespace { + +void AccumulateDescriptorCounts(const DescriptorSetLayoutDesc& setLayout, RHIPipelineLayoutDesc& desc) { + for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) { + const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex]; + switch (static_cast(binding.type)) { + case DescriptorType::CBV: + desc.constantBufferCount += binding.count; + break; + case DescriptorType::SRV: + desc.textureCount += binding.count; + break; + case DescriptorType::UAV: + desc.uavCount += binding.count; + break; + case DescriptorType::Sampler: + desc.samplerCount += binding.count; + break; + default: + break; + } + } +} + +} // namespace + D3D12PipelineLayout::D3D12PipelineLayout() : m_device(nullptr) { } @@ -23,6 +49,38 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel m_device = device; m_desc = desc; + m_setLayouts.clear(); + m_setLayoutBindings.clear(); + + if (desc.setLayoutCount > 0 && desc.setLayouts != nullptr) { + m_desc.constantBufferCount = 0; + m_desc.textureCount = 0; + m_desc.samplerCount = 0; + m_desc.uavCount = 0; + + m_setLayouts.resize(desc.setLayoutCount); + m_setLayoutBindings.resize(desc.setLayoutCount); + + for (uint32_t setIndex = 0; setIndex < desc.setLayoutCount; ++setIndex) { + const DescriptorSetLayoutDesc& srcSetLayout = desc.setLayouts[setIndex]; + auto& dstBindings = m_setLayoutBindings[setIndex]; + if (srcSetLayout.bindingCount > 0 && srcSetLayout.bindings != nullptr) { + dstBindings.assign( + srcSetLayout.bindings, + srcSetLayout.bindings + srcSetLayout.bindingCount); + } + + DescriptorSetLayoutDesc dstSetLayout = {}; + dstSetLayout.bindingCount = srcSetLayout.bindingCount; + dstSetLayout.bindings = dstBindings.empty() ? nullptr : dstBindings.data(); + m_setLayouts[setIndex] = dstSetLayout; + + AccumulateDescriptorCounts(dstSetLayout, m_desc); + } + + m_desc.setLayouts = m_setLayouts.data(); + m_desc.setLayoutCount = static_cast(m_setLayouts.size()); + } m_rootParameters.clear(); m_descriptorRanges.clear(); @@ -31,31 +89,33 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel m_unorderedAccessTableRootIndex = UINT32_MAX; m_samplerTableRootIndex = UINT32_MAX; + const RHIPipelineLayoutDesc& normalizedDesc = m_desc; + const uint32_t rootParameterCount = - desc.constantBufferCount + - (desc.textureCount > 0 ? 1u : 0u) + - (desc.uavCount > 0 ? 1u : 0u) + - (desc.samplerCount > 0 ? 1u : 0u); + normalizedDesc.constantBufferCount + + (normalizedDesc.textureCount > 0 ? 1u : 0u) + + (normalizedDesc.uavCount > 0 ? 1u : 0u) + + (normalizedDesc.samplerCount > 0 ? 1u : 0u); const uint32_t descriptorRangeCount = - (desc.textureCount > 0 ? 1u : 0u) + - (desc.uavCount > 0 ? 1u : 0u) + - (desc.samplerCount > 0 ? 1u : 0u); + (normalizedDesc.textureCount > 0 ? 1u : 0u) + + (normalizedDesc.uavCount > 0 ? 1u : 0u) + + (normalizedDesc.samplerCount > 0 ? 1u : 0u); m_rootParameters.reserve(rootParameterCount); m_descriptorRanges.reserve(descriptorRangeCount); uint32_t rootIndex = 0; - for (uint32_t i = 0; i < desc.constantBufferCount; ++i) { + for (uint32_t i = 0; i < normalizedDesc.constantBufferCount; ++i) { D3D12_ROOT_PARAMETER param = D3D12RootSignature::CreateCBV(i, ShaderVisibility::All, 0); m_rootParameters.push_back(param); m_constantBufferRootIndices[i] = rootIndex; rootIndex++; } - if (desc.textureCount > 0) { + if (normalizedDesc.textureCount > 0) { m_descriptorRanges.push_back(D3D12RootSignature::CreateDescriptorRange( - D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, desc.textureCount, 0)); + D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, normalizedDesc.textureCount, 0)); D3D12_ROOT_PARAMETER param = D3D12RootSignature::CreateDescriptorTable( 1, &m_descriptorRanges.back(), ShaderVisibility::All); m_rootParameters.push_back(param); @@ -63,9 +123,9 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel rootIndex++; } - if (desc.uavCount > 0) { + if (normalizedDesc.uavCount > 0) { m_descriptorRanges.push_back(D3D12RootSignature::CreateDescriptorRange( - D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, desc.uavCount, 0)); + D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, normalizedDesc.uavCount, 0)); D3D12_ROOT_PARAMETER param = D3D12RootSignature::CreateDescriptorTable( 1, &m_descriptorRanges.back(), ShaderVisibility::All); m_rootParameters.push_back(param); @@ -73,9 +133,9 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel rootIndex++; } - if (desc.samplerCount > 0) { + if (normalizedDesc.samplerCount > 0) { m_descriptorRanges.push_back(D3D12RootSignature::CreateDescriptorRange( - D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, desc.samplerCount, 0)); + D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, normalizedDesc.samplerCount, 0)); D3D12_ROOT_PARAMETER param = D3D12RootSignature::CreateDescriptorTable( 1, &m_descriptorRanges.back(), ShaderVisibility::All); m_rootParameters.push_back(param); @@ -124,6 +184,8 @@ void D3D12PipelineLayout::Shutdown() { m_shaderResourceTableRootIndex = UINT32_MAX; m_unorderedAccessTableRootIndex = UINT32_MAX; m_samplerTableRootIndex = UINT32_MAX; + m_setLayouts.clear(); + m_setLayoutBindings.clear(); m_device = nullptr; } diff --git a/engine/src/RHI/OpenGL/OpenGLPipelineLayout.cpp b/engine/src/RHI/OpenGL/OpenGLPipelineLayout.cpp index 4d899b13..c931fb0e 100644 --- a/engine/src/RHI/OpenGL/OpenGLPipelineLayout.cpp +++ b/engine/src/RHI/OpenGL/OpenGLPipelineLayout.cpp @@ -3,14 +3,75 @@ namespace XCEngine { namespace RHI { +namespace { + +void AccumulateDescriptorCounts(const DescriptorSetLayoutDesc& setLayout, RHIPipelineLayoutDesc& desc) { + for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) { + const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex]; + switch (static_cast(binding.type)) { + case DescriptorType::CBV: + desc.constantBufferCount += binding.count; + break; + case DescriptorType::SRV: + desc.textureCount += binding.count; + break; + case DescriptorType::UAV: + desc.uavCount += binding.count; + break; + case DescriptorType::Sampler: + desc.samplerCount += binding.count; + break; + default: + break; + } + } +} + +} // namespace + bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) { m_desc = desc; + m_setLayouts.clear(); + m_setLayoutBindings.clear(); + + if (desc.setLayoutCount > 0 && desc.setLayouts != nullptr) { + m_desc.constantBufferCount = 0; + m_desc.textureCount = 0; + m_desc.samplerCount = 0; + m_desc.uavCount = 0; + + m_setLayouts.resize(desc.setLayoutCount); + m_setLayoutBindings.resize(desc.setLayoutCount); + + for (uint32_t setIndex = 0; setIndex < desc.setLayoutCount; ++setIndex) { + const DescriptorSetLayoutDesc& srcSetLayout = desc.setLayouts[setIndex]; + auto& dstBindings = m_setLayoutBindings[setIndex]; + if (srcSetLayout.bindingCount > 0 && srcSetLayout.bindings != nullptr) { + dstBindings.assign( + srcSetLayout.bindings, + srcSetLayout.bindings + srcSetLayout.bindingCount); + } + + DescriptorSetLayoutDesc dstSetLayout = {}; + dstSetLayout.bindingCount = srcSetLayout.bindingCount; + dstSetLayout.bindings = dstBindings.empty() ? nullptr : dstBindings.data(); + m_setLayouts[setIndex] = dstSetLayout; + + AccumulateDescriptorCounts(dstSetLayout, m_desc); + } + + m_desc.setLayouts = m_setLayouts.data(); + m_desc.setLayoutCount = static_cast(m_setLayouts.size()); + } + m_initialized = true; return true; } void OpenGLPipelineLayout::Shutdown() { m_desc = {}; + m_setLayouts.clear(); + m_setLayoutBindings.clear(); m_initialized = false; } diff --git a/tests/RHI/unit/test_pipeline_layout.cpp b/tests/RHI/unit/test_pipeline_layout.cpp index 68bfee8f..9343b208 100644 --- a/tests/RHI/unit/test_pipeline_layout.cpp +++ b/tests/RHI/unit/test_pipeline_layout.cpp @@ -1,5 +1,6 @@ #include "fixtures/RHITestFixture.h" #include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h" +#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h" #include "XCEngine/RHI/RHIPipelineLayout.h" #include "XCEngine/RHI/RHIDescriptorSet.h" @@ -146,6 +147,62 @@ TEST_P(RHITestFixture, PipelineLayout_DescriptorSetAllocation) { delete layout; } +TEST_P(RHITestFixture, PipelineLayout_DeepCopiesSetLayoutsAndInfersCounts) { + DescriptorSetLayoutBinding set0Bindings[1] = {}; + set0Bindings[0].binding = 0; + set0Bindings[0].type = static_cast(DescriptorType::CBV); + set0Bindings[0].count = 1; + + DescriptorSetLayoutBinding set1Bindings[2] = {}; + set1Bindings[0].binding = 1; + set1Bindings[0].type = static_cast(DescriptorType::SRV); + set1Bindings[0].count = 2; + set1Bindings[1].binding = 3; + set1Bindings[1].type = static_cast(DescriptorType::Sampler); + set1Bindings[1].count = 1; + + DescriptorSetLayoutDesc setLayouts[2] = {}; + setLayouts[0].bindings = set0Bindings; + setLayouts[0].bindingCount = 1; + setLayouts[1].bindings = set1Bindings; + setLayouts[1].bindingCount = 2; + + RHIPipelineLayoutDesc desc = {}; + desc.setLayouts = setLayouts; + desc.setLayoutCount = 2; + + RHIPipelineLayout* layout = GetDevice()->CreatePipelineLayout(desc); + ASSERT_NE(layout, nullptr); + + set0Bindings[0].binding = 99; + set1Bindings[0].count = 7; + setLayouts[1].bindingCount = 0; + + const RHIPipelineLayoutDesc* storedDesc = nullptr; + if (GetBackendType() == RHIType::D3D12) { + storedDesc = &static_cast(layout)->GetDesc(); + } else { + storedDesc = &static_cast(layout)->GetDesc(); + } + + ASSERT_NE(storedDesc, nullptr); + ASSERT_EQ(storedDesc->setLayoutCount, 2u); + ASSERT_NE(storedDesc->setLayouts, nullptr); + ASSERT_EQ(storedDesc->setLayouts[0].bindingCount, 1u); + ASSERT_EQ(storedDesc->setLayouts[1].bindingCount, 2u); + ASSERT_NE(storedDesc->setLayouts[0].bindings, nullptr); + ASSERT_NE(storedDesc->setLayouts[1].bindings, nullptr); + EXPECT_EQ(storedDesc->setLayouts[0].bindings[0].binding, 0u); + EXPECT_EQ(storedDesc->setLayouts[1].bindings[0].count, 2u); + EXPECT_EQ(storedDesc->constantBufferCount, 1u); + EXPECT_EQ(storedDesc->textureCount, 2u); + EXPECT_EQ(storedDesc->samplerCount, 1u); + EXPECT_EQ(storedDesc->uavCount, 0u); + + layout->Shutdown(); + delete layout; +} + TEST_P(RHITestFixture, PipelineLayout_D3D12TracksDistinctBindingClasses) { if (GetBackendType() != RHIType::D3D12) { GTEST_SKIP() << "D3D12-specific root parameter verification"; @@ -182,3 +239,59 @@ TEST_P(RHITestFixture, PipelineLayout_D3D12TracksDistinctBindingClasses) { layout->Shutdown(); delete layout; } + +TEST_P(RHITestFixture, PipelineLayout_D3D12InfersBindingClassesFromSetLayouts) { + if (GetBackendType() != RHIType::D3D12) { + GTEST_SKIP() << "D3D12-specific root parameter verification"; + } + + DescriptorSetLayoutBinding set0Bindings[1] = {}; + set0Bindings[0].binding = 0; + set0Bindings[0].type = static_cast(DescriptorType::CBV); + set0Bindings[0].count = 1; + + DescriptorSetLayoutBinding set1Bindings[1] = {}; + set1Bindings[0].binding = 0; + set1Bindings[0].type = static_cast(DescriptorType::SRV); + set1Bindings[0].count = 1; + + DescriptorSetLayoutBinding set2Bindings[1] = {}; + set2Bindings[0].binding = 0; + set2Bindings[0].type = static_cast(DescriptorType::UAV); + set2Bindings[0].count = 1; + + DescriptorSetLayoutBinding set3Bindings[1] = {}; + set3Bindings[0].binding = 0; + set3Bindings[0].type = static_cast(DescriptorType::Sampler); + set3Bindings[0].count = 1; + + DescriptorSetLayoutDesc setLayouts[4] = {}; + setLayouts[0].bindings = set0Bindings; + setLayouts[0].bindingCount = 1; + setLayouts[1].bindings = set1Bindings; + setLayouts[1].bindingCount = 1; + setLayouts[2].bindings = set2Bindings; + setLayouts[2].bindingCount = 1; + setLayouts[3].bindings = set3Bindings; + setLayouts[3].bindingCount = 1; + + RHIPipelineLayoutDesc desc = {}; + desc.setLayouts = setLayouts; + desc.setLayoutCount = 4; + + RHIPipelineLayout* layout = GetDevice()->CreatePipelineLayout(desc); + 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_EQ(d3d12Layout->GetDesc().constantBufferCount, 1u); + EXPECT_EQ(d3d12Layout->GetDesc().textureCount, 1u); + EXPECT_EQ(d3d12Layout->GetDesc().uavCount, 1u); + EXPECT_EQ(d3d12Layout->GetDesc().samplerCount, 1u); + + layout->Shutdown(); + delete layout; +}