Add depth-only and shadow-caster pass skeletons

This commit is contained in:
2026-04-04 14:27:44 +08:00
parent c0124443cf
commit 9e8810e593
25 changed files with 1323 additions and 2 deletions

View File

@@ -1,6 +1,8 @@
#include <gtest/gtest.h>
#include <XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h>
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
#include <XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h>
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
@@ -217,6 +219,48 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyForwardResources) {
EXPECT_EQ(setLayouts[4].heapType, DescriptorHeapType::Sampler);
}
TEST(BuiltinDepthStylePass_Test, BuiltinDepthOnlyShaderDeclaresExplicitPerObjectResourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[0].set, 0u);
EXPECT_EQ(pass->resources[0].binding, 0u);
delete shader;
}
TEST(BuiltinDepthStylePass_Test, BuiltinShadowCasterShaderDeclaresExplicitPerObjectResourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[0].set, 0u);
EXPECT_EQ(pass->resources[0].binding, 0u);
delete shader;
}
TEST(BuiltinObjectIdPass_Test, BuiltinObjectIdShaderDeclaresExplicitPerObjectResourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath());
@@ -303,6 +347,40 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyObjectIdResources)
EXPECT_EQ(setLayouts[0].heapType, DescriptorHeapType::CBV_SRV_UAV);
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyDepthOnlyResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinDepthOnlyPassResourceBindings();
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);
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyShadowCasterResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinShadowCasterPassResourceBindings();
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);
}
TEST(BuiltinPassLayout_Test, RejectsMixedSamplerAndNonSamplerBindingsInOneSet) {
Array<ShaderResourceBindingDesc> bindings;
bindings.Resize(2);
@@ -353,3 +431,27 @@ TEST(BuiltinPassLayout_Test, RejectsDuplicateBindingsInOneSet) {
EXPECT_FALSE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error));
EXPECT_EQ(error, "Builtin pass encountered duplicate bindings inside one descriptor set");
}
TEST(BuiltinDepthOnlyPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
const InputLayoutDesc inputLayout = BuiltinDepthOnlyPass::BuildInputLayout();
ASSERT_EQ(inputLayout.elements.size(), 3u);
EXPECT_EQ(inputLayout.elements[0].semanticName, "POSITION");
EXPECT_EQ(inputLayout.elements[1].semanticName, "NORMAL");
EXPECT_EQ(inputLayout.elements[2].semanticName, "TEXCOORD");
EXPECT_EQ(inputLayout.elements[0].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, position)));
EXPECT_EQ(inputLayout.elements[1].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, normal)));
EXPECT_EQ(inputLayout.elements[2].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
}
TEST(BuiltinShadowCasterPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
const InputLayoutDesc inputLayout = BuiltinShadowCasterPass::BuildInputLayout();
ASSERT_EQ(inputLayout.elements.size(), 3u);
EXPECT_EQ(inputLayout.elements[0].semanticName, "POSITION");
EXPECT_EQ(inputLayout.elements[1].semanticName, "NORMAL");
EXPECT_EQ(inputLayout.elements[2].semanticName, "TEXCOORD");
EXPECT_EQ(inputLayout.elements[0].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, position)));
EXPECT_EQ(inputLayout.elements[1].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, normal)));
EXPECT_EQ(inputLayout.elements[2].alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
}