From af269b53ca2d9f9a3f46016531075edc02f16738 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 5 Apr 2026 20:36:30 +0800 Subject: [PATCH] Split builtin depth-style pass resources --- engine/CMakeLists.txt | 1 + .../Passes/BuiltinDepthStylePassBase.cpp | 561 +---------------- .../BuiltinDepthStylePassBaseResources.cpp | 575 ++++++++++++++++++ 3 files changed, 577 insertions(+), 560 deletions(-) create mode 100644 engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 92dd0093..caffefc2 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -460,6 +460,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/CameraRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/DirectionalShadowSurfaceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthOnlyPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp index f69b5ea2..79417ce5 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp @@ -1,101 +1,17 @@ #include "Rendering/Passes/BuiltinDepthStylePassBase.h" -#include "Components/GameObject.h" -#include "Core/Asset/ResourceManager.h" -#include "Debug/Logger.h" + #include "RHI/RHICommandList.h" -#include "Rendering/BuiltinPassLayoutUtils.h" -#include "Rendering/RenderMaterialResolve.h" -#include "Rendering/Detail/ShaderVariantUtils.h" #include "Rendering/RenderSceneExtractor.h" #include "Rendering/RenderSurface.h" -#include "Resources/Material/Material.h" #include "Resources/Mesh/Mesh.h" #include #include -#include namespace XCEngine { namespace Rendering { namespace Passes { -namespace { - -bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) { - return bindingPlan.perObject.IsValid() && - bindingPlan.bindings.Size() == 1u && - bindingPlan.descriptorSetCount == 1u && - bindingPlan.usesConstantBuffers && - !bindingPlan.usesTextures && - !bindingPlan.usesSamplers; -} - -uint32_t ResolveSurfaceColorAttachmentCount(const RenderSurface& surface) { - const std::vector& colorAttachments = surface.GetColorAttachments(); - return (!colorAttachments.empty() && colorAttachments[0] != nullptr) ? 1u : 0u; -} - -RHI::Format ResolveSurfaceColorFormat(const RenderSurface& surface) { - const std::vector& colorAttachments = surface.GetColorAttachments(); - if (colorAttachments.empty() || colorAttachments[0] == nullptr) { - return RHI::Format::Unknown; - } - - return colorAttachments[0]->GetFormat(); -} - -RHI::Format ResolveSurfaceDepthFormat(const RenderSurface& surface) { - if (RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); - depthAttachment != nullptr) { - return depthAttachment->GetFormat(); - } - - return RHI::Format::Unknown; -} - -RHI::GraphicsPipelineDesc CreatePipelineDesc( - RHI::RHIType backendType, - RHI::RHIPipelineLayout* pipelineLayout, - const Resources::Shader& shader, - const Containers::String& passName, - const Resources::Material* material, - const RenderSurface& surface, - const RHI::InputLayoutDesc& inputLayout) { - RHI::GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); - pipelineDesc.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface); - if (pipelineDesc.renderTargetCount > 0) { - pipelineDesc.renderTargetFormats[0] = - static_cast(ResolveSurfaceColorFormat(surface)); - } - pipelineDesc.depthStencilFormat = - static_cast(ResolveSurfaceDepthFormat(surface)); - pipelineDesc.sampleCount = 1; - pipelineDesc.inputLayout = inputLayout; - ApplyMaterialRenderState(material, pipelineDesc); - - pipelineDesc.blendState.blendEnable = false; - pipelineDesc.blendState.colorWriteMask = pipelineDesc.renderTargetCount > 0 ? 0xF : 0; - pipelineDesc.depthStencilState.depthTestEnable = true; - pipelineDesc.depthStencilState.depthWriteEnable = true; - pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); - if (const Resources::ShaderStageVariant* vertexVariant = - shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { - ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); - } - if (const Resources::ShaderStageVariant* fragmentVariant = - shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { - ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); - } - - return pipelineDesc; -} - -} // namespace - BuiltinDepthStylePassBase::BuiltinDepthStylePassBase( BuiltinMaterialPass passType, Containers::String builtinShaderPath) @@ -238,481 +154,6 @@ bool BuiltinDepthStylePassBase::ShouldRenderVisibleItem(const VisibleRenderItem& return true; } -bool BuiltinDepthStylePassBase::EnsureInitialized(const RenderContext& context) { - if (!context.IsValid()) { - return false; - } - - if (m_device == context.device && - m_backendType == context.backendType && - m_builtinShader.IsValid()) { - return true; - } - - DestroyResources(); - return CreateResources(context); -} - -bool BuiltinDepthStylePassBase::CreateResources(const RenderContext& context) { - m_device = context.device; - m_backendType = context.backendType; - m_builtinShader = Resources::ResourceManager::Get().Load(m_builtinShaderPath); - if (!m_builtinShader.IsValid()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase failed to load builtin shader resource: ") + m_builtinShaderPath).CStr()); - DestroyResources(); - return false; - } - - return true; -} - -void BuiltinDepthStylePassBase::DestroyResources() { - m_resourceCache.Shutdown(); - - for (auto& descriptorSetEntry : m_perObjectSets) { - DestroyOwnedDescriptorSet(descriptorSetEntry.second); - } - m_perObjectSets.clear(); - - for (auto& pipelineEntry : m_pipelineStates) { - if (pipelineEntry.second != nullptr) { - pipelineEntry.second->Shutdown(); - delete pipelineEntry.second; - } - } - m_pipelineStates.clear(); - - for (auto& layoutEntry : m_passResourceLayouts) { - DestroyPassResourceLayout(layoutEntry.second); - } - m_passResourceLayouts.clear(); - - m_device = nullptr; - m_backendType = RHI::RHIType::D3D12; - m_builtinShader.Reset(); -} - -BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::ResolveSurfaceShaderPass( - const Resources::Material* material) const { - ResolvedShaderPass resolved = {}; - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); - - auto tryResolveFromShader = - [this, backend, &resolved]( - const Resources::Shader* shader, - const Resources::Material* ownerMaterial) -> bool { - if (shader == nullptr) { - return false; - } - - auto tryAcceptPass = - [this, shader, &resolved](const Resources::ShaderPass& shaderPass) -> bool { - BuiltinPassResourceBindingPlan bindingPlan = {}; - Containers::String error; - if (!TryBuildSupportedBindingPlan(shaderPass, bindingPlan, &error)) { - return false; - } - - resolved.shader = shader; - resolved.pass = &shaderPass; - resolved.passName = shaderPass.name; - return true; - }; - - if (ownerMaterial != nullptr && !ownerMaterial->GetShaderPass().Empty()) { - const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetShaderPass()); - if (explicitPass != nullptr && - ShaderPassMatchesBuiltinPass(*explicitPass, m_passType) && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( - *shader, - explicitPass->name, - backend) && - tryAcceptPass(*explicitPass)) { - return true; - } - } - - for (const Resources::ShaderPass& shaderPass : shader->GetPasses()) { - if (!ShaderPassMatchesBuiltinPass(shaderPass, m_passType) || - !::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(*shader, shaderPass.name, backend)) { - continue; - } - - if (tryAcceptPass(shaderPass)) { - return true; - } - } - - return false; - }; - - if (material != nullptr && - material->GetShader() != nullptr && - tryResolveFromShader(material->GetShader(), material)) { - return resolved; - } - - if (material != nullptr && IsTransparentRenderQueue(ResolveMaterialRenderQueue(material))) { - return {}; - } - - if (m_builtinShader.IsValid() && - tryResolveFromShader(m_builtinShader.Get(), nullptr)) { - return resolved; - } - - return {}; -} - -bool BuiltinDepthStylePassBase::TryBuildSupportedBindingPlan( - const Resources::ShaderPass& shaderPass, - BuiltinPassResourceBindingPlan& outPlan, - Containers::String* outError) const { - if (shaderPass.resources.Empty()) { - if (outError != nullptr) { - *outError = - Containers::String("Builtin depth-style pass requires explicit resource bindings on shader pass: ") + - shaderPass.name; - } - return false; - } - - if (!TryBuildBuiltinPassResourceBindingPlan(shaderPass.resources, outPlan, outError)) { - return false; - } - - if (!IsSupportedPerObjectOnlyBindingPlan(outPlan)) { - if (outError != nullptr) { - *outError = "Builtin depth-style pass currently requires exactly one PerObject constant-buffer binding"; - } - return false; - } - - return true; -} - -BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::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 (!TryBuildSupportedBindingPlan(*resolvedShaderPass.pass, bindingPlan, &bindingPlanError)) { - const Containers::String contextualError = - Containers::String("BuiltinDepthStylePassBase failed to resolve pass resource bindings for shader='") + - resolvedShaderPass.shader->GetPath() + - "', pass='" + resolvedShaderPass.passName + - "': " + bindingPlanError + - ". Bindings: " + DescribeShaderResourceBindings(resolvedShaderPass.pass->resources); - return failLayout(contextualError.CStr()); - } - - std::vector setLayouts; - Containers::String setLayoutError; - if (!TryBuildBuiltinPassSetLayouts(bindingPlan, setLayouts, &setLayoutError)) { - return failLayout(setLayoutError.CStr()); - } - - if (bindingPlan.perObject.set >= setLayouts.size()) { - return failLayout("Builtin depth-style pass produced an invalid PerObject descriptor set index"); - } - - passLayout.perObject = bindingPlan.perObject; - passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet; - passLayout.perObjectSetLayout = setLayouts[bindingPlan.perObject.set]; - RefreshBuiltinPassSetLayoutMetadata(passLayout.perObjectSetLayout); - - std::vector nativeSetLayouts(setLayouts.size()); - for (size_t setIndex = 0; setIndex < setLayouts.size(); ++setIndex) { - nativeSetLayouts[setIndex] = 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("Builtin depth-style pass failed to create pipeline layout"); - } - - const auto result = m_passResourceLayouts.emplace(passLayoutKey, std::move(passLayout)); - RefreshBuiltinPassSetLayoutMetadata(result.first->second.perObjectSetLayout); - return &result.first->second; -} - -RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState( - const RenderContext& context, - const RenderSurface& surface, - const Resources::Material* material) { - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(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 = - material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState(); - pipelineKey.shader = resolvedShaderPass.shader; - pipelineKey.passName = resolvedShaderPass.passName; - pipelineKey.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface); - pipelineKey.renderTargetFormat = static_cast(ResolveSurfaceColorFormat(surface)); - pipelineKey.depthStencilFormat = static_cast(ResolveSurfaceDepthFormat(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.passName, - material, - surface, - BuildCommonInputLayout()); - 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 BuiltinDepthStylePassBase::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; -} - -RHI::RHIDescriptorSet* BuiltinDepthStylePassBase::GetOrCreatePerObjectSet( - const PassLayoutKey& passLayoutKey, - const PassResourceLayout& passLayout, - Core::uint64 objectId) { - if (!passLayout.perObject.IsValid() || - passLayout.perObjectSetLayout.layout.bindingCount == 0) { - return nullptr; - } - - PerObjectSetKey key = {}; - key.passLayout = passLayoutKey; - key.objectId = objectId; - - const auto existing = m_perObjectSets.find(key); - if (existing != m_perObjectSets.end()) { - return existing->second.set; - } - - OwnedDescriptorSet descriptorSet = {}; - if (!CreateOwnedDescriptorSet(passLayout.perObjectSetLayout, descriptorSet)) { - return nullptr; - } - - const auto result = m_perObjectSets.emplace(key, descriptorSet); - return result.first->second.set; -} - -void BuiltinDepthStylePassBase::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 BuiltinDepthStylePassBase::DestroyPassResourceLayout(PassResourceLayout& passLayout) { - if (passLayout.pipelineLayout != nullptr) { - passLayout.pipelineLayout->Shutdown(); - delete passLayout.pipelineLayout; - passLayout.pipelineLayout = nullptr; - } - - passLayout.perObject = {}; - passLayout.perObjectSetLayout = {}; - passLayout.firstDescriptorSet = 0; -} - -bool BuiltinDepthStylePassBase::DrawVisibleItem( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, - const VisibleRenderItem& visibleItem) { - if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinDepthStylePassBase skipped visible item because mesh or game object was null"); - return false; - } - - const RenderResourceCache::CachedMesh* cachedMesh = - m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh); - if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase failed to cache mesh for ") + - visibleItem.gameObject->GetName().c_str()).CStr()); - return false; - } - - const Resources::Material* material = ResolveMaterial(visibleItem); - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); - if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase could not resolve shader pass for ") + - visibleItem.gameObject->GetName().c_str()).CStr()); - return false; - } - - PassLayoutKey passLayoutKey = {}; - passLayoutKey.shader = resolvedShaderPass.shader; - passLayoutKey.passName = resolvedShaderPass.passName; - - PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass); - if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase failed to create pass layout for ") + - visibleItem.gameObject->GetName().c_str()).CStr()); - return false; - } - - RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material); - if (pipelineState == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase failed to create pipeline state for ") + - visibleItem.gameObject->GetName().c_str() + - " using pass " + resolvedShaderPass.passName).CStr()); - return false; - } - - RHI::RHICommandList* commandList = context.commandList; - commandList->SetPipelineState(pipelineState); - - RHI::RHIResourceView* vertexBuffers[] = { cachedMesh->vertexBufferView }; - const uint64_t offsets[] = { 0 }; - const uint32_t strides[] = { cachedMesh->vertexStride }; - commandList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); - if (cachedMesh->indexBufferView != nullptr) { - commandList->SetIndexBuffer(cachedMesh->indexBufferView, 0); - } - - RHI::RHIDescriptorSet* constantSet = GetOrCreatePerObjectSet( - passLayoutKey, - *passLayout, - visibleItem.gameObject->GetID()); - if (constantSet == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase failed to allocate descriptor set for ") + - visibleItem.gameObject->GetName().c_str()).CStr()); - return false; - } - - const Math::Matrix4x4 projectionMatrix = - m_passType == BuiltinMaterialPass::ShadowCaster - ? sceneData.cameraData.viewProjection - : sceneData.cameraData.projection; - const Math::Matrix4x4 viewMatrix = - m_passType == BuiltinMaterialPass::ShadowCaster - ? Math::Matrix4x4::Identity() - : sceneData.cameraData.view; - const PerObjectConstants constants = { - projectionMatrix, - viewMatrix, - visibleItem.localToWorld.Transpose() - }; - constantSet->WriteConstant(passLayout->perObject.binding, &constants, sizeof(constants)); - - RHI::RHIDescriptorSet* descriptorSets[] = { constantSet }; - commandList->SetGraphicsDescriptorSets( - passLayout->firstDescriptorSet, - 1, - descriptorSets, - passLayout->pipelineLayout); - - if (visibleItem.hasSection) { - const Containers::Array& sections = visibleItem.mesh->GetSections(); - if (visibleItem.sectionIndex >= sections.Size()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - (Containers::String("BuiltinDepthStylePassBase received invalid mesh section for ") + - visibleItem.gameObject->GetName().c_str()).CStr()); - return false; - } - - const Resources::MeshSection& section = sections[visibleItem.sectionIndex]; - if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) { - commandList->DrawIndexed(section.indexCount, 1, section.startIndex, 0, 0); - } else if (section.vertexCount > 0) { - commandList->Draw(section.vertexCount, 1, section.baseVertex, 0); - } - return true; - } - - if (cachedMesh->indexBufferView != nullptr && cachedMesh->indexCount > 0) { - commandList->DrawIndexed(cachedMesh->indexCount, 1, 0, 0, 0); - } else if (cachedMesh->vertexCount > 0) { - commandList->Draw(cachedMesh->vertexCount, 1, 0, 0); - } - - return true; -} - } // namespace Passes } // namespace Rendering } // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp new file mode 100644 index 00000000..45aaad3f --- /dev/null +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp @@ -0,0 +1,575 @@ +#include "Rendering/Passes/BuiltinDepthStylePassBase.h" + +#include "Components/GameObject.h" +#include "Core/Asset/ResourceManager.h" +#include "Debug/Logger.h" +#include "RHI/RHICommandList.h" +#include "Rendering/BuiltinPassLayoutUtils.h" +#include "Rendering/Detail/ShaderVariantUtils.h" +#include "Rendering/RenderMaterialResolve.h" +#include "Rendering/RenderSceneExtractor.h" +#include "Rendering/RenderSurface.h" +#include "Resources/Material/Material.h" +#include "Resources/Mesh/Mesh.h" + +#include + +namespace XCEngine { +namespace Rendering { +namespace Passes { + +namespace { + +bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) { + return bindingPlan.perObject.IsValid() && + bindingPlan.bindings.Size() == 1u && + bindingPlan.descriptorSetCount == 1u && + bindingPlan.usesConstantBuffers && + !bindingPlan.usesTextures && + !bindingPlan.usesSamplers; +} + +uint32_t ResolveSurfaceColorAttachmentCount(const RenderSurface& surface) { + const std::vector& colorAttachments = surface.GetColorAttachments(); + return (!colorAttachments.empty() && colorAttachments[0] != nullptr) ? 1u : 0u; +} + +RHI::Format ResolveSurfaceColorFormat(const RenderSurface& surface) { + const std::vector& colorAttachments = surface.GetColorAttachments(); + if (colorAttachments.empty() || colorAttachments[0] == nullptr) { + return RHI::Format::Unknown; + } + + return colorAttachments[0]->GetFormat(); +} + +RHI::Format ResolveSurfaceDepthFormat(const RenderSurface& surface) { + if (RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); + depthAttachment != nullptr) { + return depthAttachment->GetFormat(); + } + + return RHI::Format::Unknown; +} + +RHI::GraphicsPipelineDesc CreatePipelineDesc( + RHI::RHIType backendType, + RHI::RHIPipelineLayout* pipelineLayout, + const Resources::Shader& shader, + const Containers::String& passName, + const Resources::Material* material, + const RenderSurface& surface, + const RHI::InputLayoutDesc& inputLayout) { + RHI::GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); + pipelineDesc.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface); + if (pipelineDesc.renderTargetCount > 0) { + pipelineDesc.renderTargetFormats[0] = + static_cast(ResolveSurfaceColorFormat(surface)); + } + pipelineDesc.depthStencilFormat = + static_cast(ResolveSurfaceDepthFormat(surface)); + pipelineDesc.sampleCount = 1; + pipelineDesc.inputLayout = inputLayout; + ApplyMaterialRenderState(material, pipelineDesc); + + pipelineDesc.blendState.blendEnable = false; + pipelineDesc.blendState.colorWriteMask = pipelineDesc.renderTargetCount > 0 ? 0xF : 0; + pipelineDesc.depthStencilState.depthTestEnable = true; + pipelineDesc.depthStencilState.depthWriteEnable = true; + pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); + + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); + if (const Resources::ShaderStageVariant* vertexVariant = + shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); + } + if (const Resources::ShaderStageVariant* fragmentVariant = + shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); + } + + return pipelineDesc; +} + +} // namespace + +bool BuiltinDepthStylePassBase::EnsureInitialized(const RenderContext& context) { + if (!context.IsValid()) { + return false; + } + + if (m_device == context.device && + m_backendType == context.backendType && + m_builtinShader.IsValid()) { + return true; + } + + DestroyResources(); + return CreateResources(context); +} + +bool BuiltinDepthStylePassBase::CreateResources(const RenderContext& context) { + m_device = context.device; + m_backendType = context.backendType; + m_builtinShader = Resources::ResourceManager::Get().Load(m_builtinShaderPath); + if (!m_builtinShader.IsValid()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase failed to load builtin shader resource: ") + m_builtinShaderPath).CStr()); + DestroyResources(); + return false; + } + + return true; +} + +void BuiltinDepthStylePassBase::DestroyResources() { + m_resourceCache.Shutdown(); + + for (auto& descriptorSetEntry : m_perObjectSets) { + DestroyOwnedDescriptorSet(descriptorSetEntry.second); + } + m_perObjectSets.clear(); + + for (auto& pipelineEntry : m_pipelineStates) { + if (pipelineEntry.second != nullptr) { + pipelineEntry.second->Shutdown(); + delete pipelineEntry.second; + } + } + m_pipelineStates.clear(); + + for (auto& layoutEntry : m_passResourceLayouts) { + DestroyPassResourceLayout(layoutEntry.second); + } + m_passResourceLayouts.clear(); + + m_device = nullptr; + m_backendType = RHI::RHIType::D3D12; + m_builtinShader.Reset(); +} + +BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::ResolveSurfaceShaderPass( + const Resources::Material* material) const { + ResolvedShaderPass resolved = {}; + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); + + auto tryResolveFromShader = + [this, backend, &resolved]( + const Resources::Shader* shader, + const Resources::Material* ownerMaterial) -> bool { + if (shader == nullptr) { + return false; + } + + auto tryAcceptPass = + [this, shader, &resolved](const Resources::ShaderPass& shaderPass) -> bool { + BuiltinPassResourceBindingPlan bindingPlan = {}; + Containers::String error; + if (!TryBuildSupportedBindingPlan(shaderPass, bindingPlan, &error)) { + return false; + } + + resolved.shader = shader; + resolved.pass = &shaderPass; + resolved.passName = shaderPass.name; + return true; + }; + + if (ownerMaterial != nullptr && !ownerMaterial->GetShaderPass().Empty()) { + const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetShaderPass()); + if (explicitPass != nullptr && + ShaderPassMatchesBuiltinPass(*explicitPass, m_passType) && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( + *shader, + explicitPass->name, + backend) && + tryAcceptPass(*explicitPass)) { + return true; + } + } + + for (const Resources::ShaderPass& shaderPass : shader->GetPasses()) { + if (!ShaderPassMatchesBuiltinPass(shaderPass, m_passType) || + !::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(*shader, shaderPass.name, backend)) { + continue; + } + + if (tryAcceptPass(shaderPass)) { + return true; + } + } + + return false; + }; + + if (material != nullptr && + material->GetShader() != nullptr && + tryResolveFromShader(material->GetShader(), material)) { + return resolved; + } + + if (material != nullptr && IsTransparentRenderQueue(ResolveMaterialRenderQueue(material))) { + return {}; + } + + if (m_builtinShader.IsValid() && + tryResolveFromShader(m_builtinShader.Get(), nullptr)) { + return resolved; + } + + return {}; +} + +bool BuiltinDepthStylePassBase::TryBuildSupportedBindingPlan( + const Resources::ShaderPass& shaderPass, + BuiltinPassResourceBindingPlan& outPlan, + Containers::String* outError) const { + if (shaderPass.resources.Empty()) { + if (outError != nullptr) { + *outError = + Containers::String("Builtin depth-style pass requires explicit resource bindings on shader pass: ") + + shaderPass.name; + } + return false; + } + + if (!TryBuildBuiltinPassResourceBindingPlan(shaderPass.resources, outPlan, outError)) { + return false; + } + + if (!IsSupportedPerObjectOnlyBindingPlan(outPlan)) { + if (outError != nullptr) { + *outError = "Builtin depth-style pass currently requires exactly one PerObject constant-buffer binding"; + } + return false; + } + + return true; +} + +BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::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 (!TryBuildSupportedBindingPlan(*resolvedShaderPass.pass, bindingPlan, &bindingPlanError)) { + const Containers::String contextualError = + Containers::String("BuiltinDepthStylePassBase failed to resolve pass resource bindings for shader='") + + resolvedShaderPass.shader->GetPath() + + "', pass='" + resolvedShaderPass.passName + + "': " + bindingPlanError + + ". Bindings: " + DescribeShaderResourceBindings(resolvedShaderPass.pass->resources); + return failLayout(contextualError.CStr()); + } + + std::vector setLayouts; + Containers::String setLayoutError; + if (!TryBuildBuiltinPassSetLayouts(bindingPlan, setLayouts, &setLayoutError)) { + return failLayout(setLayoutError.CStr()); + } + + if (bindingPlan.perObject.set >= setLayouts.size()) { + return failLayout("Builtin depth-style pass produced an invalid PerObject descriptor set index"); + } + + passLayout.perObject = bindingPlan.perObject; + passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet; + passLayout.perObjectSetLayout = setLayouts[bindingPlan.perObject.set]; + RefreshBuiltinPassSetLayoutMetadata(passLayout.perObjectSetLayout); + + std::vector nativeSetLayouts(setLayouts.size()); + for (size_t setIndex = 0; setIndex < setLayouts.size(); ++setIndex) { + nativeSetLayouts[setIndex] = 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("Builtin depth-style pass failed to create pipeline layout"); + } + + const auto result = m_passResourceLayouts.emplace(passLayoutKey, std::move(passLayout)); + RefreshBuiltinPassSetLayoutMetadata(result.first->second.perObjectSetLayout); + return &result.first->second; +} + +RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState( + const RenderContext& context, + const RenderSurface& surface, + const Resources::Material* material) { + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(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 = + material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState(); + pipelineKey.shader = resolvedShaderPass.shader; + pipelineKey.passName = resolvedShaderPass.passName; + pipelineKey.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface); + pipelineKey.renderTargetFormat = static_cast(ResolveSurfaceColorFormat(surface)); + pipelineKey.depthStencilFormat = static_cast(ResolveSurfaceDepthFormat(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.passName, + material, + surface, + BuildCommonInputLayout()); + 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 BuiltinDepthStylePassBase::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; +} + +RHI::RHIDescriptorSet* BuiltinDepthStylePassBase::GetOrCreatePerObjectSet( + const PassLayoutKey& passLayoutKey, + const PassResourceLayout& passLayout, + Core::uint64 objectId) { + if (!passLayout.perObject.IsValid() || + passLayout.perObjectSetLayout.layout.bindingCount == 0) { + return nullptr; + } + + PerObjectSetKey key = {}; + key.passLayout = passLayoutKey; + key.objectId = objectId; + + const auto existing = m_perObjectSets.find(key); + if (existing != m_perObjectSets.end()) { + return existing->second.set; + } + + OwnedDescriptorSet descriptorSet = {}; + if (!CreateOwnedDescriptorSet(passLayout.perObjectSetLayout, descriptorSet)) { + return nullptr; + } + + const auto result = m_perObjectSets.emplace(key, descriptorSet); + return result.first->second.set; +} + +void BuiltinDepthStylePassBase::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 BuiltinDepthStylePassBase::DestroyPassResourceLayout(PassResourceLayout& passLayout) { + if (passLayout.pipelineLayout != nullptr) { + passLayout.pipelineLayout->Shutdown(); + delete passLayout.pipelineLayout; + passLayout.pipelineLayout = nullptr; + } + + passLayout.perObject = {}; + passLayout.perObjectSetLayout = {}; + passLayout.firstDescriptorSet = 0; +} + +bool BuiltinDepthStylePassBase::DrawVisibleItem( + const RenderContext& context, + const RenderSurface& surface, + const RenderSceneData& sceneData, + const VisibleRenderItem& visibleItem) { + if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinDepthStylePassBase skipped visible item because mesh or game object was null"); + return false; + } + + const RenderResourceCache::CachedMesh* cachedMesh = + m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh); + if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase failed to cache mesh for ") + + visibleItem.gameObject->GetName().c_str()).CStr()); + return false; + } + + const Resources::Material* material = ResolveMaterial(visibleItem); + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); + if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase could not resolve shader pass for ") + + visibleItem.gameObject->GetName().c_str()).CStr()); + return false; + } + + PassLayoutKey passLayoutKey = {}; + passLayoutKey.shader = resolvedShaderPass.shader; + passLayoutKey.passName = resolvedShaderPass.passName; + + PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass); + if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase failed to create pass layout for ") + + visibleItem.gameObject->GetName().c_str()).CStr()); + return false; + } + + RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material); + if (pipelineState == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase failed to create pipeline state for ") + + visibleItem.gameObject->GetName().c_str() + + " using pass " + resolvedShaderPass.passName).CStr()); + return false; + } + + RHI::RHICommandList* commandList = context.commandList; + commandList->SetPipelineState(pipelineState); + + RHI::RHIResourceView* vertexBuffers[] = { cachedMesh->vertexBufferView }; + const uint64_t offsets[] = { 0 }; + const uint32_t strides[] = { cachedMesh->vertexStride }; + commandList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); + if (cachedMesh->indexBufferView != nullptr) { + commandList->SetIndexBuffer(cachedMesh->indexBufferView, 0); + } + + RHI::RHIDescriptorSet* constantSet = GetOrCreatePerObjectSet( + passLayoutKey, + *passLayout, + visibleItem.gameObject->GetID()); + if (constantSet == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase failed to allocate descriptor set for ") + + visibleItem.gameObject->GetName().c_str()).CStr()); + return false; + } + + const Math::Matrix4x4 projectionMatrix = + m_passType == BuiltinMaterialPass::ShadowCaster + ? sceneData.cameraData.viewProjection + : sceneData.cameraData.projection; + const Math::Matrix4x4 viewMatrix = + m_passType == BuiltinMaterialPass::ShadowCaster + ? Math::Matrix4x4::Identity() + : sceneData.cameraData.view; + const PerObjectConstants constants = { + projectionMatrix, + viewMatrix, + visibleItem.localToWorld.Transpose() + }; + constantSet->WriteConstant(passLayout->perObject.binding, &constants, sizeof(constants)); + + RHI::RHIDescriptorSet* descriptorSets[] = { constantSet }; + commandList->SetGraphicsDescriptorSets( + passLayout->firstDescriptorSet, + 1, + descriptorSets, + passLayout->pipelineLayout); + + if (visibleItem.hasSection) { + const Containers::Array& sections = visibleItem.mesh->GetSections(); + if (visibleItem.sectionIndex >= sections.Size()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinDepthStylePassBase received invalid mesh section for ") + + visibleItem.gameObject->GetName().c_str()).CStr()); + return false; + } + + const Resources::MeshSection& section = sections[visibleItem.sectionIndex]; + if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) { + commandList->DrawIndexed(section.indexCount, 1, section.startIndex, 0, 0); + } else if (section.vertexCount > 0) { + commandList->Draw(section.vertexCount, 1, section.baseVertex, 0); + } + return true; + } + + if (cachedMesh->indexBufferView != nullptr && cachedMesh->indexCount > 0) { + commandList->DrawIndexed(cachedMesh->indexCount, 1, 0, 0, 0); + } else if (cachedMesh->vertexCount > 0) { + commandList->Draw(cachedMesh->vertexCount, 1, 0, 0); + } + + return true; +} + +} // namespace Passes +} // namespace Rendering +} // namespace XCEngine