Add gaussian splat integration baseline

This commit is contained in:
2026-04-11 05:37:31 +08:00
parent 3622bf3aa2
commit 39632e1a04
11 changed files with 6813 additions and 175 deletions

View File

@@ -1,12 +1,122 @@
Shader "Builtin Gaussian Splat Utilities"
{
HLSLINCLUDE
struct GaussianSplatOtherData
{
float4 rotation;
float4 scaleReserved;
};
struct GaussianSplatViewData
{
float4 clipCenter;
float4 ellipseAxisU;
float4 ellipseAxisV;
float4 colorOpacity;
};
uint FloatToSortableUint(float value)
{
const uint rawValue = asuint(value);
const uint mask = (rawValue & 0x80000000u) != 0u ? 0xffffffffu : 0x80000000u;
return rawValue ^ mask;
}
float3x3 CalcMatrixFromRotationScale(float4 rotation, float3 scale)
{
const float x = rotation.x;
const float y = rotation.y;
const float z = rotation.z;
const float w = rotation.w;
const float3x3 rotationMatrix = float3x3(
1.0 - 2.0 * (y * y + z * z), 2.0 * (x * y - w * z), 2.0 * (x * z + w * y),
2.0 * (x * y + w * z), 1.0 - 2.0 * (x * x + z * z), 2.0 * (y * z - w * x),
2.0 * (x * z - w * y), 2.0 * (y * z + w * x), 1.0 - 2.0 * (x * x + y * y));
const float3x3 scaleMatrix = float3x3(
scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.z);
return mul(rotationMatrix, scaleMatrix);
}
void CalcCovariance3D(float3x3 rotationScaleMatrix, out float3 sigma0, out float3 sigma1)
{
const float3x3 sigma = mul(rotationScaleMatrix, transpose(rotationScaleMatrix));
sigma0 = float3(sigma._m00, sigma._m01, sigma._m02);
sigma1 = float3(sigma._m11, sigma._m12, sigma._m22);
}
float3 CalcCovariance2D(
float3 viewPosition,
float3 covariance3D0,
float3 covariance3D1,
float4x4 viewMatrix,
float4x4 projectionMatrix,
float2 screenSize)
{
if (abs(viewPosition.z) <= 1.0e-5)
{
return float3(0.3, 0.0, 0.3);
}
const float aspect = projectionMatrix[0][0] / projectionMatrix[1][1];
const float tanFovX = rcp(projectionMatrix[0][0]);
const float tanFovY = rcp(projectionMatrix[1][1] * aspect);
const float limitX = 1.3 * tanFovX;
const float limitY = 1.3 * tanFovY;
float3 clampedViewPosition = viewPosition;
clampedViewPosition.x =
clamp(clampedViewPosition.x / clampedViewPosition.z, -limitX, limitX) * clampedViewPosition.z;
clampedViewPosition.y =
clamp(clampedViewPosition.y / clampedViewPosition.z, -limitY, limitY) * clampedViewPosition.z;
const float focalLength = screenSize.x * projectionMatrix[0][0] * 0.5;
const float3x3 jacobian = float3x3(
focalLength / clampedViewPosition.z,
0.0,
-(focalLength * clampedViewPosition.x) / (clampedViewPosition.z * clampedViewPosition.z),
0.0,
focalLength / clampedViewPosition.z,
-(focalLength * clampedViewPosition.y) / (clampedViewPosition.z * clampedViewPosition.z),
0.0,
0.0,
0.0);
const float3x3 worldToView = (float3x3)viewMatrix;
const float3x3 transform = mul(jacobian, worldToView);
const float3x3 covariance3D = float3x3(
covariance3D0.x, covariance3D0.y, covariance3D0.z,
covariance3D0.y, covariance3D1.x, covariance3D1.y,
covariance3D0.z, covariance3D1.y, covariance3D1.z);
float3x3 covariance2D = mul(transform, mul(covariance3D, transpose(transform)));
covariance2D._m00 += 0.3;
covariance2D._m11 += 0.3;
return float3(covariance2D._m00, covariance2D._m01, covariance2D._m11);
}
void DecomposeCovariance(float3 covariance2D, out float2 axisU, out float2 axisV)
{
const float diagonal0 = covariance2D.x;
const float diagonal1 = covariance2D.z;
const float offDiagonal = covariance2D.y;
const float mid = 0.5 * (diagonal0 + diagonal1);
const float radius = length(float2((diagonal0 - diagonal1) * 0.5, offDiagonal));
const float lambda0 = max(mid + radius, 0.1);
const float lambda1 = max(mid - radius, 0.1);
float2 basis = normalize(float2(offDiagonal, lambda0 - diagonal0));
if (all(abs(basis) < 1.0e-5))
{
basis = float2(1.0, 0.0);
}
basis.y = -basis.y;
const float maxAxisLength = 4096.0;
axisU = min(sqrt(2.0 * lambda0), maxAxisLength) * basis;
axisV = min(sqrt(2.0 * lambda1), maxAxisLength) * float2(basis.y, -basis.x);
}
ENDHLSL
SubShader
@@ -25,18 +135,21 @@ Shader "Builtin Gaussian Splat Utilities"
float4x4 gModelMatrix;
float4 gCameraRight;
float4 gCameraUp;
float4 gScreenParams;
float4 gSplatParams;
};
StructuredBuffer<float3> GaussianSplatPositions;
StructuredBuffer<float4> GaussianSplatPositions;
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
StructuredBuffer<float4> GaussianSplatColor;
RWStructuredBuffer<uint> GaussianSplatSortDistances;
RWStructuredBuffer<uint> GaussianSplatOrderBuffer;
RWStructuredBuffer<GaussianSplatViewData> GaussianSplatViewDataBuffer;
[numthreads(64, 1, 1)]
void GaussianSplatPrepareOrderCS(uint3 dispatchThreadId : SV_DispatchThreadID)
{
uint splatCount = 0u;
GaussianSplatOrderBuffer.GetDimensions(splatCount);
const uint splatCount = (uint)gSplatParams.x;
const uint index = dispatchThreadId.x;
if (index >= splatCount)
{
@@ -45,10 +158,46 @@ Shader "Builtin Gaussian Splat Utilities"
GaussianSplatOrderBuffer[index] = index;
const float3 localCenter = GaussianSplatPositions[index];
const float3 viewCenter =
mul(gViewMatrix, mul(gModelMatrix, float4(localCenter, 1.0))).xyz;
GaussianSplatViewData viewData = (GaussianSplatViewData)0;
const float3 localCenter = GaussianSplatPositions[index].xyz;
const GaussianSplatOtherData otherData = GaussianSplatOther[index];
const float4 colorOpacity = GaussianSplatColor[index];
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
const float3 viewCenter = mul(gViewMatrix, float4(worldCenter, 1.0)).xyz;
GaussianSplatSortDistances[index] = FloatToSortableUint(viewCenter.z);
const float4 clipCenter = mul(gProjectionMatrix, float4(viewCenter, 1.0));
if (clipCenter.w > 0.0)
{
const float3x3 modelLinear = (float3x3)gModelMatrix;
const float3x3 rotationScaleMatrix =
CalcMatrixFromRotationScale(otherData.rotation, otherData.scaleReserved.xyz);
const float3x3 worldRotationScale = mul(modelLinear, rotationScaleMatrix);
float3 covariance3D0 = 0.0;
float3 covariance3D1 = 0.0;
CalcCovariance3D(worldRotationScale, covariance3D0, covariance3D1);
const float3 covariance2D = CalcCovariance2D(
viewCenter,
covariance3D0,
covariance3D1,
gViewMatrix,
gProjectionMatrix,
gScreenParams.xy);
float2 axisU = 0.0;
float2 axisV = 0.0;
DecomposeCovariance(covariance2D, axisU, axisV);
viewData.clipCenter = clipCenter;
viewData.ellipseAxisU = float4(axisU, 0.0, 0.0);
viewData.ellipseAxisV = float4(axisV, 0.0, 0.0);
viewData.colorOpacity = colorOpacity;
}
GaussianSplatViewDataBuffer[index] = viewData;
}
ENDHLSL
}

View File

@@ -13,23 +13,26 @@ Shader "Builtin Gaussian Splat"
float4x4 gModelMatrix;
float4 gCameraRight;
float4 gCameraUp;
float4 gScreenParams;
float4 gSplatParams;
};
cbuffer MaterialConstants
{
float4 gSplatParams;
float4 gPointScaleParams;
float4 gOpacityScaleParams;
};
struct GaussianSplatOtherData
struct GaussianSplatViewData
{
float4 rotation;
float4 scaleReserved;
float4 clipCenter;
float4 ellipseAxisU;
float4 ellipseAxisV;
float4 colorOpacity;
};
StructuredBuffer<uint> GaussianSplatOrderBuffer;
StructuredBuffer<float3> GaussianSplatPositions;
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
StructuredBuffer<float4> GaussianSplatColor;
StructuredBuffer<GaussianSplatViewData> GaussianSplatViewDataBuffer;
struct VSOutput
{
@@ -53,39 +56,35 @@ Shader "Builtin Gaussian Splat"
VSOutput MainVS(uint vertexId : SV_VertexID, uint instanceId : SV_InstanceID)
{
VSOutput output;
const float2 corner = ResolveQuadCorner(vertexId);
VSOutput output = (VSOutput)0;
const uint splatIndex = GaussianSplatOrderBuffer[instanceId];
const float3 localCenter = GaussianSplatPositions[splatIndex];
const GaussianSplatOtherData otherData = GaussianSplatOther[splatIndex];
const float4 colorOpacity = GaussianSplatColor[splatIndex];
const GaussianSplatViewData viewData = GaussianSplatViewDataBuffer[splatIndex];
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
const float maxAxisScale =
max(max(otherData.scaleReserved.x, otherData.scaleReserved.y), otherData.scaleReserved.z);
const float radius = max(maxAxisScale * gSplatParams.x, 0.0001);
const float3 worldPosition =
worldCenter +
gCameraRight.xyz * (corner.x * radius) +
gCameraUp.xyz * (corner.y * radius);
if (viewData.clipCenter.w <= 0.0)
{
const float nanValue = asfloat(0x7fc00000u);
output.position = float4(nanValue, nanValue, nanValue, nanValue);
return output;
}
output.position = mul(gProjectionMatrix, mul(gViewMatrix, float4(worldPosition, 1.0)));
output.localUv = corner;
output.colorOpacity = colorOpacity;
const float2 quadPos = ResolveQuadCorner(vertexId) * 2.0;
const float2 ellipseOffsetPixels =
(quadPos.x * viewData.ellipseAxisU.xy + quadPos.y * viewData.ellipseAxisV.xy) * gPointScaleParams.x;
const float2 clipOffset =
ellipseOffsetPixels * (2.0 / max(gScreenParams.xy, float2(1.0, 1.0))) * viewData.clipCenter.w;
output.position = viewData.clipCenter;
output.position.xy += clipOffset;
output.localUv = quadPos;
output.colorOpacity = viewData.colorOpacity;
return output;
}
float4 MainPS(VSOutput input) : SV_TARGET
{
const float radiusSq = dot(input.localUv, input.localUv);
if (radiusSq > 1.0)
{
discard;
}
const float gaussianFalloff = exp(-radiusSq * 2.5);
const float alpha = saturate(input.colorOpacity.a * gSplatParams.y * gaussianFalloff);
if (alpha <= 0.001)
const float alpha =
saturate(exp(-dot(input.localUv, input.localUv)) * input.colorOpacity.a * gOpacityScaleParams.x);
if (alpha <= (1.0 / 255.0))
{
discard;
}

View File

@@ -59,6 +59,8 @@ private:
Math::Matrix4x4 model = Math::Matrix4x4::Identity();
Math::Vector4 cameraRight = Math::Vector4::Zero();
Math::Vector4 cameraUp = Math::Vector4::Zero();
Math::Vector4 screenParams = Math::Vector4::Zero();
Math::Vector4 splatParams = Math::Vector4::Zero();
};
struct OwnedDescriptorSet {

View File

@@ -52,6 +52,12 @@ std::vector<const DescriptorSetLayoutBinding*> GatherBindingsOfTypeSorted(
return bindings;
}
bool UsesOpenGLStorageBufferBinding(const DescriptorSetLayoutBinding& binding) {
return binding.resourceDimension == ResourceViewDimension::Buffer ||
binding.resourceDimension == ResourceViewDimension::StructuredBuffer ||
binding.resourceDimension == ResourceViewDimension::RawBuffer;
}
} // namespace
bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
@@ -93,6 +99,7 @@ bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
uint32_t nextCBVBindingPoint = 0;
uint32_t nextSRVBindingPoint = 0;
uint32_t nextStorageBufferBindingPoint = 0;
uint32_t nextUAVBindingPoint = 0;
uint32_t nextSamplerBindingPoint = 0;
@@ -100,34 +107,42 @@ bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex];
SetBindingPointMapping& mapping = m_setBindingPointMappings[setIndex];
const auto appendBindings =
[&setLayout](
DescriptorType type,
std::unordered_map<uint32_t, uint32_t>& bindingPoints,
uint32_t& nextBindingPoint) {
const auto bindings = GatherBindingsOfTypeSorted(setLayout, type);
for (const DescriptorSetLayoutBinding* binding : bindings) {
bindingPoints[binding->binding] = nextBindingPoint;
nextBindingPoint += binding->count > 0 ? binding->count : 1u;
}
};
const auto appendBindings = [&setLayout, &nextStorageBufferBindingPoint](
DescriptorType type,
std::unordered_map<uint32_t, uint32_t>& bindingPoints,
uint32_t& nextBindingPoint,
bool useStorageBufferNamespace) {
const auto bindings = GatherBindingsOfTypeSorted(setLayout, type);
for (const DescriptorSetLayoutBinding* binding : bindings) {
uint32_t& resolvedNextBindingPoint =
useStorageBufferNamespace && UsesOpenGLStorageBufferBinding(*binding)
? nextStorageBufferBindingPoint
: nextBindingPoint;
bindingPoints[binding->binding] = resolvedNextBindingPoint;
resolvedNextBindingPoint += binding->count > 0 ? binding->count : 1u;
}
};
appendBindings(
DescriptorType::CBV,
mapping.constantBufferBindingPoints,
nextCBVBindingPoint);
nextCBVBindingPoint,
false);
appendBindings(
DescriptorType::SRV,
mapping.shaderResourceBindingPoints,
nextSRVBindingPoint);
nextSRVBindingPoint,
true);
appendBindings(
DescriptorType::UAV,
mapping.unorderedAccessBindingPoints,
nextUAVBindingPoint);
nextUAVBindingPoint,
true);
appendBindings(
DescriptorType::Sampler,
mapping.samplerBindingPoints,
nextSamplerBindingPoint);
nextSamplerBindingPoint,
false);
}
}

View File

@@ -268,6 +268,7 @@ inline bool TryBuildRuntimeShaderBindings(
Core::uint32 nextTextureRegister = 0;
Core::uint32 nextSamplerRegister = 0;
Core::uint32 nextUnorderedAccessRegister = 0;
Core::uint32 nextStorageBufferRegister = 0;
for (Resources::ShaderResourceBindingDesc& binding : outBindings) {
binding.set = 0;
switch (binding.type) {
@@ -276,15 +277,25 @@ inline bool TryBuildRuntimeShaderBindings(
break;
case Resources::ShaderResourceType::Texture2D:
case Resources::ShaderResourceType::TextureCube:
binding.binding = nextTextureRegister++;
break;
case Resources::ShaderResourceType::StructuredBuffer:
case Resources::ShaderResourceType::RawBuffer:
binding.binding = nextTextureRegister++;
binding.binding =
backend == Resources::ShaderBackend::OpenGL
? nextStorageBufferRegister++
: nextTextureRegister++;
break;
case Resources::ShaderResourceType::Sampler:
binding.binding = nextSamplerRegister++;
break;
case Resources::ShaderResourceType::RWStructuredBuffer:
case Resources::ShaderResourceType::RWRawBuffer:
binding.binding =
backend == Resources::ShaderBackend::OpenGL
? nextStorageBufferRegister++
: nextUnorderedAccessRegister++;
break;
default:
binding.binding = nextUnorderedAccessRegister++;
break;

View File

@@ -205,6 +205,9 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
const RenderContext& context,
const RenderSceneData& sceneData) {
if (!EnsureInitialized(context)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass::PrepareGaussianSplatResources failed: EnsureInitialized returned false");
return false;
}
@@ -220,10 +223,16 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
cachedGaussianSplat->other.shaderResourceView == nullptr ||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass::PrepareGaussianSplatResources failed: gaussian splat GPU cache incomplete");
return false;
}
if (m_passResources == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass::PrepareGaussianSplatResources failed: pass resources missing");
return false;
}
@@ -232,7 +241,12 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
workingSet == nullptr ||
workingSet->sortDistances.unorderedAccessView == nullptr ||
workingSet->orderIndices.shaderResourceView == nullptr ||
workingSet->orderIndices.unorderedAccessView == nullptr) {
workingSet->orderIndices.unorderedAccessView == nullptr ||
workingSet->viewData.shaderResourceView == nullptr ||
workingSet->viewData.unorderedAccessView == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass::PrepareGaussianSplatResources failed: working-set allocation incomplete");
return false;
}
}
@@ -527,18 +541,19 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
}
if (!passLayout.gaussianSplatOrderBuffer.IsValid() ||
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
!passLayout.gaussianSplatColorBuffer.IsValid()) {
!passLayout.gaussianSplatViewDataBuffer.IsValid()) {
return failLayout(
"BuiltinGaussianSplatPass draw pass requires order, position, other, and color gaussian splat buffer bindings");
"BuiltinGaussianSplatPass draw pass requires order and view-data gaussian splat buffer bindings");
}
} else if (usage == PassLayoutUsage::PrepareOrder) {
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
!passLayout.gaussianSplatOrderBuffer.IsValid() ||
!passLayout.gaussianSplatPositionBuffer.IsValid()) {
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
!passLayout.gaussianSplatColorBuffer.IsValid() ||
!passLayout.gaussianSplatViewDataBuffer.IsValid()) {
return failLayout(
"BuiltinGaussianSplatPass prepare-order pass requires sort distance, order, and position gaussian splat buffer bindings");
"BuiltinGaussianSplatPass prepare-order pass requires sort distance, order, position, other, color, and view-data gaussian splat buffer bindings");
}
}
@@ -660,6 +675,10 @@ RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreateComputePipelineState
sceneData.globalShaderKeywords);
RHI::RHIPipelineState* pipelineState = context.device->CreateComputePipelineState(pipelineDesc);
if (pipelineState == nullptr || !pipelineState->IsValid()) {
const Containers::String error =
Containers::String("BuiltinGaussianSplatPass failed to create compute pipeline state for pass '") +
resolvedShaderPass.passName + "'";
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, error.CStr());
if (pipelineState != nullptr) {
pipelineState->Shutdown();
delete pipelineState;
@@ -718,6 +737,9 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass failed to allocate descriptor set");
return nullptr;
}
}
@@ -921,17 +943,22 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
const RenderContext& context,
const RenderSceneData& sceneData,
const VisibleGaussianSplatItem& visibleGaussianSplat) {
auto fail = [](const char* message) -> bool {
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
return false;
};
if (visibleGaussianSplat.gameObject == nullptr ||
visibleGaussianSplat.gaussianSplat == nullptr ||
!visibleGaussianSplat.gaussianSplat->IsValid() ||
m_passResources == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: invalid visible gaussian splat item");
}
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
if (cachedGaussianSplat == nullptr || cachedGaussianSplat->positions.shaderResourceView == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: gaussian splat GPU cache is incomplete");
}
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
@@ -939,13 +966,15 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
workingSet == nullptr ||
workingSet->sortDistances.unorderedAccessView == nullptr ||
workingSet->orderIndices.unorderedAccessView == nullptr ||
workingSet->orderIndices.shaderResourceView == nullptr) {
return false;
workingSet->orderIndices.shaderResourceView == nullptr ||
workingSet->viewData.unorderedAccessView == nullptr ||
workingSet->viewData.shaderResourceView == nullptr) {
return fail("BuiltinGaussianSplatPass prepare-order failed: working set allocation is incomplete");
}
const ResolvedShaderPass resolvedShaderPass = ResolvePrepareOrderShaderPass(sceneData);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: utilities shader pass was not resolved");
}
PassLayoutKey passLayoutKey = {};
@@ -958,7 +987,7 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
PassLayoutUsage::PrepareOrder);
RHI::RHIPipelineState* pipelineState = GetOrCreateComputePipelineState(context, sceneData);
if (passLayout == nullptr || pipelineState == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: compute pipeline setup was not created");
}
RHI::RHICommandList* commandList = context.commandList;
@@ -979,6 +1008,14 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
workingSet->orderIndices.currentState = RHI::ResourceStates::UnorderedAccess;
}
if (workingSet->viewData.currentState != RHI::ResourceStates::UnorderedAccess) {
commandList->TransitionBarrier(
workingSet->viewData.unorderedAccessView,
workingSet->viewData.currentState,
RHI::ResourceStates::UnorderedAccess);
workingSet->viewData.currentState = RHI::ResourceStates::UnorderedAccess;
}
commandList->SetPipelineState(pipelineState);
const PerObjectConstants perObjectConstants = {
@@ -986,7 +1023,13 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
sceneData.cameraData.view,
visibleGaussianSplat.localToWorld.Transpose(),
Math::Vector4(sceneData.cameraData.worldRight, 0.0f),
Math::Vector4(sceneData.cameraData.worldUp, 0.0f)
Math::Vector4(sceneData.cameraData.worldUp, 0.0f),
Math::Vector4(
static_cast<float>(sceneData.cameraData.viewportWidth),
static_cast<float>(sceneData.cameraData.viewportHeight),
sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast<float>(sceneData.cameraData.viewportWidth) : 0.0f,
sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast<float>(sceneData.cameraData.viewportHeight) : 0.0f),
Math::Vector4(static_cast<float>(cachedGaussianSplat->splatCount), 0.0f, 0.0f, 0.0f)
};
if (passLayout->descriptorSetCount > 0u) {
@@ -994,7 +1037,7 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
for (Core::uint32 descriptorOffset = 0u; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
if (setIndex >= passLayout->setLayouts.size()) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: descriptor set index overflow");
}
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
@@ -1005,8 +1048,11 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
if (!(setLayout.usesPerObject ||
setLayout.usesGaussianSplatSortDistanceBuffer ||
setLayout.usesGaussianSplatOrderBuffer ||
setLayout.usesGaussianSplatPositionBuffer)) {
return false;
setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatViewDataBuffer)) {
return fail("BuiltinGaussianSplatPass prepare-order failed: unexpected descriptor set layout");
}
const Core::uint64 objectId =
@@ -1014,7 +1060,10 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
const Resources::GaussianSplat* gaussianSplatKey =
(setLayout.usesGaussianSplatSortDistanceBuffer ||
setLayout.usesGaussianSplatOrderBuffer ||
setLayout.usesGaussianSplatPositionBuffer)
setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatViewDataBuffer)
? visibleGaussianSplat.gaussianSplat
: nullptr;
@@ -1033,13 +1082,13 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
workingSet->orderIndices.unorderedAccessView,
workingSet->viewData.unorderedAccessView);
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: dynamic descriptor set resolution failed");
}
RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set;
if (setLayout.usesPerObject) {
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
return false;
return fail("BuiltinGaussianSplatPass prepare-order failed: per-object binding is invalid");
}
descriptorSet->WriteConstant(
@@ -1070,6 +1119,11 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
RHI::ResourceStates::UnorderedAccess,
RHI::ResourceStates::NonPixelShaderResource);
workingSet->orderIndices.currentState = RHI::ResourceStates::NonPixelShaderResource;
commandList->TransitionBarrier(
workingSet->viewData.shaderResourceView,
RHI::ResourceStates::UnorderedAccess,
RHI::ResourceStates::NonPixelShaderResource);
workingSet->viewData.currentState = RHI::ResourceStates::NonPixelShaderResource;
return true;
}
@@ -1078,10 +1132,15 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
const RenderSurface& surface,
const RenderSceneData& sceneData,
const VisibleGaussianSplatItem& visibleGaussianSplat) {
auto fail = [](const char* message) -> bool {
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
return false;
};
if (visibleGaussianSplat.gameObject == nullptr ||
visibleGaussianSplat.gaussianSplat == nullptr ||
!visibleGaussianSplat.gaussianSplat->IsValid()) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: invalid visible gaussian splat item");
}
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
@@ -1090,28 +1149,29 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
cachedGaussianSplat->other.shaderResourceView == nullptr ||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: gaussian splat GPU cache is incomplete");
}
if (m_passResources == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: pass resources were not initialized");
}
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
workingSet == nullptr ||
workingSet->orderIndices.shaderResourceView == nullptr) {
return false;
workingSet->orderIndices.shaderResourceView == nullptr ||
workingSet->viewData.shaderResourceView == nullptr) {
return fail("BuiltinGaussianSplatPass draw failed: working set allocation is incomplete");
}
const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat);
if (material == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: gaussian splat material could not be resolved");
}
const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: draw shader pass was not resolved");
}
PassLayoutKey passLayoutKey = {};
@@ -1124,12 +1184,12 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
PassLayoutUsage::Draw);
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
if (passLayout == nullptr || pipelineState == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: graphics pipeline setup was not created");
}
const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
if (!materialConstants.IsValid()) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: material constant payload is invalid");
}
RHI::RHICommandList* commandList = context.commandList;
@@ -1140,7 +1200,13 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
sceneData.cameraData.view,
visibleGaussianSplat.localToWorld.Transpose(),
Math::Vector4(sceneData.cameraData.worldRight, 0.0f),
Math::Vector4(sceneData.cameraData.worldUp, 0.0f)
Math::Vector4(sceneData.cameraData.worldUp, 0.0f),
Math::Vector4(
static_cast<float>(sceneData.cameraData.viewportWidth),
static_cast<float>(sceneData.cameraData.viewportHeight),
sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast<float>(sceneData.cameraData.viewportWidth) : 0.0f,
sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast<float>(sceneData.cameraData.viewportHeight) : 0.0f),
Math::Vector4(static_cast<float>(cachedGaussianSplat->splatCount), 0.0f, 0.0f, 0.0f)
};
if (passLayout->descriptorSetCount > 0u) {
@@ -1148,7 +1214,7 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
for (Core::uint32 descriptorOffset = 0u; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
if (setIndex >= passLayout->setLayouts.size()) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: descriptor set index overflow");
}
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
@@ -1159,11 +1225,8 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
if (!(setLayout.usesPerObject ||
setLayout.usesMaterial ||
setLayout.usesGaussianSplatOrderBuffer ||
setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatSHBuffer)) {
return false;
setLayout.usesGaussianSplatViewDataBuffer)) {
return fail("BuiltinGaussianSplatPass draw failed: unexpected descriptor set layout");
}
const Core::uint64 objectId =
@@ -1172,10 +1235,7 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
setLayout.usesMaterial ? material : nullptr;
const Resources::GaussianSplat* gaussianSplatKey =
(setLayout.usesGaussianSplatOrderBuffer ||
setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatSHBuffer)
setLayout.usesGaussianSplatViewDataBuffer)
? visibleGaussianSplat.gaussianSplat
: nullptr;
@@ -1194,13 +1254,13 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
workingSet->orderIndices.shaderResourceView,
workingSet->viewData.shaderResourceView);
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: dynamic descriptor set resolution failed");
}
RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set;
if (setLayout.usesPerObject) {
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
return false;
return fail("BuiltinGaussianSplatPass draw failed: per-object binding is invalid");
}
descriptorSet->WriteConstant(