diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index bc789fe2..7394a01e 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -517,6 +517,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSkybox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp # Scene diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index e8ffa5fb..2642baff 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -3,7 +3,7 @@ #include "Debug/Logger.h" #include "Core/Asset/ResourceManager.h" #include "RHI/RHICommandList.h" -#include "Rendering/Detail/ShaderVariantUtils.h" +#include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Materials/RenderMaterialResolve.h" #include "Rendering/Passes/BuiltinVolumetricPass.h" #include "Rendering/RenderSurface.h" @@ -11,12 +11,11 @@ #include #include -#include namespace XCEngine { namespace Rendering { namespace Pipelines { -namespace Detail { +namespace Internal { class BuiltinForwardOpaquePass final : public RenderPass { public: @@ -96,7 +95,7 @@ private: BuiltinForwardPipeline& m_pipeline; }; -} // namespace Detail +} // namespace Internal namespace { @@ -105,83 +104,30 @@ bool IsDepthFormat(RHI::Format format) { format == RHI::Format::D32_Float; } -const Resources::ShaderPass* FindCompatibleSkyboxPass( - const Resources::Shader& shader, - Resources::ShaderBackend backend) { - const Resources::ShaderPass* skyboxPass = shader.FindPass("Skybox"); - if (skyboxPass != nullptr && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, skyboxPass->name, backend)) { - return skyboxPass; - } - - if (shader.GetPassCount() > 0 && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { - return &shader.GetPasses()[0]; - } - - return nullptr; -} - -RHI::GraphicsPipelineDesc CreateSkyboxPipelineDesc( - RHI::RHIType backendType, - RHI::RHIPipelineLayout* pipelineLayout, - const Resources::Shader& shader, - const Containers::String& passName) { - RHI::GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); - pipelineDesc.renderTargetCount = 1; - pipelineDesc.renderTargetFormats[0] = static_cast(RHI::Format::R8G8B8A8_UNorm); - pipelineDesc.depthStencilFormat = static_cast(RHI::Format::D24_UNorm_S8_UInt); - pipelineDesc.sampleCount = 1; - - pipelineDesc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); - pipelineDesc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); - pipelineDesc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); - pipelineDesc.rasterizerState.depthClipEnable = true; - - pipelineDesc.blendState.blendEnable = false; - pipelineDesc.blendState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); - - pipelineDesc.depthStencilState.depthTestEnable = true; - pipelineDesc.depthStencilState.depthWriteEnable = false; - pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); - - const Resources::ShaderPass* shaderPass = shader.FindPass(passName); - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); - if (const Resources::ShaderStageVariant* vertexVariant = - shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Detail::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *vertexVariant, - pipelineDesc.vertexShader); - } - } - if (const Resources::ShaderStageVariant* fragmentVariant = - shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Detail::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *fragmentVariant, - pipelineDesc.fragmentShader); +std::vector CollectSurfaceColorAttachments(const RenderSurface& surface) { + std::vector renderTargets; + const Core::uint32 colorAttachmentCount = + ::XCEngine::Rendering::Internal::ResolveSurfaceColorAttachmentCount(surface); + renderTargets.reserve(colorAttachmentCount); + for (Core::uint32 attachmentIndex = 0; attachmentIndex < colorAttachmentCount; ++attachmentIndex) { + RHI::RHIResourceView* renderTarget = surface.GetColorAttachments()[attachmentIndex]; + if (renderTarget == nullptr) { + break; } + + renderTargets.push_back(renderTarget); } - return pipelineDesc; + return renderTargets; } } // namespace BuiltinForwardPipeline::BuiltinForwardPipeline() { - m_passSequence.AddPass(std::make_unique(*this)); - m_passSequence.AddPass(std::make_unique(*this)); + m_passSequence.AddPass(std::make_unique(*this)); + m_passSequence.AddPass(std::make_unique(*this)); m_passSequence.AddPass(std::make_unique()); - m_passSequence.AddPass(std::make_unique(*this)); + m_passSequence.AddPass(std::make_unique(*this)); } BuiltinForwardPipeline::~BuiltinForwardPipeline() { @@ -241,35 +187,34 @@ bool BuiltinForwardPipeline::Render( const RenderPassContext passContext = { context, surface, - sceneData + sceneData, + nullptr, + nullptr, + RHI::ResourceStates::Common }; return m_passSequence.Execute(passContext); } -bool BuiltinForwardPipeline::HasProceduralSkybox(const RenderSceneData& sceneData) const { - return sceneData.environment.HasProceduralSkybox() && - sceneData.cameraData.perspectiveProjection; -} - -bool BuiltinForwardPipeline::HasSkybox(const RenderSceneData& sceneData) const { - return sceneData.environment.HasSkybox() && - sceneData.cameraData.perspectiveProjection; -} - bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; + RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); - const std::vector& colorAttachments = surface.GetColorAttachments(); - if (colorAttachments.empty()) { + std::vector renderTargets = CollectSurfaceColorAttachments(surface); + if (renderTargets.empty()) { + return false; + } + + const Math::RectInt renderArea = surface.GetRenderArea(); + if (renderArea.width <= 0 || renderArea.height <= 0) { return false; } RHI::RHICommandList* commandList = context.commandList; if (surface.IsAutoTransitionEnabled()) { - for (RHI::RHIResourceView* renderTarget : colorAttachments) { + for (RHI::RHIResourceView* renderTarget : renderTargets) { if (renderTarget != nullptr) { commandList->TransitionBarrier( renderTarget, @@ -277,18 +222,19 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass RHI::ResourceStates::RenderTarget); } } + + if (depthAttachment != nullptr) { + commandList->TransitionBarrier( + depthAttachment, + surface.GetDepthStateBefore(), + RHI::ResourceStates::DepthWrite); + } } - std::vector renderTargets = colorAttachments; commandList->SetRenderTargets( static_cast(renderTargets.size()), renderTargets.data(), - surface.GetDepthAttachment()); - - const Math::RectInt renderArea = surface.GetRenderArea(); - if (renderArea.width <= 0 || renderArea.height <= 0) { - return false; - } + depthAttachment); const RHI::Viewport viewport = { static_cast(renderArea.x), @@ -320,9 +266,9 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass } } } - if (surface.GetDepthAttachment() != nullptr && + if (depthAttachment != nullptr && HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) { - commandList->ClearDepthStencil(surface.GetDepthAttachment(), 1.0f, 0, 1, clearRects); + commandList->ClearDepthStencil(depthAttachment, 1.0f, 0, 1, clearRects); } commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); @@ -332,15 +278,16 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass void BuiltinForwardPipeline::EndForwardScenePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; - const std::vector& colorAttachments = surface.GetColorAttachments(); + RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); + std::vector renderTargets = CollectSurfaceColorAttachments(surface); RHI::RHICommandList* commandList = context.commandList; + commandList->EndRenderPass(); if (!surface.IsAutoTransitionEnabled()) { return; } - commandList->EndRenderPass(); - for (RHI::RHIResourceView* renderTarget : colorAttachments) { + for (RHI::RHIResourceView* renderTarget : renderTargets) { if (renderTarget != nullptr) { commandList->TransitionBarrier( renderTarget, @@ -348,10 +295,18 @@ void BuiltinForwardPipeline::EndForwardScenePass(const RenderPassContext& passCo surface.GetColorStateAfter()); } } + + if (depthAttachment != nullptr) { + commandList->TransitionBarrier( + depthAttachment, + RHI::ResourceStates::DepthWrite, + surface.GetDepthStateAfter()); + } } bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; + const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; if (!BeginForwardScenePass(passContext)) { @@ -366,131 +321,15 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p RHI::ResourceStates::PixelShaderResource); } - return DrawVisibleItems(context, sceneData, false); -} - -bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& passContext) { - const RenderContext& context = passContext.renderContext; - const RenderSurface& surface = passContext.surface; - const RenderSceneData& sceneData = passContext.sceneData; - - if (!HasSkybox(sceneData)) { - return true; - } - if (surface.GetDepthAttachment() == nullptr) { - return true; - } - - if (!EnsureSkyboxResources(context) || - m_skyboxPipelineLayout == nullptr || - m_skyboxPipelineState == nullptr || - m_skyboxEnvironmentSet.set == nullptr || - m_skyboxMaterialSet.set == nullptr || - m_skyboxPanoramicTextureSet.set == nullptr || - m_skyboxCubemapTextureSet.set == nullptr || - m_skyboxSamplerSet.set == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline skybox pass failed to initialize runtime skybox resources"); - return false; - } - - SkyboxConstants constants = {}; - constants.topColor = sceneData.environment.skybox.topColor.ToVector4(); - constants.horizonColor = sceneData.environment.skybox.horizonColor.ToVector4(); - constants.bottomColor = sceneData.environment.skybox.bottomColor.ToVector4(); - constants.cameraRightAndTanHalfFov = Math::Vector4( - sceneData.cameraData.worldRight, - std::tan(sceneData.cameraData.verticalFovRadians * 0.5f)); - constants.cameraUpAndAspect = Math::Vector4( - sceneData.cameraData.worldUp, - sceneData.cameraData.aspectRatio); - constants.cameraForwardAndUnused = Math::Vector4(sceneData.cameraData.worldForward, 0.0f); - - const Resources::Material* skyboxMaterial = sceneData.environment.HasMaterialSkybox() - ? sceneData.environment.materialSkybox.material - : nullptr; - const Resources::Texture* panoramicSkyboxTexture = ResolveSkyboxPanoramicTexture(skyboxMaterial); - const Resources::Texture* cubemapSkyboxTexture = ResolveSkyboxCubemapTexture(skyboxMaterial); - RHI::RHIResourceView* panoramicSkyboxTextureView = ResolveTextureView(panoramicSkyboxTexture); - RHI::RHIResourceView* cubemapSkyboxTextureView = ResolveTextureView(cubemapSkyboxTexture); - - const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial); - BuiltinSkyboxTextureMode activeTextureMode = BuiltinSkyboxTextureMode::None; - switch (skyboxMaterialData.textureMode) { - case BuiltinSkyboxTextureMode::Panoramic: - if (panoramicSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; - } else if (cubemapSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; - } - break; - case BuiltinSkyboxTextureMode::Cubemap: - if (cubemapSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; - } else if (panoramicSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; - } - break; - case BuiltinSkyboxTextureMode::None: - default: - if (panoramicSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; - } else if (cubemapSkyboxTextureView != nullptr) { - activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; - } - break; - } - if (panoramicSkyboxTextureView == nullptr) { - panoramicSkyboxTextureView = m_fallbackTexture2DView; - } - if (cubemapSkyboxTextureView == nullptr) { - cubemapSkyboxTextureView = m_fallbackTextureCubeView; - } - - SkyboxMaterialConstants materialConstants = {}; - materialConstants.tintAndExposure = Math::Vector4( - skyboxMaterialData.tint.x, - skyboxMaterialData.tint.y, - skyboxMaterialData.tint.z, - std::max(0.0f, skyboxMaterialData.exposure)); - materialConstants.rotationRadiansAndMode = Math::Vector4( - Math::Radians(skyboxMaterialData.rotationDegrees), - static_cast(activeTextureMode), - 0.0f, - 0.0f); - - m_skyboxEnvironmentSet.set->WriteConstant(0, &constants, sizeof(constants)); - m_skyboxMaterialSet.set->WriteConstant(0, &materialConstants, sizeof(materialConstants)); - if (m_skyboxBoundPanoramicTextureView != panoramicSkyboxTextureView) { - m_skyboxPanoramicTextureSet.set->Update(0, panoramicSkyboxTextureView); - m_skyboxBoundPanoramicTextureView = panoramicSkyboxTextureView; - } - if (m_skyboxBoundCubemapTextureView != cubemapSkyboxTextureView) { - m_skyboxCubemapTextureSet.set->Update(0, cubemapSkyboxTextureView); - m_skyboxBoundCubemapTextureView = cubemapSkyboxTextureView; - } - - RHI::RHICommandList* commandList = context.commandList; - commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); - commandList->SetPipelineState(m_skyboxPipelineState); - RHI::RHIDescriptorSet* descriptorSets[] = { - m_skyboxEnvironmentSet.set, - m_skyboxMaterialSet.set, - m_skyboxPanoramicTextureSet.set, - m_skyboxCubemapTextureSet.set, - m_skyboxSamplerSet.set - }; - commandList->SetGraphicsDescriptorSets(0, 5, descriptorSets, m_skyboxPipelineLayout); - commandList->Draw(3, 1, 0, 0); - return true; + return DrawVisibleItems(context, surface, sceneData, false); } bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; + const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; - const bool drawResult = DrawVisibleItems(context, sceneData, true); + const bool drawResult = DrawVisibleItems(context, surface, sceneData, true); if (sceneData.lighting.HasMainDirectionalShadow() && IsDepthFormat(sceneData.lighting.mainDirectionalShadow.shadowMap->GetFormat())) { @@ -507,6 +346,7 @@ bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(const RenderPassConte bool BuiltinForwardPipeline::DrawVisibleItems( const RenderContext& context, + const RenderSurface& surface, const RenderSceneData& sceneData, bool drawTransparentItems) { RHI::RHICommandList* commandList = context.commandList; @@ -523,7 +363,7 @@ bool BuiltinForwardPipeline::DrawVisibleItems( continue; } - RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, sceneData, material); + RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material); if (pipelineState == nullptr) { continue; } @@ -532,7 +372,7 @@ bool BuiltinForwardPipeline::DrawVisibleItems( currentPipelineState = pipelineState; } - DrawVisibleItem(context, sceneData, visibleItem); + DrawVisibleItem(context, surface, sceneData, visibleItem); } return true; @@ -678,194 +518,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex return true; } -bool BuiltinForwardPipeline::EnsureSkyboxResources(const RenderContext& context) { - if (m_skyboxPipelineLayout != nullptr && - m_skyboxPipelineState != nullptr && - m_skyboxEnvironmentSet.set != nullptr && - m_skyboxMaterialSet.set != nullptr && - m_skyboxPanoramicTextureSet.set != nullptr && - m_skyboxCubemapTextureSet.set != nullptr && - m_skyboxSamplerSet.set != nullptr) { - return true; - } - - DestroySkyboxResources(); - return CreateSkyboxResources(context); -} - -bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context) { - if (!context.IsValid() || !m_builtinSkyboxShader.IsValid()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline skybox resources require a valid context and loaded shader"); - return false; - } - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); - const Resources::ShaderPass* skyboxPass = - FindCompatibleSkyboxPass(*m_builtinSkyboxShader.Get(), backend); - if (skyboxPass == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline could not resolve a valid skybox shader pass"); - return false; - } - - RHI::DescriptorSetLayoutBinding environmentBinding = {}; - environmentBinding.binding = 0; - environmentBinding.type = static_cast(RHI::DescriptorType::CBV); - environmentBinding.count = 1; - environmentBinding.visibility = static_cast(RHI::ShaderVisibility::All); - - RHI::DescriptorSetLayoutBinding materialBinding = environmentBinding; - - RHI::DescriptorSetLayoutBinding textureBinding = {}; - textureBinding.binding = 0; - textureBinding.type = static_cast(RHI::DescriptorType::SRV); - textureBinding.count = 1; - textureBinding.visibility = static_cast(RHI::ShaderVisibility::All); - - RHI::DescriptorSetLayoutBinding samplerBinding = {}; - samplerBinding.binding = 0; - samplerBinding.type = static_cast(RHI::DescriptorType::Sampler); - samplerBinding.count = 1; - samplerBinding.visibility = static_cast(RHI::ShaderVisibility::All); - - RHI::DescriptorSetLayoutDesc environmentLayout = {}; - environmentLayout.bindings = &environmentBinding; - environmentLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc materialLayout = {}; - materialLayout.bindings = &materialBinding; - materialLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc panoramicTextureLayout = {}; - panoramicTextureLayout.bindings = &textureBinding; - panoramicTextureLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc cubemapTextureLayout = {}; - cubemapTextureLayout.bindings = &textureBinding; - cubemapTextureLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc samplerLayout = {}; - samplerLayout.bindings = &samplerBinding; - samplerLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc setLayouts[] = { - environmentLayout, - materialLayout, - panoramicTextureLayout, - cubemapTextureLayout, - samplerLayout - }; - - RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = setLayouts; - pipelineLayoutDesc.setLayoutCount = 5; - m_skyboxPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - if (m_skyboxPipelineLayout == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline failed to create skybox pipeline layout"); - DestroySkyboxResources(); - return false; - } - - auto createSkyboxDescriptorSet = - [this](const RHI::DescriptorSetLayoutDesc& layout, - RHI::DescriptorHeapType heapType, - bool shaderVisible, - OwnedDescriptorSet& ownedSet) -> bool { - RHI::DescriptorPoolDesc poolDesc = {}; - poolDesc.type = heapType; - poolDesc.descriptorCount = 1; - poolDesc.shaderVisible = shaderVisible; - ownedSet.pool = m_device->CreateDescriptorPool(poolDesc); - if (ownedSet.pool == nullptr) { - return false; - } - - ownedSet.set = ownedSet.pool->AllocateSet(layout); - return ownedSet.set != nullptr; - }; - - if (!createSkyboxDescriptorSet( - environmentLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - false, - m_skyboxEnvironmentSet) || - !createSkyboxDescriptorSet( - materialLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - false, - m_skyboxMaterialSet) || - !createSkyboxDescriptorSet( - panoramicTextureLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - true, - m_skyboxPanoramicTextureSet) || - !createSkyboxDescriptorSet( - cubemapTextureLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - true, - m_skyboxCubemapTextureSet) || - !createSkyboxDescriptorSet( - samplerLayout, - RHI::DescriptorHeapType::Sampler, - true, - m_skyboxSamplerSet)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline failed to allocate skybox descriptor sets"); - DestroySkyboxResources(); - return false; - } - - m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler); - m_skyboxPanoramicTextureSet.set->Update(0, m_fallbackTexture2DView); - m_skyboxCubemapTextureSet.set->Update(0, m_fallbackTextureCubeView); - m_skyboxBoundPanoramicTextureView = m_fallbackTexture2DView; - m_skyboxBoundCubemapTextureView = m_fallbackTextureCubeView; - - m_skyboxPipelineState = m_device->CreatePipelineState( - CreateSkyboxPipelineDesc( - m_backendType, - m_skyboxPipelineLayout, - *m_builtinSkyboxShader.Get(), - skyboxPass->name)); - if (m_skyboxPipelineState == nullptr || !m_skyboxPipelineState->IsValid()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline failed to create skybox graphics pipeline state"); - DestroySkyboxResources(); - return false; - } - - return true; -} - -void BuiltinForwardPipeline::DestroySkyboxResources() { - if (m_skyboxPipelineState != nullptr) { - m_skyboxPipelineState->Shutdown(); - delete m_skyboxPipelineState; - m_skyboxPipelineState = nullptr; - } - - DestroyOwnedDescriptorSet(m_skyboxSamplerSet); - DestroyOwnedDescriptorSet(m_skyboxCubemapTextureSet); - DestroyOwnedDescriptorSet(m_skyboxPanoramicTextureSet); - DestroyOwnedDescriptorSet(m_skyboxMaterialSet); - DestroyOwnedDescriptorSet(m_skyboxEnvironmentSet); - m_skyboxBoundPanoramicTextureView = nullptr; - m_skyboxBoundCubemapTextureView = nullptr; - - if (m_skyboxPipelineLayout != nullptr) { - m_skyboxPipelineLayout->Shutdown(); - delete m_skyboxPipelineLayout; - m_skyboxPipelineLayout = nullptr; - } -} - void BuiltinForwardPipeline::DestroyPipelineResources() { m_resourceCache.Shutdown(); diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSkybox.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSkybox.cpp new file mode 100644 index 00000000..6ce6a867 --- /dev/null +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSkybox.cpp @@ -0,0 +1,447 @@ +#include "Rendering/Pipelines/BuiltinForwardPipeline.h" + +#include "Debug/Logger.h" +#include "RHI/RHICommandList.h" +#include "RHI/RHIDevice.h" +#include "Rendering/Internal/RenderSurfacePipelineUtils.h" +#include "Rendering/Internal/ShaderVariantUtils.h" +#include "Rendering/RenderSurface.h" +#include "Resources/Shader/Shader.h" + +#include +#include + +namespace XCEngine { +namespace Rendering { +namespace Pipelines { +namespace { + +const Resources::ShaderPass* FindCompatibleSkyboxPass( + const Resources::Shader& shader, + Resources::ShaderBackend backend) { + const Resources::ShaderPass* skyboxPass = shader.FindPass("Skybox"); + if (skyboxPass != nullptr && + ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, skyboxPass->name, backend)) { + return skyboxPass; + } + + if (shader.GetPassCount() > 0 && + ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { + return &shader.GetPasses()[0]; + } + + return nullptr; +} + +RHI::GraphicsPipelineDesc CreateSkyboxPipelineDesc( + RHI::RHIType backendType, + RHI::RHIPipelineLayout* pipelineLayout, + const Resources::Shader& shader, + const Containers::String& passName, + const RenderSurface& surface) { + RHI::GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); + ::XCEngine::Rendering::Internal::ApplySurfacePropertiesToGraphicsPipelineDesc(surface, pipelineDesc); + + pipelineDesc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); + pipelineDesc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); + pipelineDesc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); + pipelineDesc.rasterizerState.depthClipEnable = true; + + pipelineDesc.blendState.blendEnable = false; + pipelineDesc.blendState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); + + pipelineDesc.depthStencilState.depthTestEnable = true; + pipelineDesc.depthStencilState.depthWriteEnable = false; + pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); + + const Resources::ShaderPass* shaderPass = shader.FindPass(passName); + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); + if (const Resources::ShaderStageVariant* vertexVariant = + shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { + if (shaderPass != nullptr) { + ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *vertexVariant, + pipelineDesc.vertexShader); + } + } + if (const Resources::ShaderStageVariant* fragmentVariant = + shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { + if (shaderPass != nullptr) { + ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *fragmentVariant, + pipelineDesc.fragmentShader); + } + } + + return pipelineDesc; +} + +} // namespace + +bool BuiltinForwardPipeline::HasProceduralSkybox(const RenderSceneData& sceneData) const { + return sceneData.environment.HasProceduralSkybox() && + sceneData.cameraData.perspectiveProjection; +} + +bool BuiltinForwardPipeline::HasSkybox(const RenderSceneData& sceneData) const { + return sceneData.environment.HasSkybox() && + sceneData.cameraData.perspectiveProjection; +} + +bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& passContext) { + const RenderContext& context = passContext.renderContext; + const RenderSurface& surface = passContext.surface; + const RenderSceneData& sceneData = passContext.sceneData; + + if (!HasSkybox(sceneData)) { + return true; + } + if (surface.GetDepthAttachment() == nullptr) { + return true; + } + + if (!EnsureSkyboxResources(context) || + m_skyboxPipelineLayout == nullptr || + m_skyboxEnvironmentSet.set == nullptr || + m_skyboxMaterialSet.set == nullptr || + m_skyboxPanoramicTextureSet.set == nullptr || + m_skyboxCubemapTextureSet.set == nullptr || + m_skyboxSamplerSet.set == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline skybox pass failed to initialize runtime skybox resources"); + return false; + } + + RHI::RHIPipelineState* skyboxPipelineState = GetOrCreateSkyboxPipelineState(context, surface); + if (skyboxPipelineState == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline skybox pass failed to create a surface-compatible pipeline state"); + return false; + } + + SkyboxConstants constants = {}; + constants.topColor = sceneData.environment.skybox.topColor.ToVector4(); + constants.horizonColor = sceneData.environment.skybox.horizonColor.ToVector4(); + constants.bottomColor = sceneData.environment.skybox.bottomColor.ToVector4(); + constants.cameraRightAndTanHalfFov = Math::Vector4( + sceneData.cameraData.worldRight, + std::tan(sceneData.cameraData.verticalFovRadians * 0.5f)); + constants.cameraUpAndAspect = Math::Vector4( + sceneData.cameraData.worldUp, + sceneData.cameraData.aspectRatio); + constants.cameraForwardAndUnused = Math::Vector4(sceneData.cameraData.worldForward, 0.0f); + + const Resources::Material* skyboxMaterial = sceneData.environment.HasMaterialSkybox() + ? sceneData.environment.materialSkybox.material + : nullptr; + const Resources::Texture* panoramicSkyboxTexture = ResolveSkyboxPanoramicTexture(skyboxMaterial); + const Resources::Texture* cubemapSkyboxTexture = ResolveSkyboxCubemapTexture(skyboxMaterial); + RHI::RHIResourceView* panoramicSkyboxTextureView = ResolveTextureView(panoramicSkyboxTexture); + RHI::RHIResourceView* cubemapSkyboxTextureView = ResolveTextureView(cubemapSkyboxTexture); + + const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial); + BuiltinSkyboxTextureMode activeTextureMode = BuiltinSkyboxTextureMode::None; + switch (skyboxMaterialData.textureMode) { + case BuiltinSkyboxTextureMode::Panoramic: + if (panoramicSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; + } else if (cubemapSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; + } + break; + case BuiltinSkyboxTextureMode::Cubemap: + if (cubemapSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; + } else if (panoramicSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; + } + break; + case BuiltinSkyboxTextureMode::None: + default: + if (panoramicSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; + } else if (cubemapSkyboxTextureView != nullptr) { + activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; + } + break; + } + if (panoramicSkyboxTextureView == nullptr) { + panoramicSkyboxTextureView = m_fallbackTexture2DView; + } + if (cubemapSkyboxTextureView == nullptr) { + cubemapSkyboxTextureView = m_fallbackTextureCubeView; + } + + SkyboxMaterialConstants materialConstants = {}; + materialConstants.tintAndExposure = Math::Vector4( + skyboxMaterialData.tint.x, + skyboxMaterialData.tint.y, + skyboxMaterialData.tint.z, + std::max(0.0f, skyboxMaterialData.exposure)); + materialConstants.rotationRadiansAndMode = Math::Vector4( + Math::Radians(skyboxMaterialData.rotationDegrees), + static_cast(activeTextureMode), + 0.0f, + 0.0f); + + m_skyboxEnvironmentSet.set->WriteConstant(0, &constants, sizeof(constants)); + m_skyboxMaterialSet.set->WriteConstant(0, &materialConstants, sizeof(materialConstants)); + if (m_skyboxBoundPanoramicTextureView != panoramicSkyboxTextureView) { + m_skyboxPanoramicTextureSet.set->Update(0, panoramicSkyboxTextureView); + m_skyboxBoundPanoramicTextureView = panoramicSkyboxTextureView; + } + if (m_skyboxBoundCubemapTextureView != cubemapSkyboxTextureView) { + m_skyboxCubemapTextureSet.set->Update(0, cubemapSkyboxTextureView); + m_skyboxBoundCubemapTextureView = cubemapSkyboxTextureView; + } + + RHI::RHICommandList* commandList = context.commandList; + commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); + commandList->SetPipelineState(skyboxPipelineState); + RHI::RHIDescriptorSet* descriptorSets[] = { + m_skyboxEnvironmentSet.set, + m_skyboxMaterialSet.set, + m_skyboxPanoramicTextureSet.set, + m_skyboxCubemapTextureSet.set, + m_skyboxSamplerSet.set + }; + commandList->SetGraphicsDescriptorSets(0, 5, descriptorSets, m_skyboxPipelineLayout); + commandList->Draw(3, 1, 0, 0); + return true; +} + +bool BuiltinForwardPipeline::EnsureSkyboxResources(const RenderContext& context) { + if (m_skyboxPipelineLayout != nullptr && + !m_skyboxPassName.Empty() && + m_skyboxEnvironmentSet.set != nullptr && + m_skyboxMaterialSet.set != nullptr && + m_skyboxPanoramicTextureSet.set != nullptr && + m_skyboxCubemapTextureSet.set != nullptr && + m_skyboxSamplerSet.set != nullptr) { + return true; + } + + DestroySkyboxResources(); + return CreateSkyboxResources(context); +} + +RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreateSkyboxPipelineState( + const RenderContext& context, + const RenderSurface& surface) { + if (!context.IsValid() || + m_skyboxPipelineLayout == nullptr || + !m_builtinSkyboxShader.IsValid() || + m_skyboxPassName.Empty()) { + return nullptr; + } + + SkyboxPipelineStateKey pipelineKey = {}; + pipelineKey.renderTargetCount = + ::XCEngine::Rendering::Internal::ResolveSurfaceColorAttachmentCount(surface); + pipelineKey.renderTargetFormats = + ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormats(surface); + 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_skyboxPipelineStates.find(pipelineKey); + if (existing != m_skyboxPipelineStates.end()) { + return existing->second; + } + + RHI::RHIPipelineState* pipelineState = m_device->CreatePipelineState( + CreateSkyboxPipelineDesc( + m_backendType, + m_skyboxPipelineLayout, + *m_builtinSkyboxShader.Get(), + m_skyboxPassName, + surface)); + if (pipelineState == nullptr || !pipelineState->IsValid()) { + if (pipelineState != nullptr) { + pipelineState->Shutdown(); + delete pipelineState; + } + return nullptr; + } + + m_skyboxPipelineStates.emplace(pipelineKey, pipelineState); + return pipelineState; +} + +bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context) { + if (!context.IsValid() || !m_builtinSkyboxShader.IsValid()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline skybox resources require a valid context and loaded shader"); + return false; + } + + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType); + const Resources::ShaderPass* skyboxPass = + FindCompatibleSkyboxPass(*m_builtinSkyboxShader.Get(), backend); + if (skyboxPass == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline could not resolve a valid skybox shader pass"); + return false; + } + m_skyboxPassName = skyboxPass->name; + + RHI::DescriptorSetLayoutBinding environmentBinding = {}; + environmentBinding.binding = 0; + environmentBinding.type = static_cast(RHI::DescriptorType::CBV); + environmentBinding.count = 1; + environmentBinding.visibility = static_cast(RHI::ShaderVisibility::All); + + RHI::DescriptorSetLayoutBinding materialBinding = environmentBinding; + + RHI::DescriptorSetLayoutBinding textureBinding = {}; + textureBinding.binding = 0; + textureBinding.type = static_cast(RHI::DescriptorType::SRV); + textureBinding.count = 1; + textureBinding.visibility = static_cast(RHI::ShaderVisibility::All); + + RHI::DescriptorSetLayoutBinding samplerBinding = {}; + samplerBinding.binding = 0; + samplerBinding.type = static_cast(RHI::DescriptorType::Sampler); + samplerBinding.count = 1; + samplerBinding.visibility = static_cast(RHI::ShaderVisibility::All); + + RHI::DescriptorSetLayoutDesc environmentLayout = {}; + environmentLayout.bindings = &environmentBinding; + environmentLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc materialLayout = {}; + materialLayout.bindings = &materialBinding; + materialLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc panoramicTextureLayout = {}; + panoramicTextureLayout.bindings = &textureBinding; + panoramicTextureLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc cubemapTextureLayout = {}; + cubemapTextureLayout.bindings = &textureBinding; + cubemapTextureLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc samplerLayout = {}; + samplerLayout.bindings = &samplerBinding; + samplerLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc setLayouts[] = { + environmentLayout, + materialLayout, + panoramicTextureLayout, + cubemapTextureLayout, + samplerLayout + }; + + RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; + pipelineLayoutDesc.setLayouts = setLayouts; + pipelineLayoutDesc.setLayoutCount = 5; + m_skyboxPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); + if (m_skyboxPipelineLayout == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline failed to create skybox pipeline layout"); + DestroySkyboxResources(); + return false; + } + + auto createSkyboxDescriptorSet = + [this](const RHI::DescriptorSetLayoutDesc& layout, + RHI::DescriptorHeapType heapType, + bool shaderVisible, + OwnedDescriptorSet& ownedSet) -> bool { + RHI::DescriptorPoolDesc poolDesc = {}; + poolDesc.type = heapType; + poolDesc.descriptorCount = 1; + poolDesc.shaderVisible = shaderVisible; + ownedSet.pool = m_device->CreateDescriptorPool(poolDesc); + if (ownedSet.pool == nullptr) { + return false; + } + + ownedSet.set = ownedSet.pool->AllocateSet(layout); + return ownedSet.set != nullptr; + }; + + if (!createSkyboxDescriptorSet( + environmentLayout, + RHI::DescriptorHeapType::CBV_SRV_UAV, + false, + m_skyboxEnvironmentSet) || + !createSkyboxDescriptorSet( + materialLayout, + RHI::DescriptorHeapType::CBV_SRV_UAV, + false, + m_skyboxMaterialSet) || + !createSkyboxDescriptorSet( + panoramicTextureLayout, + RHI::DescriptorHeapType::CBV_SRV_UAV, + true, + m_skyboxPanoramicTextureSet) || + !createSkyboxDescriptorSet( + cubemapTextureLayout, + RHI::DescriptorHeapType::CBV_SRV_UAV, + true, + m_skyboxCubemapTextureSet) || + !createSkyboxDescriptorSet( + samplerLayout, + RHI::DescriptorHeapType::Sampler, + true, + m_skyboxSamplerSet)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline failed to allocate skybox descriptor sets"); + DestroySkyboxResources(); + return false; + } + + m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler); + m_skyboxPanoramicTextureSet.set->Update(0, m_fallbackTexture2DView); + m_skyboxCubemapTextureSet.set->Update(0, m_fallbackTextureCubeView); + m_skyboxBoundPanoramicTextureView = m_fallbackTexture2DView; + m_skyboxBoundCubemapTextureView = m_fallbackTextureCubeView; + + return true; +} + +void BuiltinForwardPipeline::DestroySkyboxResources() { + for (auto& pipelinePair : m_skyboxPipelineStates) { + if (pipelinePair.second != nullptr) { + pipelinePair.second->Shutdown(); + delete pipelinePair.second; + } + } + m_skyboxPipelineStates.clear(); + + DestroyOwnedDescriptorSet(m_skyboxSamplerSet); + DestroyOwnedDescriptorSet(m_skyboxCubemapTextureSet); + DestroyOwnedDescriptorSet(m_skyboxPanoramicTextureSet); + DestroyOwnedDescriptorSet(m_skyboxMaterialSet); + DestroyOwnedDescriptorSet(m_skyboxEnvironmentSet); + m_skyboxBoundPanoramicTextureView = nullptr; + m_skyboxBoundCubemapTextureView = nullptr; + m_skyboxPassName.Clear(); + + if (m_skyboxPipelineLayout != nullptr) { + m_skyboxPipelineLayout->Shutdown(); + delete m_skyboxPipelineLayout; + m_skyboxPipelineLayout = nullptr; + } +} + +} // namespace Pipelines +} // namespace Rendering +} // namespace XCEngine