refactor(rhi): let pipeline layouts own set metadata

This commit is contained in:
2026-03-26 12:40:49 +08:00
parent 36d2f479cd
commit 476a56724f
6 changed files with 270 additions and 26 deletions

View File

@@ -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<DescriptorSetLayoutDesc> m_setLayouts;
std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings;
std::vector<D3D12_ROOT_PARAMETER> m_rootParameters;
std::vector<D3D12_DESCRIPTOR_RANGE> m_descriptorRanges;
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include <vector>
#include "../RHIPipelineLayout.h"
namespace XCEngine {
@@ -18,6 +20,8 @@ public:
private:
RHIPipelineLayoutDesc m_desc = {};
std::vector<DescriptorSetLayoutDesc> m_setLayouts;
std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings;
bool m_initialized = false;
};

View File

@@ -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;

View File

@@ -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<DescriptorType>(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<uint32_t>(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;
}

View File

@@ -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<DescriptorType>(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<uint32_t>(m_setLayouts.size());
}
m_initialized = true;
return true;
}
void OpenGLPipelineLayout::Shutdown() {
m_desc = {};
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_initialized = false;
}

View File

@@ -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<uint32_t>(DescriptorType::CBV);
set0Bindings[0].count = 1;
DescriptorSetLayoutBinding set1Bindings[2] = {};
set1Bindings[0].binding = 1;
set1Bindings[0].type = static_cast<uint32_t>(DescriptorType::SRV);
set1Bindings[0].count = 2;
set1Bindings[1].binding = 3;
set1Bindings[1].type = static_cast<uint32_t>(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<D3D12PipelineLayout*>(layout)->GetDesc();
} else {
storedDesc = &static_cast<OpenGLPipelineLayout*>(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<uint32_t>(DescriptorType::CBV);
set0Bindings[0].count = 1;
DescriptorSetLayoutBinding set1Bindings[1] = {};
set1Bindings[0].binding = 0;
set1Bindings[0].type = static_cast<uint32_t>(DescriptorType::SRV);
set1Bindings[0].count = 1;
DescriptorSetLayoutBinding set2Bindings[1] = {};
set2Bindings[0].binding = 0;
set2Bindings[0].type = static_cast<uint32_t>(DescriptorType::UAV);
set2Bindings[0].count = 1;
DescriptorSetLayoutBinding set3Bindings[1] = {};
set3Bindings[0].binding = 0;
set3Bindings[0].type = static_cast<uint32_t>(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<D3D12PipelineLayout*>(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;
}