Add gaussian splat compute shader contracts

This commit is contained in:
2026-04-11 01:30:59 +08:00
parent 4080b2e5fe
commit d9bc0f1457
9 changed files with 442 additions and 8 deletions

View File

@@ -1087,6 +1087,201 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatUtilitiesShaderUsesComputeAuthoringContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
ASSERT_NE(pass, nullptr);
EXPECT_EQ(pass->resources.Size(), 4u);
const ShaderResourceBindingDesc* perObject =
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "PerObjectConstants");
ASSERT_NE(perObject, nullptr);
EXPECT_EQ(perObject->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(perObject->set, 0u);
EXPECT_EQ(perObject->binding, 0u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*perObject),
BuiltinPassResourceSemantic::PerObject);
const ShaderResourceBindingDesc* positions =
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatPositions");
ASSERT_NE(positions, nullptr);
EXPECT_EQ(positions->type, ShaderResourceType::StructuredBuffer);
EXPECT_EQ(positions->set, 2u);
EXPECT_EQ(positions->binding, 0u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*positions),
BuiltinPassResourceSemantic::GaussianSplatPositionBuffer);
const ShaderResourceBindingDesc* sortDistances =
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatSortDistances");
ASSERT_NE(sortDistances, nullptr);
EXPECT_EQ(sortDistances->type, ShaderResourceType::RWStructuredBuffer);
EXPECT_EQ(sortDistances->set, 4u);
EXPECT_EQ(sortDistances->binding, 0u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*sortDistances),
BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer);
const ShaderResourceBindingDesc* orderBuffer =
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatOrderBuffer");
ASSERT_NE(orderBuffer, nullptr);
EXPECT_EQ(orderBuffer->type, ShaderResourceType::RWStructuredBuffer);
EXPECT_EQ(orderBuffer->set, 4u);
EXPECT_EQ(orderBuffer->binding, 1u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*orderBuffer),
BuiltinPassResourceSemantic::GaussianSplatOrderBuffer);
const ShaderStageVariant* computeVariant = shader->FindVariant(
"GaussianSplatPrepareOrder",
XCEngine::Resources::ShaderType::Compute,
XCEngine::Resources::ShaderBackend::D3D12);
ASSERT_NE(computeVariant, nullptr);
EXPECT_EQ(computeVariant->entryPoint, "GaussianSplatPrepareOrderCS");
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatUtilitiesShaderContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
ASSERT_NE(pass, nullptr);
BuiltinPassResourceBindingPlan plan = {};
String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
ASSERT_EQ(plan.bindings.Size(), 4u);
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
EXPECT_TRUE(plan.gaussianSplatSortDistanceBuffer.IsValid());
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
EXPECT_EQ(plan.perObject.set, 0u);
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.set, 4u);
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.binding, 0u);
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 4u);
EXPECT_EQ(plan.gaussianSplatOrderBuffer.binding, 1u);
EXPECT_EQ(plan.firstDescriptorSet, 0u);
EXPECT_EQ(plan.descriptorSetCount, 5u);
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
ASSERT_EQ(setLayouts.size(), 5u);
EXPECT_TRUE(setLayouts[0].usesPerObject);
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
EXPECT_TRUE(setLayouts[4].usesGaussianSplatSortDistanceBuffer);
EXPECT_TRUE(setLayouts[4].usesGaussianSplatOrderBuffer);
ASSERT_EQ(setLayouts[4].bindings.size(), 2u);
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
DescriptorType::UAV);
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[4].bindings[1].type),
DescriptorType::UAV);
EXPECT_EQ(
setLayouts[4].bindings[0].resourceDimension,
ResourceViewDimension::StructuredBuffer);
EXPECT_EQ(
setLayouts[4].bindings[1].resourceDimension,
ResourceViewDimension::StructuredBuffer);
delete shader;
}
TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesGaussianSplatUtilitiesComputeBindingsToDescriptorSpaces) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
ASSERT_NE(pass, nullptr);
const ShaderStageVariant* d3d12Compute = shader->FindVariant(
"GaussianSplatPrepareOrder",
XCEngine::Resources::ShaderType::Compute,
XCEngine::Resources::ShaderBackend::D3D12);
ASSERT_NE(d3d12Compute, nullptr);
ShaderCompileDesc d3d12CompileDesc = {};
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::D3D12,
*d3d12Compute,
d3d12CompileDesc);
const std::string d3d12Source(
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
d3d12CompileDesc.source.size());
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"cbuffer PerObjectConstants",
"register(b0)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"StructuredBuffer<float3> GaussianSplatPositions",
"register(t0)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
"register(u0)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"RWStructuredBuffer<uint> GaussianSplatOrderBuffer",
"register(u1)"));
const ShaderStageVariant* vulkanCompute = shader->FindVariant(
"GaussianSplatPrepareOrder",
XCEngine::Resources::ShaderType::Compute,
XCEngine::Resources::ShaderBackend::Vulkan);
ASSERT_NE(vulkanCompute, nullptr);
ShaderCompileDesc vulkanCompileDesc = {};
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::Vulkan,
*vulkanCompute,
vulkanCompileDesc);
const std::string vulkanSource(
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
vulkanCompileDesc.source.size());
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"cbuffer PerObjectConstants",
"register(b0, space0)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"StructuredBuffer<float3> GaussianSplatPositions",
"register(t0, space2)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
"register(u0, space4)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"RWStructuredBuffer<uint> GaussianSplatOrderBuffer",
"register(u1, space4)"));
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinVolumetricShaderPath());