Add gaussian splat compute shader contracts
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
|
||||
using namespace XCEngine::Resources;
|
||||
@@ -71,6 +72,7 @@ TEST(ShaderLoader, CanLoad) {
|
||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdOutlineShaderPath()));
|
||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionMaskShaderPath()));
|
||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionOutlineShaderPath()));
|
||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinGaussianSplatUtilitiesShaderPath()));
|
||||
EXPECT_FALSE(loader.CanLoad("test.vert"));
|
||||
EXPECT_FALSE(loader.CanLoad("test.frag"));
|
||||
EXPECT_FALSE(loader.CanLoad("test.glsl"));
|
||||
@@ -603,6 +605,100 @@ TEST(ShaderLoader, LoadShaderAuthoringBuildsComputeOnlyPassVariant) {
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringBuildsComputeOnlyPassConstantBufferBindings) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_compute_constants";
|
||||
const fs::path shaderPath = shaderRoot / "compute_constants.shader";
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
fs::create_directories(shaderRoot);
|
||||
|
||||
WriteTextFile(
|
||||
shaderPath,
|
||||
R"(Shader "ComputeConstantsShader"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "Prepare"
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma compute PrepareCS
|
||||
cbuffer PerObjectConstants
|
||||
{
|
||||
float4x4 ObjectToWorld;
|
||||
};
|
||||
StructuredBuffer<float4> InputData;
|
||||
RWStructuredBuffer<uint> OutputOrder;
|
||||
[numthreads(64, 1, 1)]
|
||||
void PrepareCS(uint3 dispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
OutputOrder[dispatchThreadId.x] =
|
||||
(uint)mul(ObjectToWorld, InputData[dispatchThreadId.x]).x;
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
ShaderLoader loader;
|
||||
LoadResult result = loader.Load(shaderPath.string().c_str());
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
Shader* shader = static_cast<Shader*>(result.resource);
|
||||
ASSERT_NE(shader, nullptr);
|
||||
|
||||
const ShaderPass* pass = shader->FindPass("Prepare");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
EXPECT_EQ(pass->resources.Size(), 3u);
|
||||
|
||||
const ShaderResourceBindingDesc* perObject =
|
||||
shader->FindPassResourceBinding("Prepare", "PerObjectConstants");
|
||||
ASSERT_NE(perObject, nullptr);
|
||||
EXPECT_EQ(perObject->type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(perObject->set, 0u);
|
||||
EXPECT_EQ(perObject->binding, 0u);
|
||||
|
||||
const ShaderStageVariant* computeVariant =
|
||||
shader->FindVariant("Prepare", ShaderType::Compute, ShaderBackend::D3D12);
|
||||
ASSERT_NE(computeVariant, nullptr);
|
||||
|
||||
XCEngine::RHI::ShaderCompileDesc d3d12CompileDesc = {};
|
||||
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||
*pass,
|
||||
ShaderBackend::D3D12,
|
||||
*computeVariant,
|
||||
d3d12CompileDesc);
|
||||
const std::string d3d12Source(
|
||||
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
|
||||
d3d12CompileDesc.source.size());
|
||||
EXPECT_TRUE(std::regex_search(
|
||||
d3d12Source,
|
||||
std::regex(R"(cbuffer\s+PerObjectConstants\s*:\s*register\(b0\))", std::regex::ECMAScript)));
|
||||
|
||||
XCEngine::RHI::ShaderCompileDesc vulkanCompileDesc = {};
|
||||
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||
*pass,
|
||||
ShaderBackend::Vulkan,
|
||||
*computeVariant,
|
||||
vulkanCompileDesc);
|
||||
const std::string vulkanSource(
|
||||
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
|
||||
vulkanCompileDesc.source.size());
|
||||
EXPECT_TRUE(std::regex_search(
|
||||
vulkanSource,
|
||||
std::regex(
|
||||
R"(cbuffer\s+PerObjectConstants\s*:\s*register\(b0,\s*space0\))",
|
||||
std::regex::ECMAScript)));
|
||||
|
||||
delete shader;
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringRejectsPassMixingComputeAndGraphicsPragmas) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user