#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/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; } 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; } } // namespace BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() { Shutdown(); } const char* BuiltinGaussianSplatPass::GetName() const { return "BuiltinGaussianSplatPass"; } bool BuiltinGaussianSplatPass::Initialize(const RenderContext& context) { return EnsureInitialized(context); } bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources( const RenderContext& context, const RenderSceneData& sceneData) { if (!EnsureInitialized(context)) { 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) { 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 (!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_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); return true; } void BuiltinGaussianSplatPass::DestroyResources() { m_resourceCache.Shutdown(); for (auto& pipelinePair : m_pipelineStates) { if (pipelinePair.second != nullptr) { pipelinePair.second->Shutdown(); delete pipelinePair.second; } } m_pipelineStates.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_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::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout( const RenderContext& context, const ResolvedShaderPass& resolvedShaderPass) { 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.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer; passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer; passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer; passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer; if (!passLayout.perObject.IsValid()) { return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding"); } if (!passLayout.material.IsValid()) { return failLayout("BuiltinGaussianSplatPass requires a Material resource binding"); } if (!passLayout.gaussianSplatPositionBuffer.IsValid() || !passLayout.gaussianSplatOtherBuffer.IsValid() || !passLayout.gaussianSplatColorBuffer.IsValid()) { return failLayout("BuiltinGaussianSplatPass requires position, other, and color 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); 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; } 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 Resources::Material* material, const Resources::GaussianSplat* gaussianSplat, const MaterialConstantPayloadView& materialConstants, const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat) { DynamicDescriptorSetKey key = {}; key.passLayout = passLayoutKey; key.setIndex = setIndex; key.objectId = objectId; key.material = material; key.gaussianSplat = gaussianSplat; CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key]; if (cachedDescriptorSet.descriptorSet.set == nullptr) { if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) { return nullptr; } } 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.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); } } cachedDescriptorSet.materialVersion = materialVersion; cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView; cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView; cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView; cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView; 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.gaussianSplatPositionBuffer = {}; passLayout.gaussianSplatOtherBuffer = {}; passLayout.gaussianSplatColorBuffer = {}; passLayout.gaussianSplatSHBuffer = {}; } bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat( const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData, const VisibleGaussianSplatItem& visibleGaussianSplat) { if (visibleGaussianSplat.gameObject == nullptr || visibleGaussianSplat.gaussianSplat == nullptr || !visibleGaussianSplat.gaussianSplat->IsValid()) { return false; } 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 false; } const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat); if (material == nullptr) { return false; } const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return false; } PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; passLayoutKey.passName = resolvedShaderPass.passName; PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass); RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material); if (passLayout == nullptr || pipelineState == nullptr) { return false; } const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material); if (!materialConstants.IsValid()) { return false; } RHI::RHICommandList* commandList = context.commandList; commandList->SetPipelineState(pipelineState); const PerObjectConstants perObjectConstants = { sceneData.cameraData.projection, sceneData.cameraData.view, visibleGaussianSplat.localToWorld.Transpose(), Math::Vector4(sceneData.cameraData.worldRight, 0.0f), Math::Vector4(sceneData.cameraData.worldUp, 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 false; } const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex]; if (!(setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesGaussianSplatPositionBuffer || setLayout.usesGaussianSplatOtherBuffer || setLayout.usesGaussianSplatColorBuffer || setLayout.usesGaussianSplatSHBuffer)) { return false; } const Core::uint64 objectId = setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u; const Resources::Material* materialKey = setLayout.usesMaterial ? material : nullptr; const Resources::GaussianSplat* gaussianSplatKey = (setLayout.usesGaussianSplatPositionBuffer || setLayout.usesGaussianSplatOtherBuffer || setLayout.usesGaussianSplatColorBuffer || setLayout.usesGaussianSplatSHBuffer) ? visibleGaussianSplat.gaussianSplat : nullptr; CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet( passLayoutKey, *passLayout, setLayout, setIndex, objectId, materialKey, gaussianSplatKey, materialConstants, *cachedGaussianSplat); if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) { return false; } RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set; if (setLayout.usesPerObject) { if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) { return false; } descriptorSet->WriteConstant( passLayout->perObject.binding, &perObjectConstants, sizeof(perObjectConstants)); } descriptorSets[descriptorOffset] = descriptorSet; } commandList->SetGraphicsDescriptorSets( passLayout->firstDescriptorSet, passLayout->descriptorSetCount, descriptorSets.data(), passLayout->pipelineLayout); } ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList); commandList->Draw(6u, cachedGaussianSplat->splatCount, 0u, 0u); return true; } } // namespace Passes } // namespace Rendering } // namespace XCEngine