#include "Rendering/Passes/BuiltinGaussianSplatPass.h" #include "Components/GameObject.h" #include "Debug/Logger.h" #include "RHI/RHICommandList.h" #include "RHI/RHIDevice.h" #include "Rendering/Builtin/BuiltinPassLayoutUtils.h" #include "Rendering/FrameData/RenderSceneData.h" #include "Rendering/FrameData/VisibleGaussianSplatItem.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Internal/ShaderVariantUtils.h" #include "Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.h" #include "Rendering/RenderSurface.h" #include "Resources/BuiltinResources.h" #include "Resources/GaussianSplat/GaussianSplat.h" #include "Resources/Material/Material.h" #include "Resources/Shader/Shader.h" namespace XCEngine { namespace Rendering { namespace Passes { namespace { Resources::ShaderKeywordSet ResolvePassKeywordSet( const RenderSceneData& sceneData, const Resources::Material* material) { return Resources::CombineShaderKeywordSets( sceneData.globalShaderKeywords, material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet()); } const Resources::ShaderPass* FindCompatibleGaussianSplatPass( const Resources::Shader& shader, const RenderSceneData& sceneData, const Resources::Material* material, Resources::ShaderBackend backend) { const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::GaussianSplat) && ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( shader, shaderPass.name, backend, keywordSet)) { return &shaderPass; } } return nullptr; } const Resources::ShaderPass* FindCompatibleComputePass( const Resources::Shader& shader, const Containers::String& passName, const Resources::ShaderKeywordSet& keywordSet, Resources::ShaderBackend backend) { const Resources::ShaderPass* shaderPass = shader.FindPass(passName); if (shaderPass == nullptr) { return nullptr; } return shader.FindVariant(passName, Resources::ShaderType::Compute, backend, keywordSet) != nullptr ? shaderPass : nullptr; } RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout, const Resources::Shader& shader, const Resources::ShaderPass& shaderPass, const Containers::String& passName, const Resources::ShaderKeywordSet& keywordSet, const Resources::Material* material, const RenderSurface& surface) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); ::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(surface, pipelineDesc); pipelineDesc.depthStencilFormat = static_cast(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface)); ApplyResolvedRenderState(&shaderPass, material, pipelineDesc); const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); if (const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet)) { ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( shader.GetPath(), shaderPass, backend, *vertexVariant, pipelineDesc.vertexShader); } if (const Resources::ShaderStageVariant* fragmentVariant = shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet)) { ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( shader.GetPath(), shaderPass, backend, *fragmentVariant, pipelineDesc.fragmentShader); } return pipelineDesc; } RHI::ComputePipelineDesc CreateComputePipelineDesc( RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout, const Resources::Shader& shader, const Resources::ShaderPass& shaderPass, const Containers::String& passName, const Resources::ShaderKeywordSet& keywordSet) { RHI::ComputePipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); if (const Resources::ShaderStageVariant* computeVariant = shader.FindVariant(passName, Resources::ShaderType::Compute, backend, keywordSet)) { ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( shader.GetPath(), shaderPass, backend, *computeVariant, pipelineDesc.computeShader); } return pipelineDesc; } const RHI::DescriptorSetLayoutBinding* FindSetLayoutBinding( const BuiltinPassSetLayoutMetadata& setLayout, Core::uint32 binding) { for (const RHI::DescriptorSetLayoutBinding& layoutBinding : setLayout.bindings) { if (layoutBinding.binding == binding) { return &layoutBinding; } } return nullptr; } RHI::RHIResourceView* ResolveWorkingSetView( const Internal::BuiltinGaussianSplatPassResources::CachedBufferView& bufferView, const RHI::DescriptorSetLayoutBinding* layoutBinding) { if (layoutBinding == nullptr) { return nullptr; } switch (static_cast(layoutBinding->type)) { case RHI::DescriptorType::UAV: return bufferView.unorderedAccessView; case RHI::DescriptorType::SRV: return bufferView.shaderResourceView; default: return nullptr; } } bool TransitionWorkingSetBuffer( RHI::RHICommandList* commandList, Internal::BuiltinGaussianSplatPassResources::CachedBufferView& bufferView, RHI::ResourceStates targetState) { if (commandList == nullptr || bufferView.currentState == targetState) { return true; } RHI::RHIResourceView* resourceView = targetState == RHI::ResourceStates::UnorderedAccess ? bufferView.unorderedAccessView : bufferView.shaderResourceView; if (resourceView == nullptr) { return false; } commandList->TransitionBarrier(resourceView, bufferView.currentState, targetState); bufferView.currentState = targetState; return true; } template void BindDescriptorSetRanges( Core::uint32 firstDescriptorSet, std::vector& descriptorSets, BindFn&& bindFn) { const Core::uint32 descriptorSetCount = static_cast(descriptorSets.size()); Core::uint32 rangeStart = 0u; while (rangeStart < descriptorSetCount) { while (rangeStart < descriptorSetCount && descriptorSets[rangeStart] == nullptr) { ++rangeStart; } if (rangeStart >= descriptorSetCount) { break; } Core::uint32 rangeCount = 0u; while ((rangeStart + rangeCount) < descriptorSetCount && descriptorSets[rangeStart + rangeCount] != nullptr) { ++rangeCount; } bindFn( firstDescriptorSet + rangeStart, rangeCount, descriptorSets.data() + rangeStart); rangeStart += rangeCount; } } } // namespace BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() { Shutdown(); } const char* BuiltinGaussianSplatPass::GetName() const { return "BuiltinGaussianSplatPass"; } bool BuiltinGaussianSplatPass::Initialize(const RenderContext& context) { return EnsureInitialized(context); } bool BuiltinGaussianSplatPass::IsActive(const RenderSceneData& sceneData) const { return !sceneData.visibleGaussianSplats.empty(); } bool BuiltinGaussianSplatPass::Prepare( const RenderContext& context, const RenderSceneData& sceneData) { return PrepareGaussianSplatResources(context, sceneData); } 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; } for (const VisibleGaussianSplatItem& visibleGaussianSplat : sceneData.visibleGaussianSplats) { if (visibleGaussianSplat.gaussianSplat == nullptr || !visibleGaussianSplat.gaussianSplat->IsValid()) { continue; } const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat = m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat); if (cachedGaussianSplat == nullptr || cachedGaussianSplat->positions.shaderResourceView == nullptr || cachedGaussianSplat->other.shaderResourceView == nullptr || cachedGaussianSplat->color.shaderResourceView == nullptr || cachedGaussianSplat->sh.shaderResourceView == nullptr || cachedGaussianSplat->chunks.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; } Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr; if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) || workingSet == nullptr || workingSet->visibleChunks.shaderResourceView == nullptr || workingSet->visibleChunks.unorderedAccessView == nullptr || workingSet->sortDistances.unorderedAccessView == nullptr || workingSet->orderIndices.shaderResourceView == 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; } } return true; } bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid()) { return false; } if (context.sceneData.visibleGaussianSplats.empty()) { return true; } const std::vector& colorAttachments = context.surface.GetColorAttachments(); if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(context.surface) || colorAttachments.empty() || colorAttachments[0] == nullptr) { return false; } const Math::RectInt renderArea = context.surface.GetRenderArea(); if (renderArea.width <= 0 || renderArea.height <= 0) { return false; } if (!PrepareGaussianSplatResources(context.renderContext, context.sceneData)) { return false; } RHI::RHICommandList* commandList = context.renderContext.commandList; RHI::RHIResourceView* renderTarget = colorAttachments[0]; commandList->SetRenderTargets(1, &renderTarget, context.surface.GetDepthAttachment()); const RHI::Viewport viewport = { static_cast(renderArea.x), static_cast(renderArea.y), static_cast(renderArea.width), static_cast(renderArea.height), 0.0f, 1.0f }; const RHI::Rect scissorRect = { renderArea.x, renderArea.y, renderArea.x + renderArea.width, renderArea.y + renderArea.height }; commandList->SetViewport(viewport); commandList->SetScissorRect(scissorRect); commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) { if (!MarkVisibleGaussianSplatChunks( context.renderContext, context.sceneData, visibleGaussianSplat)) { return false; } if (!PrepareVisibleGaussianSplat( context.renderContext, context.sceneData, visibleGaussianSplat)) { return false; } if (!SortVisibleGaussianSplat( context.renderContext, context.sceneData, visibleGaussianSplat)) { return false; } if (!DrawVisibleGaussianSplat( context.renderContext, context.surface, context.sceneData, visibleGaussianSplat)) { return false; } } return true; } void BuiltinGaussianSplatPass::Shutdown() { DestroyResources(); } bool BuiltinGaussianSplatPass::EnsureInitialized(const RenderContext& context) { if (!context.IsValid()) { return false; } if (m_device == context.device && m_backendType == context.backendType && m_builtinGaussianSplatShader.IsValid() && m_builtinGaussianSplatMaterial != nullptr) { return true; } DestroyResources(); return CreateResources(context); } bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) { m_device = context.device; m_backendType = context.backendType; m_builtinGaussianSplatShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinGaussianSplatShaderPath()); if (!m_builtinGaussianSplatShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinGaussianSplatPass failed to load builtin gaussian splat shader resource"); DestroyResources(); return false; } m_builtinGaussianSplatUtilitiesShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinGaussianSplatUtilitiesShaderPath()); if (!m_builtinGaussianSplatUtilitiesShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinGaussianSplatPass failed to load builtin gaussian splat utilities shader resource"); DestroyResources(); return false; } m_builtinGaussianSplatMaterial = std::make_unique(); Resources::IResource::ConstructParams params = {}; params.name = "BuiltinGaussianSplatMaterial"; params.path = "builtin://materials/gaussian-splat-default"; params.guid = Resources::ResourceGUID::Generate(params.path); m_builtinGaussianSplatMaterial->Initialize(params); m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader); m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent); m_passResources = new Internal::BuiltinGaussianSplatPassResources(); return true; } void BuiltinGaussianSplatPass::DestroyResources() { if (m_passResources != nullptr) { m_passResources->Shutdown(); delete m_passResources; m_passResources = nullptr; } m_resourceCache.Shutdown(); for (auto& pipelinePair : m_pipelineStates) { if (pipelinePair.second != nullptr) { pipelinePair.second->Shutdown(); delete pipelinePair.second; } } m_pipelineStates.clear(); for (auto& pipelinePair : m_computePipelineStates) { if (pipelinePair.second != nullptr) { pipelinePair.second->Shutdown(); delete pipelinePair.second; } } m_computePipelineStates.clear(); for (auto& descriptorSetPair : m_dynamicDescriptorSets) { DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet); } m_dynamicDescriptorSets.clear(); for (auto& passLayoutPair : m_passResourceLayouts) { DestroyPassResourceLayout(passLayoutPair.second); } m_passResourceLayouts.clear(); m_builtinGaussianSplatMaterial.reset(); m_builtinGaussianSplatUtilitiesShader.Reset(); m_builtinGaussianSplatShader.Reset(); m_device = nullptr; m_backendType = RHI::RHIType::D3D12; } const Resources::Material* BuiltinGaussianSplatPass::ResolveGaussianSplatMaterial( const VisibleGaussianSplatItem& visibleGaussianSplat) const { if (visibleGaussianSplat.material != nullptr && visibleGaussianSplat.material->GetShader() != nullptr && MatchesBuiltinPass(visibleGaussianSplat.material, BuiltinMaterialPass::GaussianSplat)) { return visibleGaussianSplat.material; } return m_builtinGaussianSplatMaterial.get(); } BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveGaussianSplatShaderPass( const RenderSceneData& sceneData, const Resources::Material* material) const { ResolvedShaderPass resolved = {}; const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType); if (material != nullptr && material->GetShader() != nullptr) { const Resources::Shader* shader = material->GetShader(); if (const Resources::ShaderPass* shaderPass = FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) { resolved.shader = shader; resolved.pass = shaderPass; resolved.passName = shaderPass->name; return resolved; } } if (m_builtinGaussianSplatShader.IsValid()) { const Resources::Shader* shader = m_builtinGaussianSplatShader.Get(); if (const Resources::ShaderPass* shaderPass = FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) { resolved.shader = shader; resolved.pass = shaderPass; resolved.passName = shaderPass->name; } } return resolved; } BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolvePrepareOrderShaderPass( 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("GaussianSplatPrepareOrder"); 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::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( 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("GaussianSplatBitonicSort"); if (const Resources::ShaderPass* shaderPass = FindCompatibleComputePass(*shader, passName, sceneData.globalShaderKeywords, backend)) { resolved.shader = shader; resolved.pass = shaderPass; resolved.passName = passName; } return resolved; } BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout( const RenderContext& context, const ResolvedShaderPass& resolvedShaderPass, PassLayoutUsage usage) { if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return nullptr; } PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; passLayoutKey.passName = resolvedShaderPass.passName; const auto existing = m_passResourceLayouts.find(passLayoutKey); if (existing != m_passResourceLayouts.end()) { return &existing->second; } PassResourceLayout passLayout = {}; auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* { Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message); DestroyPassResourceLayout(passLayout); return nullptr; }; BuiltinPassResourceBindingPlan bindingPlan = {}; Containers::String bindingPlanError; if (!TryBuildBuiltinPassResourceBindingPlan(*resolvedShaderPass.pass, bindingPlan, &bindingPlanError)) { const Containers::String contextualError = Containers::String("BuiltinGaussianSplatPass failed to resolve pass resource bindings for shader='") + resolvedShaderPass.shader->GetPath() + "', pass='" + resolvedShaderPass.passName + "': " + bindingPlanError + ". Bindings: " + DescribeShaderResourceBindings(resolvedShaderPass.pass->resources); return failLayout(contextualError.CStr()); } Containers::String setLayoutError; if (!TryBuildBuiltinPassSetLayouts(bindingPlan, passLayout.setLayouts, &setLayoutError)) { return failLayout(setLayoutError.CStr()); } passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet; passLayout.descriptorSetCount = bindingPlan.descriptorSetCount; passLayout.perObject = bindingPlan.perObject; passLayout.material = bindingPlan.material; passLayout.gaussianSplatSortDistanceBuffer = bindingPlan.gaussianSplatSortDistanceBuffer; passLayout.gaussianSplatOrderBuffer = bindingPlan.gaussianSplatOrderBuffer; passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer; passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer; passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer; passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer; passLayout.gaussianSplatChunkBuffer = bindingPlan.gaussianSplatChunkBuffer; passLayout.gaussianSplatVisibleChunkBuffer = bindingPlan.gaussianSplatVisibleChunkBuffer; passLayout.gaussianSplatViewDataBuffer = bindingPlan.gaussianSplatViewDataBuffer; if (!passLayout.perObject.IsValid()) { return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding"); } if (usage == PassLayoutUsage::Draw) { if (!passLayout.material.IsValid()) { return failLayout("BuiltinGaussianSplatPass requires a Material resource binding"); } if (!passLayout.gaussianSplatOrderBuffer.IsValid() || !passLayout.gaussianSplatViewDataBuffer.IsValid()) { return failLayout( "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) { if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() || !passLayout.gaussianSplatOrderBuffer.IsValid() || !passLayout.gaussianSplatPositionBuffer.IsValid() || !passLayout.gaussianSplatOtherBuffer.IsValid() || !passLayout.gaussianSplatColorBuffer.IsValid() || !passLayout.gaussianSplatSHBuffer.IsValid() || !passLayout.gaussianSplatVisibleChunkBuffer.IsValid() || !passLayout.gaussianSplatViewDataBuffer.IsValid()) { return failLayout( "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) { if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() || !passLayout.gaussianSplatOrderBuffer.IsValid()) { return failLayout( "BuiltinGaussianSplatPass bitonic-sort pass requires sort distance and order gaussian splat buffer bindings"); } } std::vector nativeSetLayouts(passLayout.setLayouts.size()); for (size_t setIndex = 0; setIndex < passLayout.setLayouts.size(); ++setIndex) { nativeSetLayouts[setIndex] = passLayout.setLayouts[setIndex].layout; } RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data(); pipelineLayoutDesc.setLayoutCount = static_cast(nativeSetLayouts.size()); passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc); if (passLayout.pipelineLayout == nullptr) { return failLayout("BuiltinGaussianSplatPass failed to create pipeline layout from shader pass resources"); } const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout); PassResourceLayout& storedPassLayout = result.first->second; RefreshBuiltinPassSetLayouts(storedPassLayout.setLayouts); return &storedPassLayout; } RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState( const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData, const Resources::Material* material) { const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return nullptr; } PassResourceLayout* passLayout = GetOrCreatePassResourceLayout( context, resolvedShaderPass, PassLayoutUsage::Draw); if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) { return nullptr; } PipelineStateKey pipelineKey = {}; pipelineKey.renderState = BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material)); pipelineKey.shader = resolvedShaderPass.shader; pipelineKey.passName = resolvedShaderPass.passName; pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet); pipelineKey.renderTargetCount = ::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) ? 1u : 0u; pipelineKey.renderTargetFormat = static_cast(::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u)); pipelineKey.depthStencilFormat = static_cast(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface)); pipelineKey.sampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); pipelineKey.sampleQuality = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface); const auto existing = m_pipelineStates.find(pipelineKey); if (existing != m_pipelineStates.end()) { return existing->second; } const RHI::GraphicsPipelineDesc pipelineDesc = CreatePipelineDesc( context.backendType, passLayout->pipelineLayout, *resolvedShaderPass.shader, *resolvedShaderPass.pass, resolvedShaderPass.passName, keywordSet, material, surface); RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc); if (pipelineState == nullptr || !pipelineState->IsValid()) { if (pipelineState != nullptr) { pipelineState->Shutdown(); delete pipelineState; } return nullptr; } m_pipelineStates.emplace(pipelineKey, pipelineState); return pipelineState; } RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreateComputePipelineState( const RenderContext& context, const ResolvedShaderPass& resolvedShaderPass, PassLayoutUsage usage, const Resources::ShaderKeywordSet& keywordSet) { if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return nullptr; } PassResourceLayout* passLayout = GetOrCreatePassResourceLayout( context, resolvedShaderPass, usage); if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) { return nullptr; } ComputePipelineKey pipelineKey = {}; pipelineKey.shader = resolvedShaderPass.shader; pipelineKey.passName = resolvedShaderPass.passName; pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet); const auto existing = m_computePipelineStates.find(pipelineKey); if (existing != m_computePipelineStates.end()) { return existing->second; } const RHI::ComputePipelineDesc pipelineDesc = CreateComputePipelineDesc( context.backendType, passLayout->pipelineLayout, *resolvedShaderPass.shader, *resolvedShaderPass.pass, resolvedShaderPass.passName, keywordSet); 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; } return nullptr; } m_computePipelineStates.emplace(pipelineKey, pipelineState); return pipelineState; } bool BuiltinGaussianSplatPass::CreateOwnedDescriptorSet( const BuiltinPassSetLayoutMetadata& setLayout, OwnedDescriptorSet& descriptorSet) { RHI::DescriptorPoolDesc poolDesc = {}; poolDesc.type = setLayout.heapType; poolDesc.descriptorCount = CountBuiltinPassHeapDescriptors(setLayout.heapType, setLayout.bindings); poolDesc.shaderVisible = setLayout.shaderVisible; descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc); if (descriptorSet.pool == nullptr) { return false; } descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout); if (descriptorSet.set == nullptr) { DestroyOwnedDescriptorSet(descriptorSet); return false; } return true; } BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCreateDynamicDescriptorSet( const PassLayoutKey& passLayoutKey, const PassResourceLayout& passLayout, const BuiltinPassSetLayoutMetadata& setLayout, Core::uint32 setIndex, Core::uint64 objectId, const Components::GaussianSplatRendererComponent* gaussianSplatRenderer, const Resources::Material* material, const Resources::GaussianSplat* gaussianSplat, const MaterialConstantPayloadView& materialConstants, const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat, RHI::RHIResourceView* sortDistanceView, RHI::RHIResourceView* orderView, RHI::RHIResourceView* visibleChunkView, RHI::RHIResourceView* viewDataView) { DynamicDescriptorSetKey key = {}; key.passLayout = passLayoutKey; key.setIndex = setIndex; key.objectId = objectId; key.gaussianSplatRenderer = gaussianSplatRenderer; key.material = material; key.gaussianSplat = gaussianSplat; 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; } } const Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = (gaussianSplatRenderer != nullptr && m_passResources != nullptr) ? m_passResources->FindWorkingSet(gaussianSplatRenderer) : nullptr; RHI::RHIResourceView* resolvedSortDistanceView = sortDistanceView; RHI::RHIResourceView* resolvedOrderView = orderView; RHI::RHIResourceView* resolvedVisibleChunkView = visibleChunkView; RHI::RHIResourceView* resolvedViewDataView = viewDataView; if (setLayout.usesGaussianSplatSortDistanceBuffer && workingSet != nullptr) { if (const RHI::DescriptorSetLayoutBinding* layoutBinding = FindSetLayoutBinding(setLayout, passLayout.gaussianSplatSortDistanceBuffer.binding)) { if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->sortDistances, layoutBinding)) { resolvedSortDistanceView = view; } } } if (setLayout.usesGaussianSplatOrderBuffer && workingSet != nullptr) { if (const RHI::DescriptorSetLayoutBinding* layoutBinding = FindSetLayoutBinding(setLayout, passLayout.gaussianSplatOrderBuffer.binding)) { if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->orderIndices, layoutBinding)) { resolvedOrderView = view; } } } if (setLayout.usesGaussianSplatViewDataBuffer && workingSet != nullptr) { if (const RHI::DescriptorSetLayoutBinding* layoutBinding = FindSetLayoutBinding(setLayout, passLayout.gaussianSplatViewDataBuffer.binding)) { if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->viewData, layoutBinding)) { resolvedViewDataView = view; } } } 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; if (setLayout.usesMaterial) { if (!passLayout.material.IsValid() || passLayout.material.set != setIndex || !materialConstants.IsValid()) { return nullptr; } if (cachedDescriptorSet.materialVersion != materialVersion) { cachedDescriptorSet.descriptorSet.set->WriteConstant( passLayout.material.binding, materialConstants.data, materialConstants.size); } } if (setLayout.usesGaussianSplatPositionBuffer) { if (cachedGaussianSplat.positions.shaderResourceView == nullptr || !passLayout.gaussianSplatPositionBuffer.IsValid() || passLayout.gaussianSplatPositionBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.positionsView != cachedGaussianSplat.positions.shaderResourceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatPositionBuffer.binding, cachedGaussianSplat.positions.shaderResourceView); } } if (setLayout.usesGaussianSplatSortDistanceBuffer) { if (resolvedSortDistanceView == nullptr || !passLayout.gaussianSplatSortDistanceBuffer.IsValid() || passLayout.gaussianSplatSortDistanceBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.sortDistanceView != resolvedSortDistanceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatSortDistanceBuffer.binding, resolvedSortDistanceView); } } if (setLayout.usesGaussianSplatOrderBuffer) { if (resolvedOrderView == nullptr || !passLayout.gaussianSplatOrderBuffer.IsValid() || passLayout.gaussianSplatOrderBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.orderView != resolvedOrderView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatOrderBuffer.binding, resolvedOrderView); } } if (setLayout.usesGaussianSplatOtherBuffer) { if (cachedGaussianSplat.other.shaderResourceView == nullptr || !passLayout.gaussianSplatOtherBuffer.IsValid() || passLayout.gaussianSplatOtherBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.otherView != cachedGaussianSplat.other.shaderResourceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatOtherBuffer.binding, cachedGaussianSplat.other.shaderResourceView); } } if (setLayout.usesGaussianSplatColorBuffer) { if (cachedGaussianSplat.color.shaderResourceView == nullptr || !passLayout.gaussianSplatColorBuffer.IsValid() || passLayout.gaussianSplatColorBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.colorView != cachedGaussianSplat.color.shaderResourceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatColorBuffer.binding, cachedGaussianSplat.color.shaderResourceView); } } if (setLayout.usesGaussianSplatSHBuffer) { if (cachedGaussianSplat.sh.shaderResourceView == nullptr || !passLayout.gaussianSplatSHBuffer.IsValid() || passLayout.gaussianSplatSHBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.shView != cachedGaussianSplat.sh.shaderResourceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatSHBuffer.binding, cachedGaussianSplat.sh.shaderResourceView); } } if (setLayout.usesGaussianSplatChunkBuffer) { if (cachedGaussianSplat.chunks.shaderResourceView == nullptr || !passLayout.gaussianSplatChunkBuffer.IsValid() || passLayout.gaussianSplatChunkBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.chunkView != cachedGaussianSplat.chunks.shaderResourceView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatChunkBuffer.binding, cachedGaussianSplat.chunks.shaderResourceView); } } 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 (resolvedViewDataView == nullptr || !passLayout.gaussianSplatViewDataBuffer.IsValid() || passLayout.gaussianSplatViewDataBuffer.set != setIndex) { return nullptr; } if (cachedDescriptorSet.viewDataView != resolvedViewDataView) { cachedDescriptorSet.descriptorSet.set->Update( passLayout.gaussianSplatViewDataBuffer.binding, resolvedViewDataView); } } cachedDescriptorSet.materialVersion = materialVersion; cachedDescriptorSet.sortDistanceView = resolvedSortDistanceView; cachedDescriptorSet.orderView = resolvedOrderView; cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView; cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView; cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView; cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView; cachedDescriptorSet.chunkView = cachedGaussianSplat.chunks.shaderResourceView; cachedDescriptorSet.visibleChunkView = resolvedVisibleChunkView; cachedDescriptorSet.viewDataView = resolvedViewDataView; return &cachedDescriptorSet; } void BuiltinGaussianSplatPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) { if (descriptorSet.set != nullptr) { descriptorSet.set->Shutdown(); delete descriptorSet.set; descriptorSet.set = nullptr; } if (descriptorSet.pool != nullptr) { descriptorSet.pool->Shutdown(); delete descriptorSet.pool; descriptorSet.pool = nullptr; } } void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& passLayout) { if (passLayout.pipelineLayout != nullptr) { passLayout.pipelineLayout->Shutdown(); delete passLayout.pipelineLayout; passLayout.pipelineLayout = nullptr; } passLayout.setLayouts.clear(); passLayout.firstDescriptorSet = 0; passLayout.descriptorSetCount = 0; passLayout.perObject = {}; passLayout.material = {}; passLayout.gaussianSplatSortDistanceBuffer = {}; passLayout.gaussianSplatOrderBuffer = {}; passLayout.gaussianSplatPositionBuffer = {}; passLayout.gaussianSplatOtherBuffer = {}; passLayout.gaussianSplatColorBuffer = {}; passLayout.gaussianSplatSHBuffer = {}; passLayout.gaussianSplatChunkBuffer = {}; passLayout.gaussianSplatVisibleChunkBuffer = {}; 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(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(sceneData.cameraData.viewportWidth), static_cast(sceneData.cameraData.viewportHeight), sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportWidth) : 0.0f, sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportHeight) : 0.0f), Math::Vector4( static_cast(cachedGaussianSplat->splatCount), static_cast(workingSet->sortCapacity), shOrder, static_cast(cachedGaussianSplat->chunkCount)) }; if (passLayout->descriptorSetCount > 0u) { std::vector 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( 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 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 || cachedGaussianSplat->other.shaderResourceView == nullptr || cachedGaussianSplat->color.shaderResourceView == nullptr || cachedGaussianSplat->sh.shaderResourceView == nullptr) { return fail("BuiltinGaussianSplatPass prepare-order failed: gaussian splat GPU cache is incomplete"); } Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr; if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) || workingSet == nullptr || workingSet->visibleChunks.shaderResourceView == nullptr || workingSet->sortDistances.unorderedAccessView == nullptr || workingSet->orderIndices.unorderedAccessView == nullptr || 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 fail("BuiltinGaussianSplatPass prepare-order failed: utilities shader pass was not resolved"); } PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; passLayoutKey.passName = resolvedShaderPass.passName; PassResourceLayout* passLayout = GetOrCreatePassResourceLayout( context, resolvedShaderPass, PassLayoutUsage::PrepareOrder); RHI::RHIPipelineState* pipelineState = GetOrCreateComputePipelineState( context, resolvedShaderPass, PassLayoutUsage::PrepareOrder, sceneData.globalShaderKeywords); if (passLayout == nullptr || pipelineState == nullptr) { return fail("BuiltinGaussianSplatPass prepare-order failed: compute pipeline setup was not created"); } RHI::RHICommandList* commandList = context.commandList; 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->viewData, RHI::ResourceStates::UnorderedAccess)) { return fail("BuiltinGaussianSplatPass prepare-order failed: resource transition failed"); } commandList->SetPipelineState(pipelineState); const float shOrder = visibleGaussianSplat.gaussianSplat != nullptr ? static_cast(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(sceneData.cameraData.viewportWidth), static_cast(sceneData.cameraData.viewportHeight), sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportWidth) : 0.0f, sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportHeight) : 0.0f), Math::Vector4( static_cast(cachedGaussianSplat->splatCount), static_cast(workingSet->sortCapacity), shOrder, static_cast(cachedGaussianSplat->chunkCount)) }; if (passLayout->descriptorSetCount > 0u) { std::vector 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 prepare-order failed: descriptor set index overflow"); } const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex]; if (setLayout.layout.bindingCount == 0u) { continue; } if (!(setLayout.usesPerObject || setLayout.usesGaussianSplatVisibleChunkBuffer || setLayout.usesGaussianSplatSortDistanceBuffer || setLayout.usesGaussianSplatOrderBuffer || setLayout.usesGaussianSplatPositionBuffer || setLayout.usesGaussianSplatOtherBuffer || setLayout.usesGaussianSplatColorBuffer || setLayout.usesGaussianSplatSHBuffer || setLayout.usesGaussianSplatViewDataBuffer)) { return fail("BuiltinGaussianSplatPass prepare-order failed: unexpected descriptor set layout"); } const Core::uint64 objectId = setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u; const Resources::GaussianSplat* gaussianSplatKey = (setLayout.usesGaussianSplatSortDistanceBuffer || setLayout.usesGaussianSplatVisibleChunkBuffer || setLayout.usesGaussianSplatOrderBuffer || setLayout.usesGaussianSplatPositionBuffer || setLayout.usesGaussianSplatOtherBuffer || setLayout.usesGaussianSplatColorBuffer || setLayout.usesGaussianSplatSHBuffer || setLayout.usesGaussianSplatViewDataBuffer) ? visibleGaussianSplat.gaussianSplat : nullptr; CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet( passLayoutKey, *passLayout, setLayout, setIndex, objectId, visibleGaussianSplat.gaussianSplatRenderer, nullptr, gaussianSplatKey, MaterialConstantPayloadView(), *cachedGaussianSplat, workingSet->sortDistances.unorderedAccessView, workingSet->orderIndices.unorderedAccessView, workingSet->visibleChunks.shaderResourceView, workingSet->viewData.unorderedAccessView); if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) { 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 fail("BuiltinGaussianSplatPass prepare-order 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((workingSet->sortCapacity + 63u) / 64u, 1u, 1u); if (!TransitionWorkingSetBuffer(commandList, workingSet->viewData, RHI::ResourceStates::NonPixelShaderResource)) { return fail("BuiltinGaussianSplatPass prepare-order failed: view-data transition failed"); } return true; } bool BuiltinGaussianSplatPass::SortVisibleGaussianSplat( 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 bitonic-sort failed: invalid visible gaussian splat item"); } const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat = m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat); if (cachedGaussianSplat == nullptr) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: gaussian splat GPU cache is missing"); } Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr; if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) || workingSet == nullptr || workingSet->sortDistances.shaderResourceView == nullptr || workingSet->sortDistances.unorderedAccessView == nullptr || workingSet->orderIndices.shaderResourceView == nullptr || workingSet->orderIndices.unorderedAccessView == nullptr) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: working set allocation is incomplete"); } RHI::RHICommandList* commandList = context.commandList; if (workingSet->sortCapacity <= 1u) { return TransitionWorkingSetBuffer( commandList, workingSet->orderIndices, RHI::ResourceStates::NonPixelShaderResource); } const ResolvedShaderPass resolvedShaderPass = ResolveBitonicSortShaderPass(sceneData); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: utilities shader pass was not resolved"); } PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; passLayoutKey.passName = resolvedShaderPass.passName; PassResourceLayout* passLayout = GetOrCreatePassResourceLayout( context, resolvedShaderPass, PassLayoutUsage::BitonicSort); RHI::RHIPipelineState* pipelineState = GetOrCreateComputePipelineState( context, resolvedShaderPass, PassLayoutUsage::BitonicSort, sceneData.globalShaderKeywords); if (passLayout == nullptr || pipelineState == nullptr) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: compute pipeline setup was not created"); } commandList->SetPipelineState(pipelineState); for (Core::uint32 levelMask = 2u; levelMask <= workingSet->sortCapacity; levelMask <<= 1u) { for (Core::uint32 partnerMask = levelMask >> 1u; partnerMask > 0u; partnerMask >>= 1u) { if (!TransitionWorkingSetBuffer(commandList, workingSet->sortDistances, RHI::ResourceStates::UnorderedAccess) || !TransitionWorkingSetBuffer(commandList, workingSet->orderIndices, RHI::ResourceStates::UnorderedAccess)) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: resource transition to UAV failed"); } 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(sceneData.cameraData.viewportWidth), static_cast(sceneData.cameraData.viewportHeight), sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportWidth) : 0.0f, sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportHeight) : 0.0f), Math::Vector4( static_cast(cachedGaussianSplat->splatCount), static_cast(workingSet->sortCapacity), static_cast(partnerMask), static_cast(levelMask)) }; if (passLayout->descriptorSetCount > 0u) { std::vector 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 bitonic-sort failed: descriptor set index overflow"); } const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex]; if (setLayout.layout.bindingCount == 0u) { continue; } if (!(setLayout.usesPerObject || setLayout.usesGaussianSplatSortDistanceBuffer || setLayout.usesGaussianSplatOrderBuffer)) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: unexpected descriptor set layout"); } const Core::uint64 objectId = setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u; const Resources::GaussianSplat* gaussianSplatKey = (setLayout.usesGaussianSplatSortDistanceBuffer || setLayout.usesGaussianSplatOrderBuffer) ? visibleGaussianSplat.gaussianSplat : nullptr; CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet( passLayoutKey, *passLayout, setLayout, setIndex, objectId, visibleGaussianSplat.gaussianSplatRenderer, nullptr, gaussianSplatKey, MaterialConstantPayloadView(), *cachedGaussianSplat, workingSet->sortDistances.unorderedAccessView, workingSet->orderIndices.unorderedAccessView, nullptr, nullptr); if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) { return fail("BuiltinGaussianSplatPass bitonic-sort 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 bitonic-sort 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((workingSet->sortCapacity + 255u) / 256u, 1u, 1u); if (!TransitionWorkingSetBuffer(commandList, workingSet->sortDistances, RHI::ResourceStates::NonPixelShaderResource) || !TransitionWorkingSetBuffer(commandList, workingSet->orderIndices, RHI::ResourceStates::NonPixelShaderResource)) { return fail("BuiltinGaussianSplatPass bitonic-sort failed: resource transition to SRV failed"); } } } return true; } bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat( const RenderContext& context, 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 fail("BuiltinGaussianSplatPass draw failed: invalid visible gaussian splat item"); } const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat = m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat); if (cachedGaussianSplat == nullptr || cachedGaussianSplat->positions.shaderResourceView == nullptr || cachedGaussianSplat->other.shaderResourceView == nullptr || cachedGaussianSplat->color.shaderResourceView == nullptr) { return fail("BuiltinGaussianSplatPass draw failed: gaussian splat GPU cache is incomplete"); } if (m_passResources == nullptr) { 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 || workingSet->viewData.shaderResourceView == nullptr) { return fail("BuiltinGaussianSplatPass draw failed: working set allocation is incomplete"); } const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat); if (material == nullptr) { 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 fail("BuiltinGaussianSplatPass draw failed: draw shader pass was not resolved"); } PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; passLayoutKey.passName = resolvedShaderPass.passName; PassResourceLayout* passLayout = GetOrCreatePassResourceLayout( context, resolvedShaderPass, PassLayoutUsage::Draw); RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material); if (passLayout == nullptr || pipelineState == nullptr) { return fail("BuiltinGaussianSplatPass draw failed: graphics pipeline setup was not created"); } const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material); if (!materialConstants.IsValid()) { return fail("BuiltinGaussianSplatPass draw failed: material constant payload is invalid"); } RHI::RHICommandList* commandList = context.commandList; commandList->SetPipelineState(pipelineState); 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(sceneData.cameraData.viewportWidth), static_cast(sceneData.cameraData.viewportHeight), sceneData.cameraData.viewportWidth > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportWidth) : 0.0f, sceneData.cameraData.viewportHeight > 0u ? 1.0f / static_cast(sceneData.cameraData.viewportHeight) : 0.0f), Math::Vector4( static_cast(cachedGaussianSplat->splatCount), static_cast(workingSet->sortCapacity), 0.0f, 0.0f) }; if (passLayout->descriptorSetCount > 0u) { std::vector 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 draw failed: descriptor set index overflow"); } const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex]; if (setLayout.layout.bindingCount == 0u) { continue; } if (!(setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesGaussianSplatOrderBuffer || setLayout.usesGaussianSplatViewDataBuffer)) { return fail("BuiltinGaussianSplatPass draw failed: unexpected descriptor set layout"); } const Core::uint64 objectId = setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u; const Resources::Material* materialKey = setLayout.usesMaterial ? material : nullptr; const Resources::GaussianSplat* gaussianSplatKey = (setLayout.usesGaussianSplatOrderBuffer || setLayout.usesGaussianSplatViewDataBuffer) ? visibleGaussianSplat.gaussianSplat : nullptr; CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet( passLayoutKey, *passLayout, setLayout, setIndex, objectId, visibleGaussianSplat.gaussianSplatRenderer, materialKey, gaussianSplatKey, materialConstants, *cachedGaussianSplat, nullptr, workingSet->orderIndices.shaderResourceView, nullptr, workingSet->viewData.shaderResourceView); if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) { 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 fail("BuiltinGaussianSplatPass draw 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->SetGraphicsDescriptorSets( firstSet, count, sets, passLayout->pipelineLayout); }); } ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList); commandList->Draw(6u, cachedGaussianSplat->splatCount, 0u, 0u); return true; } } // namespace Passes } // namespace Rendering } // namespace XCEngine