Formalize gaussian splat prepare-order pass

This commit is contained in:
2026-04-11 03:02:30 +08:00
parent 5191bb1149
commit fac6e588a8
16 changed files with 1529 additions and 68 deletions

View File

@@ -885,7 +885,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
const ShaderPass* pass = shader->FindPass("GaussianSplat");
ASSERT_NE(pass, nullptr);
EXPECT_EQ(pass->resources.Size(), 5u);
EXPECT_EQ(pass->resources.Size(), 6u);
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);
@@ -900,12 +900,22 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
ASSERT_NE(opacityScale, nullptr);
EXPECT_EQ(opacityScale->type, ShaderPropertyType::Float);
const ShaderResourceBindingDesc* order =
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatOrderBuffer");
ASSERT_NE(order, nullptr);
EXPECT_EQ(order->type, ShaderResourceType::StructuredBuffer);
EXPECT_EQ(order->set, 2u);
EXPECT_EQ(order->binding, 0u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*order),
BuiltinPassResourceSemantic::GaussianSplatOrderBuffer);
const ShaderResourceBindingDesc* positions =
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatPositions");
ASSERT_NE(positions, nullptr);
EXPECT_EQ(positions->type, ShaderResourceType::StructuredBuffer);
EXPECT_EQ(positions->set, 2u);
EXPECT_EQ(positions->binding, 0u);
EXPECT_EQ(positions->binding, 1u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*positions),
BuiltinPassResourceSemantic::GaussianSplatPositionBuffer);
@@ -915,7 +925,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
ASSERT_NE(other, nullptr);
EXPECT_EQ(other->type, ShaderResourceType::StructuredBuffer);
EXPECT_EQ(other->set, 2u);
EXPECT_EQ(other->binding, 1u);
EXPECT_EQ(other->binding, 2u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*other),
BuiltinPassResourceSemantic::GaussianSplatOtherBuffer);
@@ -925,7 +935,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
ASSERT_NE(color, nullptr);
EXPECT_EQ(color->type, ShaderResourceType::StructuredBuffer);
EXPECT_EQ(color->set, 2u);
EXPECT_EQ(color->binding, 2u);
EXPECT_EQ(color->binding, 3u);
EXPECT_EQ(
ResolveBuiltinPassResourceSemantic(*color),
BuiltinPassResourceSemantic::GaussianSplatColorBuffer);
@@ -948,21 +958,24 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
BuiltinPassResourceBindingPlan plan = {};
String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
ASSERT_EQ(plan.bindings.Size(), 5u);
ASSERT_EQ(plan.bindings.Size(), 6u);
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.material.IsValid());
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
EXPECT_FALSE(plan.gaussianSplatSHBuffer.IsValid());
EXPECT_EQ(plan.perObject.set, 0u);
EXPECT_EQ(plan.material.set, 1u);
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 2u);
EXPECT_EQ(plan.gaussianSplatOrderBuffer.binding, 0u);
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
EXPECT_EQ(plan.gaussianSplatPositionBuffer.binding, 0u);
EXPECT_EQ(plan.gaussianSplatPositionBuffer.binding, 1u);
EXPECT_EQ(plan.gaussianSplatOtherBuffer.set, 2u);
EXPECT_EQ(plan.gaussianSplatOtherBuffer.binding, 1u);
EXPECT_EQ(plan.gaussianSplatOtherBuffer.binding, 2u);
EXPECT_EQ(plan.gaussianSplatColorBuffer.set, 2u);
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 2u);
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 3u);
EXPECT_EQ(plan.firstDescriptorSet, 0u);
EXPECT_EQ(plan.descriptorSetCount, 3u);
@@ -971,10 +984,11 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
ASSERT_EQ(setLayouts.size(), 3u);
EXPECT_TRUE(setLayouts[0].usesPerObject);
EXPECT_TRUE(setLayouts[1].usesMaterial);
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOrderBuffer);
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
ASSERT_EQ(setLayouts[2].bindings.size(), 3u);
ASSERT_EQ(setLayouts[2].bindings.size(), 4u);
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
DescriptorType::SRV);
@@ -984,6 +998,9 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[2].bindings[2].type),
DescriptorType::SRV);
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[2].bindings[3].type),
DescriptorType::SRV);
EXPECT_EQ(
setLayouts[2].bindings[0].resourceDimension,
ResourceViewDimension::StructuredBuffer);
@@ -993,6 +1010,9 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
EXPECT_EQ(
setLayouts[2].bindings[2].resourceDimension,
ResourceViewDimension::StructuredBuffer);
EXPECT_EQ(
setLayouts[2].bindings[3].resourceDimension,
ResourceViewDimension::StructuredBuffer);
delete shader;
}
@@ -1034,16 +1054,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
"register(b1)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"StructuredBuffer<float3> GaussianSplatPositions",
"StructuredBuffer<uint> GaussianSplatOrderBuffer",
"register(t0)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
"StructuredBuffer<float3> GaussianSplatPositions",
"register(t1)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"StructuredBuffer<float4> GaussianSplatColor",
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
"register(t2)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
d3d12Source,
"StructuredBuffer<float4> GaussianSplatColor",
"register(t3)"));
EXPECT_EQ(d3d12Source.find("space0"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space1"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space2"), std::string::npos);
@@ -1073,16 +1097,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
"register(b0, space1)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"StructuredBuffer<float3> GaussianSplatPositions",
"StructuredBuffer<uint> GaussianSplatOrderBuffer",
"register(t0, space2)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
"StructuredBuffer<float3> GaussianSplatPositions",
"register(t1, space2)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"StructuredBuffer<float4> GaussianSplatColor",
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
"register(t2, space2)"));
EXPECT_TRUE(SourceContainsRegisterBinding(
vulkanSource,
"StructuredBuffer<float4> GaussianSplatColor",
"register(t3, space2)"));
delete shader;
}
@@ -2410,6 +2438,57 @@ TEST(BuiltinPassLayout_Test, AcceptsRuntimeMaterialRawUavBufferBindings) {
EXPECT_TRUE(setLayouts[4].usesMaterialBuffers);
}
TEST(BuiltinPassLayout_Test, AcceptsRuntimeMaterialTextureBindingsWithoutBuiltinSemanticMetadata) {
Array<ShaderResourceBindingDesc> bindings;
ShaderResourceBindingDesc perObjectBinding = {};
perObjectBinding.name = "PerObjectConstants";
perObjectBinding.type = ShaderResourceType::ConstantBuffer;
perObjectBinding.set = 0u;
perObjectBinding.binding = 0u;
perObjectBinding.semantic = "PerObject";
bindings.PushBack(perObjectBinding);
ShaderResourceBindingDesc materialBinding = {};
materialBinding.name = "MaterialConstants";
materialBinding.type = ShaderResourceType::ConstantBuffer;
materialBinding.set = 1u;
materialBinding.binding = 0u;
materialBinding.semantic = "Material";
bindings.PushBack(materialBinding);
ShaderResourceBindingDesc textureBinding = {};
textureBinding.name = "_LightMap";
textureBinding.type = ShaderResourceType::Texture2D;
textureBinding.set = 4u;
textureBinding.binding = 1u;
bindings.PushBack(textureBinding);
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.usesMaterialTextures);
ASSERT_EQ(plan.materialTextureBindings.Size(), 1u);
EXPECT_EQ(plan.materialTextureBindings[0].name, "_LightMap");
EXPECT_EQ(plan.materialTextureBindings[0].semantic, BuiltinPassResourceSemantic::MaterialTexture);
EXPECT_EQ(plan.materialTextureBindings[0].resourceType, ShaderResourceType::Texture2D);
EXPECT_EQ(plan.materialTextureBindings[0].location.set, 4u);
EXPECT_EQ(plan.materialTextureBindings[0].location.binding, 1u);
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
ASSERT_EQ(setLayouts.size(), 5u);
EXPECT_TRUE(setLayouts[4].usesTexture);
EXPECT_TRUE(setLayouts[4].usesMaterialTextures);
ASSERT_EQ(setLayouts[4].materialTextureBindings.size(), 1u);
EXPECT_EQ(setLayouts[4].materialTextureBindings[0].name, "_LightMap");
ASSERT_EQ(setLayouts[4].bindings.size(), 1u);
EXPECT_EQ(
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
DescriptorType::SRV);
EXPECT_EQ(setLayouts[4].bindings[0].resourceDimension, ResourceViewDimension::Texture2D);
}
TEST(BuiltinDepthOnlyPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
const InputLayoutDesc inputLayout = BuiltinDepthOnlyPass::BuildInputLayout();

View File

@@ -2,6 +2,8 @@
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/RHI/RHITypes.h>
#include "Rendering/Internal/ShaderVariantUtils.h"
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
@@ -77,6 +79,69 @@ TEST(Shader, AddGetAttributes) {
EXPECT_EQ(attributes[0].name, "aPosition");
}
TEST(Shader, StoresBackendCompiledBinariesPerBackend) {
ShaderStageVariant variant = {};
variant.stage = ShaderType::Fragment;
variant.language = ShaderLanguage::HLSL;
variant.backend = ShaderBackend::Generic;
Array<XCEngine::Core::uint8> d3d12Payload;
d3d12Payload.PushBack(0x01);
d3d12Payload.PushBack(0x02);
variant.SetCompiledBinaryForBackend(ShaderBackend::D3D12, d3d12Payload);
Array<XCEngine::Core::uint8> vulkanPayload;
vulkanPayload.PushBack(0x03);
vulkanPayload.PushBack(0x04);
vulkanPayload.PushBack(0x05);
variant.SetCompiledBinaryForBackend(ShaderBackend::Vulkan, vulkanPayload);
const Array<XCEngine::Core::uint8>* d3d12Binary =
variant.GetCompiledBinaryForBackend(ShaderBackend::D3D12);
ASSERT_NE(d3d12Binary, nullptr);
ASSERT_EQ(d3d12Binary->Size(), 2u);
EXPECT_EQ((*d3d12Binary)[1], 0x02);
const Array<XCEngine::Core::uint8>* vulkanBinary =
variant.GetCompiledBinaryForBackend(ShaderBackend::Vulkan);
ASSERT_NE(vulkanBinary, nullptr);
ASSERT_EQ(vulkanBinary->Size(), 3u);
EXPECT_EQ((*vulkanBinary)[2], 0x05);
EXPECT_EQ(variant.GetCompiledBinaryForBackend(ShaderBackend::OpenGL), nullptr);
}
TEST(Shader, ApplyShaderStageVariantCarriesMatchedBackendCompiledBinary) {
ShaderPass pass = {};
pass.name = "ForwardLit";
ShaderStageVariant variant = {};
variant.stage = ShaderType::Fragment;
variant.language = ShaderLanguage::HLSL;
variant.backend = ShaderBackend::Generic;
variant.entryPoint = "MainPS";
variant.profile = "ps_5_1";
variant.sourceCode = "float4 MainPS() : SV_TARGET { return 1; }";
Array<XCEngine::Core::uint8> vulkanPayload;
vulkanPayload.PushBack(0x07);
vulkanPayload.PushBack(0x08);
vulkanPayload.PushBack(0x09);
variant.SetCompiledBinaryForBackend(ShaderBackend::Vulkan, vulkanPayload);
XCEngine::RHI::ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
pass,
ShaderBackend::Vulkan,
variant,
compileDesc);
EXPECT_EQ(compileDesc.compiledBinaryBackend, XCEngine::RHI::ShaderBinaryBackend::Vulkan);
ASSERT_EQ(compileDesc.compiledBinary.size(), 3u);
EXPECT_EQ(compileDesc.compiledBinary[0], 0x07);
EXPECT_EQ(compileDesc.compiledBinary[2], 0x09);
}
TEST(Shader, FindsBackendSpecificVariantAndFallsBackToGeneric) {
Shader shader;