Share builtin pass layout assembly utilities

This commit is contained in:
2026-04-04 13:48:13 +08:00
parent 0ebd2d4979
commit a3ba08bb99
7 changed files with 328 additions and 180 deletions

View File

@@ -184,6 +184,39 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLegacy
EXPECT_EQ(plan.linearClampSampler.set, 4u);
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyForwardResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinForwardPassResourceBindings();
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
ASSERT_EQ(setLayouts.size(), 5u);
EXPECT_EQ(setLayouts[0].layout.bindingCount, 0u);
EXPECT_EQ(setLayouts[1].layout.bindingCount, 1u);
EXPECT_FALSE(setLayouts[1].shaderVisible);
EXPECT_TRUE(setLayouts[1].usesPerObject);
EXPECT_FALSE(setLayouts[1].usesMaterial);
EXPECT_EQ(setLayouts[2].layout.bindingCount, 1u);
EXPECT_TRUE(setLayouts[2].usesMaterial);
EXPECT_FALSE(setLayouts[2].shaderVisible);
EXPECT_EQ(setLayouts[3].layout.bindingCount, 1u);
EXPECT_TRUE(setLayouts[3].usesTexture);
EXPECT_TRUE(setLayouts[3].shaderVisible);
EXPECT_EQ(setLayouts[3].heapType, DescriptorHeapType::CBV_SRV_UAV);
EXPECT_EQ(setLayouts[4].layout.bindingCount, 1u);
EXPECT_TRUE(setLayouts[4].usesSampler);
EXPECT_TRUE(setLayouts[4].shaderVisible);
EXPECT_EQ(setLayouts[4].heapType, DescriptorHeapType::Sampler);
}
TEST(BuiltinObjectIdPass_Test, BuiltinObjectIdShaderDeclaresExplicitPerObjectResourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath());
@@ -227,7 +260,7 @@ TEST(BuiltinObjectIdPass_Test, BuildsBuiltinPassResourceBindingPlanFromLegacyFal
TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout();
ASSERT_EQ(inputLayout.elements.size(), 1u);
ASSERT_EQ(inputLayout.elements.size(), 3u);
const InputElementDesc& position = inputLayout.elements[0];
EXPECT_EQ(position.semanticName, "POSITION");
@@ -235,4 +268,88 @@ TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertice
EXPECT_EQ(position.format, static_cast<uint32_t>(Format::R32G32B32_Float));
EXPECT_EQ(position.inputSlot, 0u);
EXPECT_EQ(position.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, position)));
const InputElementDesc& normal = inputLayout.elements[1];
EXPECT_EQ(normal.semanticName, "NORMAL");
EXPECT_EQ(normal.semanticIndex, 0u);
EXPECT_EQ(normal.format, static_cast<uint32_t>(Format::R32G32B32_Float));
EXPECT_EQ(normal.inputSlot, 0u);
EXPECT_EQ(normal.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, normal)));
const InputElementDesc& texcoord = inputLayout.elements[2];
EXPECT_EQ(texcoord.semanticName, "TEXCOORD");
EXPECT_EQ(texcoord.semanticIndex, 0u);
EXPECT_EQ(texcoord.format, static_cast<uint32_t>(Format::R32G32_Float));
EXPECT_EQ(texcoord.inputSlot, 0u);
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyObjectIdResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinObjectIdPassResourceBindings();
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
ASSERT_EQ(setLayouts.size(), 1u);
EXPECT_EQ(setLayouts[0].layout.bindingCount, 1u);
EXPECT_TRUE(setLayouts[0].usesPerObject);
EXPECT_FALSE(setLayouts[0].usesMaterial);
EXPECT_FALSE(setLayouts[0].usesTexture);
EXPECT_FALSE(setLayouts[0].usesSampler);
EXPECT_FALSE(setLayouts[0].shaderVisible);
EXPECT_EQ(setLayouts[0].heapType, DescriptorHeapType::CBV_SRV_UAV);
}
TEST(BuiltinPassLayout_Test, RejectsMixedSamplerAndNonSamplerBindingsInOneSet) {
Array<ShaderResourceBindingDesc> bindings;
bindings.Resize(2);
bindings[0].name = "BaseColorTexture";
bindings[0].type = ShaderResourceType::Texture2D;
bindings[0].set = 0;
bindings[0].binding = 0;
bindings[0].semantic = "BaseColorTexture";
bindings[1].name = "LinearClampSampler";
bindings[1].type = ShaderResourceType::Sampler;
bindings[1].set = 0;
bindings[1].binding = 1;
bindings[1].semantic = "LinearClampSampler";
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
EXPECT_FALSE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error));
EXPECT_EQ(error, "Builtin pass does not support mixing sampler and non-sampler bindings in one set");
}
TEST(BuiltinPassLayout_Test, RejectsDuplicateBindingsInOneSet) {
BuiltinPassResourceBindingPlan plan = {};
BuiltinPassResourceBindingDesc perObjectBinding = {};
perObjectBinding.semantic = BuiltinPassResourceSemantic::PerObject;
perObjectBinding.resourceType = ShaderResourceType::ConstantBuffer;
perObjectBinding.location = { 0, 0 };
plan.bindings.PushBack(perObjectBinding);
BuiltinPassResourceBindingDesc materialBinding = {};
materialBinding.semantic = BuiltinPassResourceSemantic::Material;
materialBinding.resourceType = ShaderResourceType::ConstantBuffer;
materialBinding.location = { 0, 0 };
plan.bindings.PushBack(materialBinding);
plan.maxSetIndex = 0;
plan.firstDescriptorSet = 0;
plan.descriptorSetCount = 1;
plan.usesConstantBuffers = true;
plan.perObject = { 0, 0 };
plan.material = { 0, 0 };
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
String error;
EXPECT_FALSE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error));
EXPECT_EQ(error, "Builtin pass encountered duplicate bindings inside one descriptor set");
}