Precompute gaussian splat chunk visibility
This commit is contained in:
@@ -302,6 +302,60 @@ Shader "Builtin Gaussian Splat Utilities"
|
|||||||
|
|
||||||
SubShader
|
SubShader
|
||||||
{
|
{
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "GaussianSplatMarkVisibleChunks"
|
||||||
|
HLSLPROGRAM
|
||||||
|
#pragma target 4.5
|
||||||
|
#pragma compute GaussianSplatMarkVisibleChunksCS
|
||||||
|
|
||||||
|
cbuffer PerObjectConstants
|
||||||
|
{
|
||||||
|
float4x4 gProjectionMatrix;
|
||||||
|
float4x4 gViewMatrix;
|
||||||
|
float4x4 gModelMatrix;
|
||||||
|
float4x4 gWorldToObjectMatrix;
|
||||||
|
float4 gCameraRight;
|
||||||
|
float4 gCameraUp;
|
||||||
|
float4 gCameraWorldPos;
|
||||||
|
float4 gScreenParams;
|
||||||
|
float4 gSplatParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
StructuredBuffer<GaussianSplatChunkData> GaussianSplatChunks;
|
||||||
|
RWStructuredBuffer<uint> GaussianSplatVisibleChunks;
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void GaussianSplatMarkVisibleChunksCS(uint3 dispatchThreadId : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
const uint chunkCount = (uint)gSplatParams.w;
|
||||||
|
const uint chunkIndex = dispatchThreadId.x;
|
||||||
|
if (chunkIndex >= chunkCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GaussianSplatChunkData chunkData = GaussianSplatChunks[chunkIndex];
|
||||||
|
uint visibleFlag = 1u;
|
||||||
|
if (chunkData.posX.x > chunkData.posX.y ||
|
||||||
|
chunkData.posY.x > chunkData.posY.y ||
|
||||||
|
chunkData.posZ.x > chunkData.posZ.y ||
|
||||||
|
IsChunkDefinitelyOutsideFrustum(
|
||||||
|
chunkData,
|
||||||
|
chunkCount,
|
||||||
|
chunkIndex,
|
||||||
|
gModelMatrix,
|
||||||
|
gViewMatrix,
|
||||||
|
gProjectionMatrix))
|
||||||
|
{
|
||||||
|
visibleFlag = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
GaussianSplatVisibleChunks[chunkIndex] = visibleFlag;
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
}
|
||||||
|
|
||||||
Pass
|
Pass
|
||||||
{
|
{
|
||||||
Name "GaussianSplatPrepareOrder"
|
Name "GaussianSplatPrepareOrder"
|
||||||
@@ -326,7 +380,7 @@ Shader "Builtin Gaussian Splat Utilities"
|
|||||||
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
||||||
StructuredBuffer<float4> GaussianSplatColor;
|
StructuredBuffer<float4> GaussianSplatColor;
|
||||||
StructuredBuffer<GaussianSplatSHData> GaussianSplatSH;
|
StructuredBuffer<GaussianSplatSHData> GaussianSplatSH;
|
||||||
StructuredBuffer<GaussianSplatChunkData> GaussianSplatChunks;
|
StructuredBuffer<uint> GaussianSplatVisibleChunks : register(t0, space3);
|
||||||
RWStructuredBuffer<uint> GaussianSplatSortDistances;
|
RWStructuredBuffer<uint> GaussianSplatSortDistances;
|
||||||
RWStructuredBuffer<uint> GaussianSplatOrderBuffer;
|
RWStructuredBuffer<uint> GaussianSplatOrderBuffer;
|
||||||
RWStructuredBuffer<GaussianSplatViewData> GaussianSplatViewDataBuffer;
|
RWStructuredBuffer<GaussianSplatViewData> GaussianSplatViewDataBuffer;
|
||||||
@@ -358,23 +412,8 @@ Shader "Builtin Gaussian Splat Utilities"
|
|||||||
const GaussianSplatSHData shData = GaussianSplatSH[index];
|
const GaussianSplatSHData shData = GaussianSplatSH[index];
|
||||||
const uint chunkIndex = index / GAUSSIAN_SPLAT_CHUNK_SIZE;
|
const uint chunkIndex = index / GAUSSIAN_SPLAT_CHUNK_SIZE;
|
||||||
const uint chunkCount = (uint)gSplatParams.w;
|
const uint chunkCount = (uint)gSplatParams.w;
|
||||||
const GaussianSplatChunkData chunkData = GaussianSplatChunks[chunkIndex];
|
|
||||||
const uint shOrder = min((uint)gSplatParams.z, 3u);
|
const uint shOrder = min((uint)gSplatParams.z, 3u);
|
||||||
if (chunkData.posX.x > chunkData.posX.y ||
|
if (chunkIndex >= chunkCount || GaussianSplatVisibleChunks[chunkIndex] == 0u)
|
||||||
chunkData.posY.x > chunkData.posY.y ||
|
|
||||||
chunkData.posZ.x > chunkData.posZ.y)
|
|
||||||
{
|
|
||||||
GaussianSplatSortDistances[index] = 0xffffffffu;
|
|
||||||
GaussianSplatViewDataBuffer[index] = viewData;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (IsChunkDefinitelyOutsideFrustum(
|
|
||||||
chunkData,
|
|
||||||
chunkCount,
|
|
||||||
chunkIndex,
|
|
||||||
gModelMatrix,
|
|
||||||
gViewMatrix,
|
|
||||||
gProjectionMatrix))
|
|
||||||
{
|
{
|
||||||
GaussianSplatSortDistances[index] = 0xffffffffu;
|
GaussianSplatSortDistances[index] = 0xffffffffu;
|
||||||
GaussianSplatViewDataBuffer[index] = viewData;
|
GaussianSplatViewDataBuffer[index] = viewData;
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
|||||||
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
||||||
location = &outPlan.gaussianSplatChunkBuffer;
|
location = &outPlan.gaussianSplatChunkBuffer;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer:
|
||||||
|
location = &outPlan.gaussianSplatVisibleChunkBuffer;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
location = &outPlan.gaussianSplatViewDataBuffer;
|
location = &outPlan.gaussianSplatViewDataBuffer;
|
||||||
break;
|
break;
|
||||||
@@ -643,6 +646,9 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
|||||||
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
||||||
setLayout.usesGaussianSplatChunkBuffer = true;
|
setLayout.usesGaussianSplatChunkBuffer = true;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer:
|
||||||
|
setLayout.usesGaussianSplatVisibleChunkBuffer = true;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
setLayout.usesGaussianSplatViewDataBuffer = true;
|
setLayout.usesGaussianSplatViewDataBuffer = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -189,6 +189,12 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
|||||||
return BuiltinPassResourceSemantic::GaussianSplatChunkBuffer;
|
return BuiltinPassResourceSemantic::GaussianSplatChunkBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatvisiblechunkbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatvisiblechunks") ||
|
||||||
|
semantic == Containers::String("splatvisiblechunks")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
if (semantic == Containers::String("gaussiansplatviewdatabuffer") ||
|
if (semantic == Containers::String("gaussiansplatviewdatabuffer") ||
|
||||||
semantic == Containers::String("gaussiansplatviewdata") ||
|
semantic == Containers::String("gaussiansplatviewdata") ||
|
||||||
semantic == Containers::String("splatviewdata")) {
|
semantic == Containers::String("splatviewdata")) {
|
||||||
@@ -276,6 +282,8 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
|
|||||||
return "GaussianSplatSHBuffer";
|
return "GaussianSplatSHBuffer";
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
||||||
return "GaussianSplatChunkBuffer";
|
return "GaussianSplatChunkBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer:
|
||||||
|
return "GaussianSplatVisibleChunkBuffer";
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
return "GaussianSplatViewDataBuffer";
|
return "GaussianSplatViewDataBuffer";
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
@@ -376,6 +384,7 @@ inline bool IsBuiltinPassResourceTypeCompatible(
|
|||||||
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatChunkBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
||||||
type == Resources::ShaderResourceType::RawBuffer ||
|
type == Resources::ShaderResourceType::RawBuffer ||
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
|||||||
GaussianSplatColorBuffer,
|
GaussianSplatColorBuffer,
|
||||||
GaussianSplatSHBuffer,
|
GaussianSplatSHBuffer,
|
||||||
GaussianSplatChunkBuffer,
|
GaussianSplatChunkBuffer,
|
||||||
|
GaussianSplatVisibleChunkBuffer,
|
||||||
GaussianSplatViewDataBuffer,
|
GaussianSplatViewDataBuffer,
|
||||||
BaseColorTexture,
|
BaseColorTexture,
|
||||||
SourceColorTexture,
|
SourceColorTexture,
|
||||||
@@ -98,6 +99,7 @@ struct BuiltinPassResourceBindingPlan {
|
|||||||
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatChunkBuffer = {};
|
PassResourceBindingLocation gaussianSplatChunkBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatVisibleChunkBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
||||||
PassResourceBindingLocation baseColorTexture = {};
|
PassResourceBindingLocation baseColorTexture = {};
|
||||||
PassResourceBindingLocation sourceColorTexture = {};
|
PassResourceBindingLocation sourceColorTexture = {};
|
||||||
@@ -141,6 +143,7 @@ struct BuiltinPassSetLayoutMetadata {
|
|||||||
bool usesGaussianSplatColorBuffer = false;
|
bool usesGaussianSplatColorBuffer = false;
|
||||||
bool usesGaussianSplatSHBuffer = false;
|
bool usesGaussianSplatSHBuffer = false;
|
||||||
bool usesGaussianSplatChunkBuffer = false;
|
bool usesGaussianSplatChunkBuffer = false;
|
||||||
|
bool usesGaussianSplatVisibleChunkBuffer = false;
|
||||||
bool usesGaussianSplatViewDataBuffer = false;
|
bool usesGaussianSplatViewDataBuffer = false;
|
||||||
bool usesTexture = false;
|
bool usesTexture = false;
|
||||||
bool usesBaseColorTexture = false;
|
bool usesBaseColorTexture = false;
|
||||||
@@ -221,6 +224,7 @@ private:
|
|||||||
usesGaussianSplatColorBuffer = other.usesGaussianSplatColorBuffer;
|
usesGaussianSplatColorBuffer = other.usesGaussianSplatColorBuffer;
|
||||||
usesGaussianSplatSHBuffer = other.usesGaussianSplatSHBuffer;
|
usesGaussianSplatSHBuffer = other.usesGaussianSplatSHBuffer;
|
||||||
usesGaussianSplatChunkBuffer = other.usesGaussianSplatChunkBuffer;
|
usesGaussianSplatChunkBuffer = other.usesGaussianSplatChunkBuffer;
|
||||||
|
usesGaussianSplatVisibleChunkBuffer = other.usesGaussianSplatVisibleChunkBuffer;
|
||||||
usesGaussianSplatViewDataBuffer = other.usesGaussianSplatViewDataBuffer;
|
usesGaussianSplatViewDataBuffer = other.usesGaussianSplatViewDataBuffer;
|
||||||
usesTexture = other.usesTexture;
|
usesTexture = other.usesTexture;
|
||||||
usesBaseColorTexture = other.usesBaseColorTexture;
|
usesBaseColorTexture = other.usesBaseColorTexture;
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ private:
|
|||||||
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatChunkBuffer = {};
|
PassResourceBindingLocation gaussianSplatChunkBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatVisibleChunkBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -144,6 +145,7 @@ private:
|
|||||||
RHI::RHIResourceView* colorView = nullptr;
|
RHI::RHIResourceView* colorView = nullptr;
|
||||||
RHI::RHIResourceView* shView = nullptr;
|
RHI::RHIResourceView* shView = nullptr;
|
||||||
RHI::RHIResourceView* chunkView = nullptr;
|
RHI::RHIResourceView* chunkView = nullptr;
|
||||||
|
RHI::RHIResourceView* visibleChunkView = nullptr;
|
||||||
RHI::RHIResourceView* viewDataView = nullptr;
|
RHI::RHIResourceView* viewDataView = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -215,6 +217,7 @@ private:
|
|||||||
|
|
||||||
enum class PassLayoutUsage : Core::uint8 {
|
enum class PassLayoutUsage : Core::uint8 {
|
||||||
Draw,
|
Draw,
|
||||||
|
MarkVisibleChunks,
|
||||||
PrepareOrder,
|
PrepareOrder,
|
||||||
BitonicSort
|
BitonicSort
|
||||||
};
|
};
|
||||||
@@ -228,6 +231,7 @@ private:
|
|||||||
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
const Resources::Material* material) const;
|
const Resources::Material* material) const;
|
||||||
|
ResolvedShaderPass ResolveMarkVisibleChunksShaderPass(const RenderSceneData& sceneData) const;
|
||||||
ResolvedShaderPass ResolvePrepareOrderShaderPass(const RenderSceneData& sceneData) const;
|
ResolvedShaderPass ResolvePrepareOrderShaderPass(const RenderSceneData& sceneData) const;
|
||||||
ResolvedShaderPass ResolveBitonicSortShaderPass(const RenderSceneData& sceneData) const;
|
ResolvedShaderPass ResolveBitonicSortShaderPass(const RenderSceneData& sceneData) const;
|
||||||
PassResourceLayout* GetOrCreatePassResourceLayout(
|
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||||
@@ -260,9 +264,14 @@ private:
|
|||||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
||||||
RHI::RHIResourceView* sortDistanceView,
|
RHI::RHIResourceView* sortDistanceView,
|
||||||
RHI::RHIResourceView* orderView,
|
RHI::RHIResourceView* orderView,
|
||||||
|
RHI::RHIResourceView* visibleChunkView,
|
||||||
RHI::RHIResourceView* viewDataView);
|
RHI::RHIResourceView* viewDataView);
|
||||||
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||||
|
bool MarkVisibleGaussianSplatChunks(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat);
|
||||||
bool PrepareVisibleGaussianSplat(
|
bool PrepareVisibleGaussianSplat(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
|
|||||||
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
workingSet == nullptr ||
|
workingSet == nullptr ||
|
||||||
|
workingSet->visibleChunks.shaderResourceView == nullptr ||
|
||||||
|
workingSet->visibleChunks.unorderedAccessView == nullptr ||
|
||||||
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
||||||
workingSet->orderIndices.shaderResourceView == nullptr ||
|
workingSet->orderIndices.shaderResourceView == nullptr ||
|
||||||
workingSet->orderIndices.unorderedAccessView == nullptr ||
|
workingSet->orderIndices.unorderedAccessView == nullptr ||
|
||||||
@@ -325,6 +327,13 @@ bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) {
|
|||||||
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
||||||
|
if (!MarkVisibleGaussianSplatChunks(
|
||||||
|
context.renderContext,
|
||||||
|
context.sceneData,
|
||||||
|
visibleGaussianSplat)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PrepareVisibleGaussianSplat(
|
if (!PrepareVisibleGaussianSplat(
|
||||||
context.renderContext,
|
context.renderContext,
|
||||||
context.sceneData,
|
context.sceneData,
|
||||||
@@ -509,6 +518,26 @@ BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolvePr
|
|||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveMarkVisibleChunksShaderPass(
|
||||||
|
const RenderSceneData& sceneData) const {
|
||||||
|
ResolvedShaderPass resolved = {};
|
||||||
|
if (!m_builtinGaussianSplatUtilitiesShader.IsValid()) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Resources::Shader* shader = m_builtinGaussianSplatUtilitiesShader.Get();
|
||||||
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
|
||||||
|
const Containers::String passName("GaussianSplatMarkVisibleChunks");
|
||||||
|
if (const Resources::ShaderPass* shaderPass =
|
||||||
|
FindCompatibleComputePass(*shader, passName, sceneData.globalShaderKeywords, backend)) {
|
||||||
|
resolved.shader = shader;
|
||||||
|
resolved.pass = shaderPass;
|
||||||
|
resolved.passName = passName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveBitonicSortShaderPass(
|
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveBitonicSortShaderPass(
|
||||||
const RenderSceneData& sceneData) const {
|
const RenderSceneData& sceneData) const {
|
||||||
ResolvedShaderPass resolved = {};
|
ResolvedShaderPass resolved = {};
|
||||||
@@ -581,6 +610,7 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
|
|||||||
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
||||||
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
||||||
passLayout.gaussianSplatChunkBuffer = bindingPlan.gaussianSplatChunkBuffer;
|
passLayout.gaussianSplatChunkBuffer = bindingPlan.gaussianSplatChunkBuffer;
|
||||||
|
passLayout.gaussianSplatVisibleChunkBuffer = bindingPlan.gaussianSplatVisibleChunkBuffer;
|
||||||
passLayout.gaussianSplatViewDataBuffer = bindingPlan.gaussianSplatViewDataBuffer;
|
passLayout.gaussianSplatViewDataBuffer = bindingPlan.gaussianSplatViewDataBuffer;
|
||||||
|
|
||||||
if (!passLayout.perObject.IsValid()) {
|
if (!passLayout.perObject.IsValid()) {
|
||||||
@@ -596,6 +626,12 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
|
|||||||
return failLayout(
|
return failLayout(
|
||||||
"BuiltinGaussianSplatPass draw pass requires order and view-data gaussian splat buffer bindings");
|
"BuiltinGaussianSplatPass draw pass requires order and view-data gaussian splat buffer bindings");
|
||||||
}
|
}
|
||||||
|
} else if (usage == PassLayoutUsage::MarkVisibleChunks) {
|
||||||
|
if (!passLayout.gaussianSplatChunkBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatVisibleChunkBuffer.IsValid()) {
|
||||||
|
return failLayout(
|
||||||
|
"BuiltinGaussianSplatPass mark-visible-chunks pass requires chunk and visible-chunk gaussian splat buffer bindings");
|
||||||
|
}
|
||||||
} else if (usage == PassLayoutUsage::PrepareOrder) {
|
} else if (usage == PassLayoutUsage::PrepareOrder) {
|
||||||
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
||||||
@@ -603,10 +639,10 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
|
|||||||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatColorBuffer.IsValid() ||
|
!passLayout.gaussianSplatColorBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatSHBuffer.IsValid() ||
|
!passLayout.gaussianSplatSHBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatChunkBuffer.IsValid() ||
|
!passLayout.gaussianSplatVisibleChunkBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatViewDataBuffer.IsValid()) {
|
!passLayout.gaussianSplatViewDataBuffer.IsValid()) {
|
||||||
return failLayout(
|
return failLayout(
|
||||||
"BuiltinGaussianSplatPass prepare-order pass requires sort distance, order, position, other, color, SH, chunk, and view-data gaussian splat buffer bindings");
|
"BuiltinGaussianSplatPass prepare-order pass requires sort distance, order, position, other, color, SH, visible-chunk, and view-data gaussian splat buffer bindings");
|
||||||
}
|
}
|
||||||
} else if (usage == PassLayoutUsage::BitonicSort) {
|
} else if (usage == PassLayoutUsage::BitonicSort) {
|
||||||
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
||||||
@@ -785,6 +821,7 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
||||||
RHI::RHIResourceView* sortDistanceView,
|
RHI::RHIResourceView* sortDistanceView,
|
||||||
RHI::RHIResourceView* orderView,
|
RHI::RHIResourceView* orderView,
|
||||||
|
RHI::RHIResourceView* visibleChunkView,
|
||||||
RHI::RHIResourceView* viewDataView) {
|
RHI::RHIResourceView* viewDataView) {
|
||||||
DynamicDescriptorSetKey key = {};
|
DynamicDescriptorSetKey key = {};
|
||||||
key.passLayout = passLayoutKey;
|
key.passLayout = passLayoutKey;
|
||||||
@@ -810,6 +847,7 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
: nullptr;
|
: nullptr;
|
||||||
RHI::RHIResourceView* resolvedSortDistanceView = sortDistanceView;
|
RHI::RHIResourceView* resolvedSortDistanceView = sortDistanceView;
|
||||||
RHI::RHIResourceView* resolvedOrderView = orderView;
|
RHI::RHIResourceView* resolvedOrderView = orderView;
|
||||||
|
RHI::RHIResourceView* resolvedVisibleChunkView = visibleChunkView;
|
||||||
RHI::RHIResourceView* resolvedViewDataView = viewDataView;
|
RHI::RHIResourceView* resolvedViewDataView = viewDataView;
|
||||||
|
|
||||||
if (setLayout.usesGaussianSplatSortDistanceBuffer && workingSet != nullptr) {
|
if (setLayout.usesGaussianSplatSortDistanceBuffer && workingSet != nullptr) {
|
||||||
@@ -839,6 +877,15 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatVisibleChunkBuffer && workingSet != nullptr) {
|
||||||
|
if (const RHI::DescriptorSetLayoutBinding* layoutBinding =
|
||||||
|
FindSetLayoutBinding(setLayout, passLayout.gaussianSplatVisibleChunkBuffer.binding)) {
|
||||||
|
if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->visibleChunks, layoutBinding)) {
|
||||||
|
resolvedVisibleChunkView = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
|
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
|
||||||
if (setLayout.usesMaterial) {
|
if (setLayout.usesMaterial) {
|
||||||
if (!passLayout.material.IsValid() ||
|
if (!passLayout.material.IsValid() ||
|
||||||
@@ -953,6 +1000,20 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatVisibleChunkBuffer) {
|
||||||
|
if (resolvedVisibleChunkView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatVisibleChunkBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatVisibleChunkBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.visibleChunkView != resolvedVisibleChunkView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatVisibleChunkBuffer.binding,
|
||||||
|
resolvedVisibleChunkView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (setLayout.usesGaussianSplatViewDataBuffer) {
|
if (setLayout.usesGaussianSplatViewDataBuffer) {
|
||||||
if (resolvedViewDataView == nullptr ||
|
if (resolvedViewDataView == nullptr ||
|
||||||
!passLayout.gaussianSplatViewDataBuffer.IsValid() ||
|
!passLayout.gaussianSplatViewDataBuffer.IsValid() ||
|
||||||
@@ -975,6 +1036,7 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
||||||
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
||||||
cachedDescriptorSet.chunkView = cachedGaussianSplat.chunks.shaderResourceView;
|
cachedDescriptorSet.chunkView = cachedGaussianSplat.chunks.shaderResourceView;
|
||||||
|
cachedDescriptorSet.visibleChunkView = resolvedVisibleChunkView;
|
||||||
cachedDescriptorSet.viewDataView = resolvedViewDataView;
|
cachedDescriptorSet.viewDataView = resolvedViewDataView;
|
||||||
return &cachedDescriptorSet;
|
return &cachedDescriptorSet;
|
||||||
}
|
}
|
||||||
@@ -1012,9 +1074,178 @@ void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& pas
|
|||||||
passLayout.gaussianSplatColorBuffer = {};
|
passLayout.gaussianSplatColorBuffer = {};
|
||||||
passLayout.gaussianSplatSHBuffer = {};
|
passLayout.gaussianSplatSHBuffer = {};
|
||||||
passLayout.gaussianSplatChunkBuffer = {};
|
passLayout.gaussianSplatChunkBuffer = {};
|
||||||
|
passLayout.gaussianSplatVisibleChunkBuffer = {};
|
||||||
passLayout.gaussianSplatViewDataBuffer = {};
|
passLayout.gaussianSplatViewDataBuffer = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::MarkVisibleGaussianSplatChunks(
|
||||||
|
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 fail("BuiltinGaussianSplatPass mark-visible-chunks failed: invalid visible gaussian splat item");
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
|
||||||
|
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
|
||||||
|
if (cachedGaussianSplat == nullptr ||
|
||||||
|
cachedGaussianSplat->chunks.shaderResourceView == nullptr ||
|
||||||
|
cachedGaussianSplat->chunkCount == 0u) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: gaussian splat chunk GPU cache is incomplete");
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
|
workingSet == nullptr ||
|
||||||
|
workingSet->visibleChunks.shaderResourceView == nullptr ||
|
||||||
|
workingSet->visibleChunks.unorderedAccessView == nullptr) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: working set allocation is incomplete");
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResolvedShaderPass resolvedShaderPass = ResolveMarkVisibleChunksShaderPass(sceneData);
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: utilities shader pass was not resolved");
|
||||||
|
}
|
||||||
|
|
||||||
|
PassLayoutKey passLayoutKey = {};
|
||||||
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::MarkVisibleChunks);
|
||||||
|
RHI::RHIPipelineState* pipelineState = GetOrCreateComputePipelineState(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::MarkVisibleChunks,
|
||||||
|
sceneData.globalShaderKeywords);
|
||||||
|
if (passLayout == nullptr || pipelineState == nullptr) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: compute pipeline setup was not created");
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHICommandList* commandList = context.commandList;
|
||||||
|
if (!TransitionWorkingSetBuffer(commandList, workingSet->visibleChunks, RHI::ResourceStates::UnorderedAccess)) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: resource transition failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
|
||||||
|
const float shOrder =
|
||||||
|
visibleGaussianSplat.gaussianSplat != nullptr
|
||||||
|
? static_cast<float>(visibleGaussianSplat.gaussianSplat->GetSHOrder())
|
||||||
|
: 0.0f;
|
||||||
|
|
||||||
|
const PerObjectConstants perObjectConstants = {
|
||||||
|
sceneData.cameraData.projection,
|
||||||
|
sceneData.cameraData.view,
|
||||||
|
visibleGaussianSplat.localToWorld.Transpose(),
|
||||||
|
visibleGaussianSplat.localToWorld.Inverse(),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldRight, 0.0f),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldUp, 0.0f),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldPosition, 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),
|
||||||
|
static_cast<float>(workingSet->sortCapacity),
|
||||||
|
shOrder,
|
||||||
|
static_cast<float>(cachedGaussianSplat->chunkCount))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (passLayout->descriptorSetCount > 0u) {
|
||||||
|
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||||
|
for (Core::uint32 descriptorOffset = 0u; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||||
|
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||||
|
if (setIndex >= passLayout->setLayouts.size()) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: descriptor set index overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||||
|
if (setLayout.layout.bindingCount == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(setLayout.usesPerObject ||
|
||||||
|
setLayout.usesGaussianSplatChunkBuffer ||
|
||||||
|
setLayout.usesGaussianSplatVisibleChunkBuffer)) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: unexpected descriptor set layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::uint64 objectId =
|
||||||
|
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
|
||||||
|
const Resources::GaussianSplat* gaussianSplatKey =
|
||||||
|
(setLayout.usesGaussianSplatChunkBuffer ||
|
||||||
|
setLayout.usesGaussianSplatVisibleChunkBuffer)
|
||||||
|
? visibleGaussianSplat.gaussianSplat
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||||
|
passLayoutKey,
|
||||||
|
*passLayout,
|
||||||
|
setLayout,
|
||||||
|
setIndex,
|
||||||
|
objectId,
|
||||||
|
visibleGaussianSplat.gaussianSplatRenderer,
|
||||||
|
nullptr,
|
||||||
|
gaussianSplatKey,
|
||||||
|
MaterialConstantPayloadView(),
|
||||||
|
*cachedGaussianSplat,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
workingSet->visibleChunks.unorderedAccessView,
|
||||||
|
nullptr);
|
||||||
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: dynamic descriptor set resolution failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||||
|
if (setLayout.usesPerObject) {
|
||||||
|
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: per-object binding is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSet->WriteConstant(
|
||||||
|
passLayout->perObject.binding,
|
||||||
|
&perObjectConstants,
|
||||||
|
sizeof(perObjectConstants));
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSets[descriptorOffset] = descriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindDescriptorSetRanges(
|
||||||
|
passLayout->firstDescriptorSet,
|
||||||
|
descriptorSets,
|
||||||
|
[commandList, passLayout](Core::uint32 firstSet, Core::uint32 count, RHI::RHIDescriptorSet** sets) {
|
||||||
|
commandList->SetComputeDescriptorSets(
|
||||||
|
firstSet,
|
||||||
|
count,
|
||||||
|
sets,
|
||||||
|
passLayout->pipelineLayout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList->Dispatch((cachedGaussianSplat->chunkCount + 63u) / 64u, 1u, 1u);
|
||||||
|
|
||||||
|
if (!TransitionWorkingSetBuffer(commandList, workingSet->visibleChunks, RHI::ResourceStates::NonPixelShaderResource)) {
|
||||||
|
return fail("BuiltinGaussianSplatPass mark-visible-chunks failed: visible-chunk transition failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
@@ -1037,14 +1268,14 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
|
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
|
||||||
cachedGaussianSplat->other.shaderResourceView == nullptr ||
|
cachedGaussianSplat->other.shaderResourceView == nullptr ||
|
||||||
cachedGaussianSplat->color.shaderResourceView == nullptr ||
|
cachedGaussianSplat->color.shaderResourceView == nullptr ||
|
||||||
cachedGaussianSplat->sh.shaderResourceView == nullptr ||
|
cachedGaussianSplat->sh.shaderResourceView == nullptr) {
|
||||||
cachedGaussianSplat->chunks.shaderResourceView == nullptr) {
|
|
||||||
return fail("BuiltinGaussianSplatPass prepare-order failed: gaussian splat GPU cache is incomplete");
|
return fail("BuiltinGaussianSplatPass prepare-order failed: gaussian splat GPU cache is incomplete");
|
||||||
}
|
}
|
||||||
|
|
||||||
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
workingSet == nullptr ||
|
workingSet == nullptr ||
|
||||||
|
workingSet->visibleChunks.shaderResourceView == nullptr ||
|
||||||
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
||||||
workingSet->orderIndices.unorderedAccessView == nullptr ||
|
workingSet->orderIndices.unorderedAccessView == nullptr ||
|
||||||
workingSet->orderIndices.shaderResourceView == nullptr ||
|
workingSet->orderIndices.shaderResourceView == nullptr ||
|
||||||
@@ -1076,7 +1307,8 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHICommandList* commandList = context.commandList;
|
RHI::RHICommandList* commandList = context.commandList;
|
||||||
if (!TransitionWorkingSetBuffer(commandList, workingSet->sortDistances, RHI::ResourceStates::UnorderedAccess) ||
|
if (!TransitionWorkingSetBuffer(commandList, workingSet->visibleChunks, RHI::ResourceStates::NonPixelShaderResource) ||
|
||||||
|
!TransitionWorkingSetBuffer(commandList, workingSet->sortDistances, RHI::ResourceStates::UnorderedAccess) ||
|
||||||
!TransitionWorkingSetBuffer(commandList, workingSet->orderIndices, RHI::ResourceStates::UnorderedAccess) ||
|
!TransitionWorkingSetBuffer(commandList, workingSet->orderIndices, RHI::ResourceStates::UnorderedAccess) ||
|
||||||
!TransitionWorkingSetBuffer(commandList, workingSet->viewData, RHI::ResourceStates::UnorderedAccess)) {
|
!TransitionWorkingSetBuffer(commandList, workingSet->viewData, RHI::ResourceStates::UnorderedAccess)) {
|
||||||
return fail("BuiltinGaussianSplatPass prepare-order failed: resource transition failed");
|
return fail("BuiltinGaussianSplatPass prepare-order failed: resource transition failed");
|
||||||
@@ -1123,13 +1355,13 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(setLayout.usesPerObject ||
|
if (!(setLayout.usesPerObject ||
|
||||||
|
setLayout.usesGaussianSplatVisibleChunkBuffer ||
|
||||||
setLayout.usesGaussianSplatSortDistanceBuffer ||
|
setLayout.usesGaussianSplatSortDistanceBuffer ||
|
||||||
setLayout.usesGaussianSplatOrderBuffer ||
|
setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
setLayout.usesGaussianSplatPositionBuffer ||
|
setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
setLayout.usesGaussianSplatOtherBuffer ||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
setLayout.usesGaussianSplatColorBuffer ||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
setLayout.usesGaussianSplatSHBuffer ||
|
setLayout.usesGaussianSplatSHBuffer ||
|
||||||
setLayout.usesGaussianSplatChunkBuffer ||
|
|
||||||
setLayout.usesGaussianSplatViewDataBuffer)) {
|
setLayout.usesGaussianSplatViewDataBuffer)) {
|
||||||
return fail("BuiltinGaussianSplatPass prepare-order failed: unexpected descriptor set layout");
|
return fail("BuiltinGaussianSplatPass prepare-order failed: unexpected descriptor set layout");
|
||||||
}
|
}
|
||||||
@@ -1138,12 +1370,12 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
|
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
|
||||||
const Resources::GaussianSplat* gaussianSplatKey =
|
const Resources::GaussianSplat* gaussianSplatKey =
|
||||||
(setLayout.usesGaussianSplatSortDistanceBuffer ||
|
(setLayout.usesGaussianSplatSortDistanceBuffer ||
|
||||||
|
setLayout.usesGaussianSplatVisibleChunkBuffer ||
|
||||||
setLayout.usesGaussianSplatOrderBuffer ||
|
setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
setLayout.usesGaussianSplatPositionBuffer ||
|
setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
setLayout.usesGaussianSplatOtherBuffer ||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
setLayout.usesGaussianSplatColorBuffer ||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
setLayout.usesGaussianSplatSHBuffer ||
|
setLayout.usesGaussianSplatSHBuffer ||
|
||||||
setLayout.usesGaussianSplatChunkBuffer ||
|
|
||||||
setLayout.usesGaussianSplatViewDataBuffer)
|
setLayout.usesGaussianSplatViewDataBuffer)
|
||||||
? visibleGaussianSplat.gaussianSplat
|
? visibleGaussianSplat.gaussianSplat
|
||||||
: nullptr;
|
: nullptr;
|
||||||
@@ -1161,6 +1393,7 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
|||||||
*cachedGaussianSplat,
|
*cachedGaussianSplat,
|
||||||
workingSet->sortDistances.unorderedAccessView,
|
workingSet->sortDistances.unorderedAccessView,
|
||||||
workingSet->orderIndices.unorderedAccessView,
|
workingSet->orderIndices.unorderedAccessView,
|
||||||
|
workingSet->visibleChunks.shaderResourceView,
|
||||||
workingSet->viewData.unorderedAccessView);
|
workingSet->viewData.unorderedAccessView);
|
||||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
return fail("BuiltinGaussianSplatPass prepare-order failed: dynamic descriptor set resolution failed");
|
return fail("BuiltinGaussianSplatPass prepare-order failed: dynamic descriptor set resolution failed");
|
||||||
@@ -1332,6 +1565,7 @@ bool BuiltinGaussianSplatPass::SortVisibleGaussianSplat(
|
|||||||
*cachedGaussianSplat,
|
*cachedGaussianSplat,
|
||||||
workingSet->sortDistances.unorderedAccessView,
|
workingSet->sortDistances.unorderedAccessView,
|
||||||
workingSet->orderIndices.unorderedAccessView,
|
workingSet->orderIndices.unorderedAccessView,
|
||||||
|
nullptr,
|
||||||
nullptr);
|
nullptr);
|
||||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
return fail("BuiltinGaussianSplatPass bitonic-sort failed: dynamic descriptor set resolution failed");
|
return fail("BuiltinGaussianSplatPass bitonic-sort failed: dynamic descriptor set resolution failed");
|
||||||
@@ -1507,6 +1741,7 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
*cachedGaussianSplat,
|
*cachedGaussianSplat,
|
||||||
nullptr,
|
nullptr,
|
||||||
workingSet->orderIndices.shaderResourceView,
|
workingSet->orderIndices.shaderResourceView,
|
||||||
|
nullptr,
|
||||||
workingSet->viewData.shaderResourceView);
|
workingSet->viewData.shaderResourceView);
|
||||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
return fail("BuiltinGaussianSplatPass draw failed: dynamic descriptor set resolution failed");
|
return fail("BuiltinGaussianSplatPass draw failed: dynamic descriptor set resolution failed");
|
||||||
|
|||||||
@@ -92,12 +92,16 @@ bool BuiltinGaussianSplatPassResources::EnsureWorkingSet(
|
|||||||
|
|
||||||
const Core::uint32 splatCapacity = visibleGaussianSplat.gaussianSplat->GetSplatCount();
|
const Core::uint32 splatCapacity = visibleGaussianSplat.gaussianSplat->GetSplatCount();
|
||||||
const Core::uint32 sortCapacity = ComputeSortCapacity(splatCapacity);
|
const Core::uint32 sortCapacity = ComputeSortCapacity(splatCapacity);
|
||||||
|
Core::uint32 chunkCapacity = visibleGaussianSplat.gaussianSplat->GetChunkCount();
|
||||||
if (splatCapacity == 0u) {
|
if (splatCapacity == 0u) {
|
||||||
Debug::Logger::Get().Error(
|
Debug::Logger::Get().Error(
|
||||||
Debug::LogCategory::Rendering,
|
Debug::LogCategory::Rendering,
|
||||||
"BuiltinGaussianSplatPassResources::EnsureWorkingSet failed: splat capacity is zero");
|
"BuiltinGaussianSplatPassResources::EnsureWorkingSet failed: splat capacity is zero");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (chunkCapacity == 0u) {
|
||||||
|
chunkCapacity = (splatCapacity + 255u) / 256u;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ResetForDevice(device)) {
|
if (!ResetForDevice(device)) {
|
||||||
Debug::Logger::Get().Error(
|
Debug::Logger::Get().Error(
|
||||||
@@ -107,11 +111,14 @@ bool BuiltinGaussianSplatPassResources::EnsureWorkingSet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
WorkingSet& workingSet = m_workingSets[visibleGaussianSplat.gaussianSplatRenderer];
|
WorkingSet& workingSet = m_workingSets[visibleGaussianSplat.gaussianSplatRenderer];
|
||||||
if (workingSet.splatCapacity < splatCapacity) {
|
if (workingSet.splatCapacity < splatCapacity ||
|
||||||
|
workingSet.sortCapacity < sortCapacity ||
|
||||||
|
workingSet.chunkCapacity < chunkCapacity) {
|
||||||
DestroyBufferView(workingSet.sortDistances);
|
DestroyBufferView(workingSet.sortDistances);
|
||||||
DestroyBufferView(workingSet.orderIndices);
|
DestroyBufferView(workingSet.orderIndices);
|
||||||
|
DestroyBufferView(workingSet.visibleChunks);
|
||||||
DestroyBufferView(workingSet.viewData);
|
DestroyBufferView(workingSet.viewData);
|
||||||
if (!RecreateWorkingSet(device, splatCapacity, sortCapacity, workingSet)) {
|
if (!RecreateWorkingSet(device, splatCapacity, sortCapacity, chunkCapacity, workingSet)) {
|
||||||
Debug::Logger::Get().Error(
|
Debug::Logger::Get().Error(
|
||||||
Debug::LogCategory::Rendering,
|
Debug::LogCategory::Rendering,
|
||||||
"BuiltinGaussianSplatPassResources::EnsureWorkingSet failed: RecreateWorkingSet returned false");
|
"BuiltinGaussianSplatPassResources::EnsureWorkingSet failed: RecreateWorkingSet returned false");
|
||||||
@@ -160,6 +167,7 @@ void BuiltinGaussianSplatPassResources::Shutdown() {
|
|||||||
for (auto& workingSetPair : m_workingSets) {
|
for (auto& workingSetPair : m_workingSets) {
|
||||||
DestroyBufferView(workingSetPair.second.sortDistances);
|
DestroyBufferView(workingSetPair.second.sortDistances);
|
||||||
DestroyBufferView(workingSetPair.second.orderIndices);
|
DestroyBufferView(workingSetPair.second.orderIndices);
|
||||||
|
DestroyBufferView(workingSetPair.second.visibleChunks);
|
||||||
DestroyBufferView(workingSetPair.second.viewData);
|
DestroyBufferView(workingSetPair.second.viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +254,7 @@ bool BuiltinGaussianSplatPassResources::RecreateWorkingSet(
|
|||||||
RHI::RHIDevice* device,
|
RHI::RHIDevice* device,
|
||||||
Core::uint32 splatCapacity,
|
Core::uint32 splatCapacity,
|
||||||
Core::uint32 sortCapacity,
|
Core::uint32 sortCapacity,
|
||||||
|
Core::uint32 chunkCapacity,
|
||||||
WorkingSet& workingSet) {
|
WorkingSet& workingSet) {
|
||||||
if (!CreateStructuredBufferView(device, sortCapacity, kSortDistanceStride, workingSet.sortDistances)) {
|
if (!CreateStructuredBufferView(device, sortCapacity, kSortDistanceStride, workingSet.sortDistances)) {
|
||||||
Debug::Logger::Get().Error(
|
Debug::Logger::Get().Error(
|
||||||
@@ -253,10 +262,12 @@ bool BuiltinGaussianSplatPassResources::RecreateWorkingSet(
|
|||||||
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: sort-distance buffer view creation failed");
|
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: sort-distance buffer view creation failed");
|
||||||
DestroyBufferView(workingSet.sortDistances);
|
DestroyBufferView(workingSet.sortDistances);
|
||||||
DestroyBufferView(workingSet.orderIndices);
|
DestroyBufferView(workingSet.orderIndices);
|
||||||
|
DestroyBufferView(workingSet.visibleChunks);
|
||||||
DestroyBufferView(workingSet.viewData);
|
DestroyBufferView(workingSet.viewData);
|
||||||
workingSet.renderer = nullptr;
|
workingSet.renderer = nullptr;
|
||||||
workingSet.splatCapacity = 0u;
|
workingSet.splatCapacity = 0u;
|
||||||
workingSet.sortCapacity = 0u;
|
workingSet.sortCapacity = 0u;
|
||||||
|
workingSet.chunkCapacity = 0u;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,10 +277,27 @@ bool BuiltinGaussianSplatPassResources::RecreateWorkingSet(
|
|||||||
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: order-index buffer view creation failed");
|
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: order-index buffer view creation failed");
|
||||||
DestroyBufferView(workingSet.sortDistances);
|
DestroyBufferView(workingSet.sortDistances);
|
||||||
DestroyBufferView(workingSet.orderIndices);
|
DestroyBufferView(workingSet.orderIndices);
|
||||||
|
DestroyBufferView(workingSet.visibleChunks);
|
||||||
DestroyBufferView(workingSet.viewData);
|
DestroyBufferView(workingSet.viewData);
|
||||||
workingSet.renderer = nullptr;
|
workingSet.renderer = nullptr;
|
||||||
workingSet.splatCapacity = 0u;
|
workingSet.splatCapacity = 0u;
|
||||||
workingSet.sortCapacity = 0u;
|
workingSet.sortCapacity = 0u;
|
||||||
|
workingSet.chunkCapacity = 0u;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateStructuredBufferView(device, chunkCapacity, kVisibleChunkStride, workingSet.visibleChunks)) {
|
||||||
|
Debug::Logger::Get().Error(
|
||||||
|
Debug::LogCategory::Rendering,
|
||||||
|
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: visible-chunk buffer view creation failed");
|
||||||
|
DestroyBufferView(workingSet.sortDistances);
|
||||||
|
DestroyBufferView(workingSet.orderIndices);
|
||||||
|
DestroyBufferView(workingSet.visibleChunks);
|
||||||
|
DestroyBufferView(workingSet.viewData);
|
||||||
|
workingSet.renderer = nullptr;
|
||||||
|
workingSet.splatCapacity = 0u;
|
||||||
|
workingSet.sortCapacity = 0u;
|
||||||
|
workingSet.chunkCapacity = 0u;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,15 +307,18 @@ bool BuiltinGaussianSplatPassResources::RecreateWorkingSet(
|
|||||||
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: view-data buffer view creation failed");
|
"BuiltinGaussianSplatPassResources::RecreateWorkingSet failed: view-data buffer view creation failed");
|
||||||
DestroyBufferView(workingSet.sortDistances);
|
DestroyBufferView(workingSet.sortDistances);
|
||||||
DestroyBufferView(workingSet.orderIndices);
|
DestroyBufferView(workingSet.orderIndices);
|
||||||
|
DestroyBufferView(workingSet.visibleChunks);
|
||||||
DestroyBufferView(workingSet.viewData);
|
DestroyBufferView(workingSet.viewData);
|
||||||
workingSet.renderer = nullptr;
|
workingSet.renderer = nullptr;
|
||||||
workingSet.splatCapacity = 0u;
|
workingSet.splatCapacity = 0u;
|
||||||
workingSet.sortCapacity = 0u;
|
workingSet.sortCapacity = 0u;
|
||||||
|
workingSet.chunkCapacity = 0u;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
workingSet.splatCapacity = splatCapacity;
|
workingSet.splatCapacity = splatCapacity;
|
||||||
workingSet.sortCapacity = sortCapacity;
|
workingSet.sortCapacity = sortCapacity;
|
||||||
|
workingSet.chunkCapacity = chunkCapacity;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ public:
|
|||||||
const Components::GaussianSplatRendererComponent* renderer = nullptr;
|
const Components::GaussianSplatRendererComponent* renderer = nullptr;
|
||||||
Core::uint32 splatCapacity = 0u;
|
Core::uint32 splatCapacity = 0u;
|
||||||
Core::uint32 sortCapacity = 0u;
|
Core::uint32 sortCapacity = 0u;
|
||||||
|
Core::uint32 chunkCapacity = 0u;
|
||||||
CachedBufferView sortDistances = {};
|
CachedBufferView sortDistances = {};
|
||||||
CachedBufferView orderIndices = {};
|
CachedBufferView orderIndices = {};
|
||||||
|
CachedBufferView visibleChunks = {};
|
||||||
CachedBufferView viewData = {};
|
CachedBufferView viewData = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,6 +75,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
static constexpr Core::uint32 kSortDistanceStride = sizeof(float);
|
static constexpr Core::uint32 kSortDistanceStride = sizeof(float);
|
||||||
static constexpr Core::uint32 kOrderIndexStride = sizeof(Core::uint32);
|
static constexpr Core::uint32 kOrderIndexStride = sizeof(Core::uint32);
|
||||||
|
static constexpr Core::uint32 kVisibleChunkStride = sizeof(Core::uint32);
|
||||||
static constexpr Core::uint32 kViewDataStride = sizeof(GaussianSplatViewData);
|
static constexpr Core::uint32 kViewDataStride = sizeof(GaussianSplatViewData);
|
||||||
|
|
||||||
static void DestroyBufferView(CachedBufferView& bufferView);
|
static void DestroyBufferView(CachedBufferView& bufferView);
|
||||||
@@ -83,6 +86,7 @@ private:
|
|||||||
RHI::RHIDevice* device,
|
RHI::RHIDevice* device,
|
||||||
Core::uint32 splatCapacity,
|
Core::uint32 splatCapacity,
|
||||||
Core::uint32 sortCapacity,
|
Core::uint32 sortCapacity,
|
||||||
|
Core::uint32 chunkCapacity,
|
||||||
WorkingSet& workingSet);
|
WorkingSet& workingSet);
|
||||||
bool CreateStructuredBufferView(
|
bool CreateStructuredBufferView(
|
||||||
RHI::RHIDevice* device,
|
RHI::RHIDevice* device,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@@ -194,6 +195,8 @@ void ImportConcretePass(Shader& shader, const ShaderPass& sourcePass) {
|
|||||||
|
|
||||||
bool IsScannedAuthoringResourceType(ShaderResourceType type) {
|
bool IsScannedAuthoringResourceType(ShaderResourceType type) {
|
||||||
return type == ShaderResourceType::ConstantBuffer ||
|
return type == ShaderResourceType::ConstantBuffer ||
|
||||||
|
type == ShaderResourceType::Texture2D ||
|
||||||
|
type == ShaderResourceType::TextureCube ||
|
||||||
type == ShaderResourceType::StructuredBuffer ||
|
type == ShaderResourceType::StructuredBuffer ||
|
||||||
type == ShaderResourceType::RawBuffer ||
|
type == ShaderResourceType::RawBuffer ||
|
||||||
type == ShaderResourceType::RWStructuredBuffer ||
|
type == ShaderResourceType::RWStructuredBuffer ||
|
||||||
@@ -202,6 +205,9 @@ bool IsScannedAuthoringResourceType(ShaderResourceType type) {
|
|||||||
|
|
||||||
Core::uint32 ResolveDefaultAuthoringSet(ShaderResourceType type) {
|
Core::uint32 ResolveDefaultAuthoringSet(ShaderResourceType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case ShaderResourceType::Texture2D:
|
||||||
|
case ShaderResourceType::TextureCube:
|
||||||
|
return 4u;
|
||||||
case ShaderResourceType::StructuredBuffer:
|
case ShaderResourceType::StructuredBuffer:
|
||||||
case ShaderResourceType::RawBuffer:
|
case ShaderResourceType::RawBuffer:
|
||||||
return 2u;
|
return 2u;
|
||||||
@@ -247,6 +253,9 @@ bool TryParseHlslAuthoringResourceLine(
|
|||||||
static const std::regex kConstantBufferPattern(
|
static const std::regex kConstantBufferPattern(
|
||||||
R"(^\s*cbuffer\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*(?:\{\s*)?$)",
|
R"(^\s*cbuffer\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*(?:\{\s*)?$)",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
|
static const std::regex kTexturePattern(
|
||||||
|
R"(^\s*(Texture2D|TextureCube)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
||||||
|
std::regex::ECMAScript);
|
||||||
static const std::regex kStructuredPattern(
|
static const std::regex kStructuredPattern(
|
||||||
R"(^\s*(?:globallycoherent\s+)?(RWStructuredBuffer|StructuredBuffer)\s*<[^;\r\n>]+>\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
R"(^\s*(?:globallycoherent\s+)?(RWStructuredBuffer|StructuredBuffer)\s*<[^;\r\n>]+>\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
@@ -261,6 +270,15 @@ bool TryParseHlslAuthoringResourceLine(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::regex_match(line, match, kTexturePattern) && match.size() >= 3u) {
|
||||||
|
outType =
|
||||||
|
match[1].str() == "TextureCube"
|
||||||
|
? ShaderResourceType::TextureCube
|
||||||
|
: ShaderResourceType::Texture2D;
|
||||||
|
outName = match[2].str().c_str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (std::regex_match(line, match, kStructuredPattern) && match.size() >= 3u) {
|
if (std::regex_match(line, match, kStructuredPattern) && match.size() >= 3u) {
|
||||||
outType =
|
outType =
|
||||||
match[1].str() == "RWStructuredBuffer"
|
match[1].str() == "RWStructuredBuffer"
|
||||||
@@ -282,6 +300,66 @@ bool TryParseHlslAuthoringResourceLine(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* TryGetAuthoringRegisterPrefix(ShaderResourceType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ShaderResourceType::ConstantBuffer:
|
||||||
|
return "b";
|
||||||
|
case ShaderResourceType::Texture2D:
|
||||||
|
case ShaderResourceType::TextureCube:
|
||||||
|
case ShaderResourceType::StructuredBuffer:
|
||||||
|
case ShaderResourceType::RawBuffer:
|
||||||
|
return "t";
|
||||||
|
case ShaderResourceType::RWStructuredBuffer:
|
||||||
|
case ShaderResourceType::RWRawBuffer:
|
||||||
|
return "u";
|
||||||
|
case ShaderResourceType::Sampler:
|
||||||
|
return "s";
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseHlslAuthoringRegisterBinding(
|
||||||
|
const std::string& line,
|
||||||
|
ShaderResourceType type,
|
||||||
|
Core::uint32& outSet,
|
||||||
|
Core::uint32& outBinding) {
|
||||||
|
outSet = 0u;
|
||||||
|
outBinding = 0u;
|
||||||
|
|
||||||
|
const char* expectedPrefix = TryGetAuthoringRegisterPrefix(type);
|
||||||
|
if (expectedPrefix == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::regex kRegisterPattern(
|
||||||
|
R"(register\s*\(\s*([a-zA-Z])\s*([0-9]+)\s*(?:,\s*space\s*([0-9]+)\s*)?\))",
|
||||||
|
std::regex::ECMAScript);
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
if (!std::regex_search(line, match, kRegisterPattern) || match.size() < 3u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string prefix = match[1].str();
|
||||||
|
if (prefix.size() != 1u ||
|
||||||
|
static_cast<char>(std::tolower(static_cast<unsigned char>(prefix[0]))) != expectedPrefix[0]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
outBinding = static_cast<Core::uint32>(std::stoul(match[2].str()));
|
||||||
|
outSet =
|
||||||
|
match.size() >= 4u && match[3].matched
|
||||||
|
? static_cast<Core::uint32>(std::stoul(match[3].str()))
|
||||||
|
: 0u;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AppendScannedAuthoringResourceBindings(
|
void AppendScannedAuthoringResourceBindings(
|
||||||
const Containers::String& sourceText,
|
const Containers::String& sourceText,
|
||||||
Containers::Array<ShaderResourceBindingDesc>& ioBindings) {
|
Containers::Array<ShaderResourceBindingDesc>& ioBindings) {
|
||||||
@@ -309,8 +387,10 @@ void AppendScannedAuthoringResourceBindings(
|
|||||||
ShaderResourceBindingDesc binding = {};
|
ShaderResourceBindingDesc binding = {};
|
||||||
binding.name = resourceName;
|
binding.name = resourceName;
|
||||||
binding.type = resourceType;
|
binding.type = resourceType;
|
||||||
binding.set = ResolveDefaultAuthoringSet(resourceType);
|
if (!TryParseHlslAuthoringRegisterBinding(line, resourceType, binding.set, binding.binding)) {
|
||||||
binding.binding = FindNextBindingInSet(ioBindings, binding.set);
|
binding.set = ResolveDefaultAuthoringSet(resourceType);
|
||||||
|
binding.binding = FindNextBindingInSet(ioBindings, binding.set);
|
||||||
|
}
|
||||||
ioBindings.PushBack(binding);
|
ioBindings.PushBack(binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1147,16 +1147,6 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatUtilitiesShaderUsesCompute
|
|||||||
ResolveBuiltinPassResourceSemantic(*sh),
|
ResolveBuiltinPassResourceSemantic(*sh),
|
||||||
BuiltinPassResourceSemantic::GaussianSplatSHBuffer);
|
BuiltinPassResourceSemantic::GaussianSplatSHBuffer);
|
||||||
|
|
||||||
const ShaderResourceBindingDesc* chunks =
|
|
||||||
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatChunks");
|
|
||||||
ASSERT_NE(chunks, nullptr);
|
|
||||||
EXPECT_EQ(chunks->type, ShaderResourceType::StructuredBuffer);
|
|
||||||
EXPECT_EQ(chunks->set, 2u);
|
|
||||||
EXPECT_EQ(chunks->binding, 4u);
|
|
||||||
EXPECT_EQ(
|
|
||||||
ResolveBuiltinPassResourceSemantic(*chunks),
|
|
||||||
BuiltinPassResourceSemantic::GaussianSplatChunkBuffer);
|
|
||||||
|
|
||||||
const ShaderResourceBindingDesc* sortDistances =
|
const ShaderResourceBindingDesc* sortDistances =
|
||||||
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatSortDistances");
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatSortDistances");
|
||||||
ASSERT_NE(sortDistances, nullptr);
|
ASSERT_NE(sortDistances, nullptr);
|
||||||
@@ -1167,6 +1157,16 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatUtilitiesShaderUsesCompute
|
|||||||
ResolveBuiltinPassResourceSemantic(*sortDistances),
|
ResolveBuiltinPassResourceSemantic(*sortDistances),
|
||||||
BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer);
|
BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* visibleChunks =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatVisibleChunks");
|
||||||
|
ASSERT_NE(visibleChunks, nullptr);
|
||||||
|
EXPECT_EQ(visibleChunks->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(visibleChunks->set, 3u);
|
||||||
|
EXPECT_EQ(visibleChunks->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*visibleChunks),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer);
|
||||||
|
|
||||||
const ShaderResourceBindingDesc* orderBuffer =
|
const ShaderResourceBindingDesc* orderBuffer =
|
||||||
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatOrderBuffer");
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatOrderBuffer");
|
||||||
ASSERT_NE(orderBuffer, nullptr);
|
ASSERT_NE(orderBuffer, nullptr);
|
||||||
@@ -1197,6 +1197,59 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatUtilitiesShaderUsesCompute
|
|||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatMarkVisibleChunksShaderUsesComputeAuthoringContract) {
|
||||||
|
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("GaussianSplatMarkVisibleChunks");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
EXPECT_EQ(pass->resources.Size(), 3u);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* perObject =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatMarkVisibleChunks", "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* chunks =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatMarkVisibleChunks", "GaussianSplatChunks");
|
||||||
|
ASSERT_NE(chunks, nullptr);
|
||||||
|
EXPECT_EQ(chunks->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(chunks->set, 2u);
|
||||||
|
EXPECT_EQ(chunks->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*chunks),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatChunkBuffer);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* visibleChunks =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatMarkVisibleChunks", "GaussianSplatVisibleChunks");
|
||||||
|
ASSERT_NE(visibleChunks, nullptr);
|
||||||
|
EXPECT_EQ(visibleChunks->type, ShaderResourceType::RWStructuredBuffer);
|
||||||
|
EXPECT_EQ(visibleChunks->set, 4u);
|
||||||
|
EXPECT_EQ(visibleChunks->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*visibleChunks),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatVisibleChunkBuffer);
|
||||||
|
|
||||||
|
const ShaderStageVariant* computeVariant = shader->FindVariant(
|
||||||
|
"GaussianSplatMarkVisibleChunks",
|
||||||
|
XCEngine::Resources::ShaderType::Compute,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(computeVariant, nullptr);
|
||||||
|
EXPECT_EQ(computeVariant->entryPoint, "GaussianSplatMarkVisibleChunksCS");
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatBitonicSortShaderUsesComputeAuthoringContract) {
|
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatBitonicSortShaderUsesComputeAuthoringContract) {
|
||||||
ShaderLoader loader;
|
ShaderLoader loader;
|
||||||
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
@@ -1271,7 +1324,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatSHBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatSHBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatChunkBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatVisibleChunkBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatSortDistanceBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatSortDistanceBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatViewDataBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatViewDataBuffer.IsValid());
|
||||||
@@ -1283,8 +1336,8 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 2u);
|
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatSHBuffer.set, 2u);
|
EXPECT_EQ(plan.gaussianSplatSHBuffer.set, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatSHBuffer.binding, 3u);
|
EXPECT_EQ(plan.gaussianSplatSHBuffer.binding, 3u);
|
||||||
EXPECT_EQ(plan.gaussianSplatChunkBuffer.set, 2u);
|
EXPECT_EQ(plan.gaussianSplatVisibleChunkBuffer.set, 3u);
|
||||||
EXPECT_EQ(plan.gaussianSplatChunkBuffer.binding, 4u);
|
EXPECT_EQ(plan.gaussianSplatVisibleChunkBuffer.binding, 0u);
|
||||||
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.set, 4u);
|
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.set, 4u);
|
||||||
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.binding, 0u);
|
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.binding, 0u);
|
||||||
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 4u);
|
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 4u);
|
||||||
@@ -1302,11 +1355,12 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatSHBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatSHBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatChunkBuffer);
|
EXPECT_TRUE(setLayouts[3].usesGaussianSplatVisibleChunkBuffer);
|
||||||
EXPECT_TRUE(setLayouts[4].usesGaussianSplatSortDistanceBuffer);
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatSortDistanceBuffer);
|
||||||
EXPECT_TRUE(setLayouts[4].usesGaussianSplatOrderBuffer);
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatOrderBuffer);
|
||||||
EXPECT_TRUE(setLayouts[4].usesGaussianSplatViewDataBuffer);
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatViewDataBuffer);
|
||||||
ASSERT_EQ(setLayouts[2].bindings.size(), 5u);
|
ASSERT_EQ(setLayouts[2].bindings.size(), 4u);
|
||||||
|
ASSERT_EQ(setLayouts[3].bindings.size(), 1u);
|
||||||
ASSERT_EQ(setLayouts[4].bindings.size(), 3u);
|
ASSERT_EQ(setLayouts[4].bindings.size(), 3u);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
||||||
@@ -1320,9 +1374,6 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
static_cast<DescriptorType>(setLayouts[2].bindings[3].type),
|
static_cast<DescriptorType>(setLayouts[2].bindings[3].type),
|
||||||
DescriptorType::SRV);
|
DescriptorType::SRV);
|
||||||
EXPECT_EQ(
|
|
||||||
static_cast<DescriptorType>(setLayouts[2].bindings[4].type),
|
|
||||||
DescriptorType::SRV);
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
|
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
|
||||||
DescriptorType::UAV);
|
DescriptorType::UAV);
|
||||||
@@ -1345,7 +1396,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
setLayouts[2].bindings[3].resourceDimension,
|
setLayouts[2].bindings[3].resourceDimension,
|
||||||
ResourceViewDimension::StructuredBuffer);
|
ResourceViewDimension::StructuredBuffer);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
setLayouts[2].bindings[4].resourceDimension,
|
setLayouts[3].bindings[0].resourceDimension,
|
||||||
ResourceViewDimension::StructuredBuffer);
|
ResourceViewDimension::StructuredBuffer);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
setLayouts[4].bindings[0].resourceDimension,
|
setLayouts[4].bindings[0].resourceDimension,
|
||||||
@@ -1360,6 +1411,51 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatMarkVisibleChunksContract) {
|
||||||
|
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("GaussianSplatMarkVisibleChunks");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
|
||||||
|
BuiltinPassResourceBindingPlan plan = {};
|
||||||
|
String error;
|
||||||
|
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(plan.bindings.Size(), 3u);
|
||||||
|
EXPECT_TRUE(plan.perObject.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatChunkBuffer.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatVisibleChunkBuffer.IsValid());
|
||||||
|
EXPECT_EQ(plan.perObject.set, 0u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatChunkBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatChunkBuffer.binding, 0u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatVisibleChunkBuffer.set, 4u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatVisibleChunkBuffer.binding, 0u);
|
||||||
|
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].usesGaussianSplatChunkBuffer);
|
||||||
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatVisibleChunkBuffer);
|
||||||
|
ASSERT_EQ(setLayouts[2].bindings.size(), 1u);
|
||||||
|
ASSERT_EQ(setLayouts[4].bindings.size(), 1u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
|
||||||
|
DescriptorType::UAV);
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatBitonicSortContract) {
|
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatBitonicSortContract) {
|
||||||
ShaderLoader loader;
|
ShaderLoader loader;
|
||||||
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
@@ -1444,7 +1540,8 @@ TEST(BuiltinForwardPipeline_Test, OpenGLPipelineLayoutUsesUnifiedStorageBufferBi
|
|||||||
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 1u), 1u);
|
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 1u), 1u);
|
||||||
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 2u), 2u);
|
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 2u), 2u);
|
||||||
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 3u), 3u);
|
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 3u), 3u);
|
||||||
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 4u), 4u);
|
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(2u, 4u), UINT32_MAX);
|
||||||
|
EXPECT_EQ(pipelineLayout.GetShaderResourceBindingPoint(3u, 0u), 4u);
|
||||||
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 0u), 5u);
|
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 0u), 5u);
|
||||||
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 1u), 6u);
|
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 1u), 6u);
|
||||||
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 2u), 7u);
|
EXPECT_EQ(pipelineLayout.GetUnorderedAccessBindingPoint(4u, 2u), 7u);
|
||||||
@@ -1502,7 +1599,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesGaussianSplatU
|
|||||||
"register(t3)"));
|
"register(t3)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
d3d12Source,
|
d3d12Source,
|
||||||
"StructuredBuffer<GaussianSplatChunkData> GaussianSplatChunks",
|
"StructuredBuffer<uint> GaussianSplatVisibleChunks",
|
||||||
"register(t4)"));
|
"register(t4)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
d3d12Source,
|
d3d12Source,
|
||||||
@@ -1554,8 +1651,8 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesGaussianSplatU
|
|||||||
"register(t3, space2)"));
|
"register(t3, space2)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
vulkanSource,
|
vulkanSource,
|
||||||
"StructuredBuffer<GaussianSplatChunkData> GaussianSplatChunks",
|
"StructuredBuffer<uint> GaussianSplatVisibleChunks",
|
||||||
"register(t4, space2)"));
|
"register(t0, space3)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
vulkanSource,
|
vulkanSource,
|
||||||
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
|
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
|
||||||
@@ -1625,7 +1722,7 @@ TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesGaussianSplatUtilitiesC
|
|||||||
glslSource.find("layout(binding = 3, std430) readonly buffer type_StructuredBuffer_GaussianSplatSHData"),
|
glslSource.find("layout(binding = 3, std430) readonly buffer type_StructuredBuffer_GaussianSplatSHData"),
|
||||||
std::string::npos);
|
std::string::npos);
|
||||||
EXPECT_NE(
|
EXPECT_NE(
|
||||||
glslSource.find("layout(binding = 4, std430) readonly buffer type_StructuredBuffer_GaussianSplatChunkData"),
|
glslSource.find("layout(binding = 4, std430) readonly buffer"),
|
||||||
std::string::npos);
|
std::string::npos);
|
||||||
EXPECT_NE(
|
EXPECT_NE(
|
||||||
glslSource.find("layout(binding = 5, std430) buffer type_RWStructuredBuffer_uint"),
|
glslSource.find("layout(binding = 5, std430) buffer type_RWStructuredBuffer_uint"),
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ GaussianSplat* CreateTestGaussianSplat(const char* path, XCEngine::Core::uint32
|
|||||||
|
|
||||||
GaussianSplatMetadata metadata = {};
|
GaussianSplatMetadata metadata = {};
|
||||||
metadata.splatCount = splatCount;
|
metadata.splatCount = splatCount;
|
||||||
|
metadata.chunkCount = (splatCount + 255u) / 256u;
|
||||||
|
|
||||||
XCEngine::Containers::Array<GaussianSplatSection> sections;
|
XCEngine::Containers::Array<GaussianSplatSection> sections;
|
||||||
sections.Resize(1);
|
sections.Resize(1);
|
||||||
@@ -336,21 +337,24 @@ TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetAllocatesAndReusesS
|
|||||||
EXPECT_EQ(workingSet->renderer, renderer);
|
EXPECT_EQ(workingSet->renderer, renderer);
|
||||||
EXPECT_EQ(workingSet->splatCapacity, 8u);
|
EXPECT_EQ(workingSet->splatCapacity, 8u);
|
||||||
EXPECT_EQ(workingSet->sortCapacity, 8u);
|
EXPECT_EQ(workingSet->sortCapacity, 8u);
|
||||||
|
EXPECT_EQ(workingSet->chunkCapacity, 1u);
|
||||||
EXPECT_EQ(workingSet->sortDistances.elementStride, sizeof(float));
|
EXPECT_EQ(workingSet->sortDistances.elementStride, sizeof(float));
|
||||||
EXPECT_EQ(workingSet->orderIndices.elementStride, sizeof(XCEngine::Core::uint32));
|
EXPECT_EQ(workingSet->orderIndices.elementStride, sizeof(XCEngine::Core::uint32));
|
||||||
|
EXPECT_EQ(workingSet->visibleChunks.elementStride, sizeof(XCEngine::Core::uint32));
|
||||||
EXPECT_EQ(workingSet->viewData.elementStride, sizeof(GaussianSplatViewData));
|
EXPECT_EQ(workingSet->viewData.elementStride, sizeof(GaussianSplatViewData));
|
||||||
EXPECT_EQ(workingSet->sortDistances.shaderResourceView->GetDimension(), XCEngine::RHI::ResourceViewDimension::StructuredBuffer);
|
EXPECT_EQ(workingSet->sortDistances.shaderResourceView->GetDimension(), XCEngine::RHI::ResourceViewDimension::StructuredBuffer);
|
||||||
EXPECT_EQ(workingSet->sortDistances.unorderedAccessView->GetViewType(), XCEngine::RHI::ResourceViewType::UnorderedAccess);
|
EXPECT_EQ(workingSet->sortDistances.unorderedAccessView->GetViewType(), XCEngine::RHI::ResourceViewType::UnorderedAccess);
|
||||||
EXPECT_EQ(state->createBufferCalls, 3);
|
EXPECT_EQ(workingSet->visibleChunks.elementCount, 1u);
|
||||||
EXPECT_EQ(state->createBufferShaderViewCalls, 3);
|
EXPECT_EQ(state->createBufferCalls, 4);
|
||||||
EXPECT_EQ(state->createBufferUavCalls, 3);
|
EXPECT_EQ(state->createBufferShaderViewCalls, 4);
|
||||||
|
EXPECT_EQ(state->createBufferUavCalls, 4);
|
||||||
|
|
||||||
BuiltinGaussianSplatPassResources::WorkingSet* reusedWorkingSet = nullptr;
|
BuiltinGaussianSplatPassResources::WorkingSet* reusedWorkingSet = nullptr;
|
||||||
ASSERT_TRUE(resources.EnsureWorkingSet(&device, item, reusedWorkingSet));
|
ASSERT_TRUE(resources.EnsureWorkingSet(&device, item, reusedWorkingSet));
|
||||||
EXPECT_EQ(reusedWorkingSet, workingSet);
|
EXPECT_EQ(reusedWorkingSet, workingSet);
|
||||||
EXPECT_EQ(state->createBufferCalls, 3);
|
EXPECT_EQ(state->createBufferCalls, 4);
|
||||||
EXPECT_EQ(state->createBufferShaderViewCalls, 3);
|
EXPECT_EQ(state->createBufferShaderViewCalls, 4);
|
||||||
EXPECT_EQ(state->createBufferUavCalls, 3);
|
EXPECT_EQ(state->createBufferUavCalls, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetKeepsPerRendererIsolationAndRecreatesOnCapacityGrowth) {
|
TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetKeepsPerRendererIsolationAndRecreatesOnCapacityGrowth) {
|
||||||
@@ -376,7 +380,7 @@ TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetKeepsPerRendererIso
|
|||||||
ASSERT_NE(secondWorkingSet, nullptr);
|
ASSERT_NE(secondWorkingSet, nullptr);
|
||||||
EXPECT_NE(firstWorkingSet, secondWorkingSet);
|
EXPECT_NE(firstWorkingSet, secondWorkingSet);
|
||||||
EXPECT_EQ(resources.GetWorkingSetCount(), 2u);
|
EXPECT_EQ(resources.GetWorkingSetCount(), 2u);
|
||||||
EXPECT_EQ(state->createBufferCalls, 6);
|
EXPECT_EQ(state->createBufferCalls, 8);
|
||||||
|
|
||||||
std::unique_ptr<GaussianSplat> grownGaussianSplat(CreateTestGaussianSplat("GaussianSplats/first_grown.xcgsplat", 12u));
|
std::unique_ptr<GaussianSplat> grownGaussianSplat(CreateTestGaussianSplat("GaussianSplats/first_grown.xcgsplat", 12u));
|
||||||
firstItem.gaussianSplat = grownGaussianSplat.get();
|
firstItem.gaussianSplat = grownGaussianSplat.get();
|
||||||
@@ -387,10 +391,11 @@ TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetKeepsPerRendererIso
|
|||||||
EXPECT_EQ(grownWorkingSet, resources.FindWorkingSet(firstRenderer));
|
EXPECT_EQ(grownWorkingSet, resources.FindWorkingSet(firstRenderer));
|
||||||
EXPECT_EQ(grownWorkingSet->splatCapacity, 12u);
|
EXPECT_EQ(grownWorkingSet->splatCapacity, 12u);
|
||||||
EXPECT_EQ(grownWorkingSet->sortCapacity, 16u);
|
EXPECT_EQ(grownWorkingSet->sortCapacity, 16u);
|
||||||
|
EXPECT_EQ(grownWorkingSet->chunkCapacity, 1u);
|
||||||
EXPECT_EQ(resources.GetWorkingSetCount(), 2u);
|
EXPECT_EQ(resources.GetWorkingSetCount(), 2u);
|
||||||
EXPECT_EQ(state->createBufferCalls, 9);
|
EXPECT_EQ(state->createBufferCalls, 12);
|
||||||
EXPECT_GE(state->bufferShutdownCalls, 3);
|
EXPECT_GE(state->bufferShutdownCalls, 4);
|
||||||
EXPECT_GE(state->bufferDestroyCalls, 3);
|
EXPECT_GE(state->bufferDestroyCalls, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetRoundsSortBuffersUpToNextPowerOfTwo) {
|
TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetRoundsSortBuffersUpToNextPowerOfTwo) {
|
||||||
@@ -408,8 +413,10 @@ TEST(BuiltinGaussianSplatPassResources_Test, EnsureWorkingSetRoundsSortBuffersUp
|
|||||||
ASSERT_NE(workingSet, nullptr);
|
ASSERT_NE(workingSet, nullptr);
|
||||||
EXPECT_EQ(workingSet->splatCapacity, 9u);
|
EXPECT_EQ(workingSet->splatCapacity, 9u);
|
||||||
EXPECT_EQ(workingSet->sortCapacity, 16u);
|
EXPECT_EQ(workingSet->sortCapacity, 16u);
|
||||||
|
EXPECT_EQ(workingSet->chunkCapacity, 1u);
|
||||||
EXPECT_EQ(workingSet->sortDistances.elementCount, 16u);
|
EXPECT_EQ(workingSet->sortDistances.elementCount, 16u);
|
||||||
EXPECT_EQ(workingSet->orderIndices.elementCount, 16u);
|
EXPECT_EQ(workingSet->orderIndices.elementCount, 16u);
|
||||||
|
EXPECT_EQ(workingSet->visibleChunks.elementCount, 1u);
|
||||||
EXPECT_EQ(workingSet->viewData.elementCount, 9u);
|
EXPECT_EQ(workingSet->viewData.elementCount, 9u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user