diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index 05e23e86..06f089a2 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -331,7 +331,18 @@ private: bool ExecuteForwardSceneFrame( const FrameExecutionContext& executionContext, bool manageMainDirectionalShadowTransitions); - bool BeginForwardScenePass(const RenderPassContext& context); + bool ExecuteForwardSceneSteps( + const FrameExecutionContext& executionContext, + size_t beginStepIndex, + size_t endStepIndex); + bool ExecuteForwardSceneSegmentPass( + const FrameExecutionContext& executionContext, + size_t beginStepIndex, + size_t endStepIndex, + bool clearAttachments); + bool BeginForwardScenePass( + const RenderPassContext& context, + bool clearAttachments = true); void EndForwardScenePass(const RenderPassContext& context); bool ExecuteForwardOpaquePass(const ScenePhaseExecutionContext& context); bool ExecuteForwardSkyboxPass(const RenderPassContext& context); diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp index 8b324659..3e62740a 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp @@ -6,6 +6,9 @@ #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/RenderSurface.h" +#include +#include + namespace XCEngine { namespace Rendering { namespace Pipelines { @@ -57,6 +60,36 @@ RenderSurface BuildGraphManagedForwardSceneSurface(const RenderSurface& template return surface; } +struct ForwardSceneGraphSegmentDesc { + const char* suffix = ""; + size_t beginStepIndex = 0u; + size_t endStepIndex = 0u; + bool clearAttachments = false; + bool samplesMainDirectionalShadow = false; +}; + +struct ForwardSceneGraphExecutionState { + bool initialized = false; +}; + +const std::array& GetForwardSceneGraphSegments() { + static constexpr std::array kSegments = {{ + { "Opaque", 0u, 3u, true, true }, + { "Skybox", 3u, 6u, false, false }, + { "Transparent", 6u, 9u, false, true } + }}; + return kSegments; +} + +Containers::String BuildForwardSceneSegmentPassName( + const Containers::String& baseName, + const char* suffix) { + std::string name = baseName.CStr(); + name += '.'; + name += suffix != nullptr ? suffix : "Segment"; + return Containers::String(name.c_str()); +} + } // namespace bool BuiltinForwardPipeline::ShouldSampleMainDirectionalShadowMap(const RenderSceneData& sceneData) { @@ -84,21 +117,28 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph( const RenderGraphTextureHandle mainDirectionalShadowTexture = context.mainDirectionalShadowTexture; bool* const executionSucceeded = context.executionSucceeded; + const std::shared_ptr graphExecutionState = + std::make_shared(); - context.graphBuilder.AddRasterPass( - passName, - [this, - surfaceTemplate, - renderContext, - sceneData, - sourceSurface, - sourceColorView, - sourceColorState, - colorTargets, - depthTarget, - mainDirectionalShadowTexture, - executionSucceeded]( - RenderGraphPassBuilder& passBuilder) { + for (const ForwardSceneGraphSegmentDesc& segment : GetForwardSceneGraphSegments()) { + const Containers::String segmentPassName = + BuildForwardSceneSegmentPassName(passName, segment.suffix); + context.graphBuilder.AddRasterPass( + segmentPassName, + [this, + graphExecutionState, + surfaceTemplate, + renderContext, + sceneData, + sourceSurface, + sourceColorView, + sourceColorState, + colorTargets, + depthTarget, + mainDirectionalShadowTexture, + executionSucceeded, + segment]( + RenderGraphPassBuilder& passBuilder) { for (RenderGraphTextureHandle colorTarget : colorTargets) { if (colorTarget.IsValid()) { passBuilder.WriteTexture(colorTarget); @@ -108,20 +148,22 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph( if (depthTarget.IsValid()) { passBuilder.WriteDepthTexture(depthTarget); } - if (mainDirectionalShadowTexture.IsValid()) { + if (segment.samplesMainDirectionalShadow && + mainDirectionalShadowTexture.IsValid()) { passBuilder.ReadTexture(mainDirectionalShadowTexture); } passBuilder.SetExecuteCallback( [this, + graphExecutionState, surfaceTemplate, renderContext, sceneData, sourceSurface, sourceColorView, sourceColorState, - mainDirectionalShadowTexture, - executionSucceeded]( + executionSucceeded, + segment]( const RenderGraphExecutionContext&) { if (executionSucceeded != nullptr && !(*executionSucceeded)) { return; @@ -134,15 +176,40 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph( sourceSurface, sourceColorView, sourceColorState); + if (!graphExecutionState->initialized) { + if (!Initialize(*renderContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: Initialize returned false"); + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + if (!m_forwardSceneFeatureHost.Prepare(executionContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: SceneRenderFeatureHost::Prepare returned false"); + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + graphExecutionState->initialized = true; + } + const bool renderResult = - ExecuteForwardSceneFrame( + ExecuteForwardSceneSegmentPass( executionContext, - !mainDirectionalShadowTexture.IsValid()); + segment.beginStepIndex, + segment.endStepIndex, + segment.clearAttachments); if (executionSucceeded != nullptr) { *executionSucceeded = renderResult; } }); - }); + }); + } return true; } @@ -199,7 +266,9 @@ bool BuiltinForwardPipeline::ExecuteForwardSceneFrame( return renderResult; } -bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) { +bool BuiltinForwardPipeline::BeginForwardScenePass( + const RenderPassContext& passContext, + bool clearAttachments) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; @@ -261,7 +330,8 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass ? surface.GetClearColorOverride() : sceneData.cameraData.clearColor; const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a }; - if (HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color) && + if (clearAttachments && + HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color) && !HasSkybox(sceneData)) { for (RHI::RHIResourceView* renderTarget : renderTargets) { if (renderTarget != nullptr) { @@ -269,7 +339,8 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass } } } - if (depthAttachment != nullptr && + if (clearAttachments && + depthAttachment != nullptr && HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) { commandList->ClearDepthStencil(depthAttachment, 1.0f, 0, 1, clearRects); } diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineScenePhases.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineScenePhases.cpp new file mode 100644 index 00000000..2cefd5e8 --- /dev/null +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineScenePhases.cpp @@ -0,0 +1,128 @@ +#include "Rendering/Pipelines/BuiltinForwardPipeline.h" + +#include "Debug/Logger.h" +#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h" + +namespace XCEngine { +namespace Rendering { +namespace Pipelines { + +ScenePhaseExecutionContext BuiltinForwardPipeline::BuildScenePhaseExecutionContext( + const FrameExecutionContext& executionContext, + ScenePhase scenePhase) const { + return ScenePhaseExecutionContext( + executionContext, + scenePhase, + ShouldSampleMainDirectionalShadowMap(executionContext.sceneData)); +} + +DrawSettings BuiltinForwardPipeline::BuildDrawSettings(ScenePhase scenePhase) const { + DrawSettings drawSettings = {}; + drawSettings.scenePhase = scenePhase; + switch (scenePhase) { + case ScenePhase::Opaque: + drawSettings.rendererListType = RendererListType::Opaque; + break; + case ScenePhase::Transparent: + drawSettings.rendererListType = RendererListType::Transparent; + break; + default: + drawSettings.rendererListType = RendererListType::AllVisible; + break; + } + + return drawSettings; +} + +bool BuiltinForwardPipeline::ExecuteBuiltinScenePhase( + const ScenePhaseExecutionContext& executionContext) { + switch (executionContext.scenePhase) { + case ScenePhase::Opaque: + return ExecuteForwardOpaquePass(executionContext); + case ScenePhase::Skybox: + return ExecuteForwardSkyboxPass(BuildRenderPassContext(executionContext)); + case ScenePhase::Transparent: + return ExecuteForwardTransparentPass(executionContext); + default: + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline::ExecuteBuiltinScenePhase does not support scene phase: ") + + ToString(executionContext.scenePhase)).CStr()); + return false; + } +} + +bool BuiltinForwardPipeline::ExecuteForwardScene( + const FrameExecutionContext& executionContext) { + return ExecuteForwardSceneSteps( + executionContext, + 0u, + Internal::GetBuiltinForwardSceneSteps().size()); +} + +bool BuiltinForwardPipeline::ExecuteForwardSceneSteps( + const FrameExecutionContext& executionContext, + size_t beginStepIndex, + size_t endStepIndex) { + const auto& steps = Internal::GetBuiltinForwardSceneSteps(); + if (beginStepIndex > endStepIndex || + endStepIndex > steps.size()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline::ExecuteForwardSceneSteps received an invalid step range"); + return false; + } + + for (size_t stepIndex = beginStepIndex; stepIndex < endStepIndex; ++stepIndex) { + const Internal::ForwardSceneStep& step = steps[stepIndex]; + if (step.type == Internal::ForwardSceneStepType::InjectionPoint) { + if (m_forwardSceneFeatureHost.Execute(executionContext, step.injectionPoint)) { + continue; + } + + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline::ExecuteForwardScene failed at injection point: ") + + ToString(step.injectionPoint)).CStr()); + return false; + } + + const ScenePhaseExecutionContext scenePhaseExecutionContext = + BuildScenePhaseExecutionContext(executionContext, step.scenePhase); + if (!ExecuteBuiltinScenePhase(scenePhaseExecutionContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline::ExecuteForwardScene failed during builtin phase: ") + + ToString(step.scenePhase)).CStr()); + return false; + } + } + + return true; +} + +bool BuiltinForwardPipeline::ExecuteForwardSceneSegmentPass( + const FrameExecutionContext& executionContext, + size_t beginStepIndex, + size_t endStepIndex, + bool clearAttachments) { + const RenderPassContext passContext = BuildRenderPassContext(executionContext); + if (!BeginForwardScenePass(passContext, clearAttachments)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardPipeline::ExecuteForwardSceneSegmentPass failed: BeginForwardScenePass returned false"); + return false; + } + + const bool executeResult = + ExecuteForwardSceneSteps( + executionContext, + beginStepIndex, + endStepIndex); + EndForwardScenePass(passContext); + return executeResult; +} + +} // namespace Pipelines +} // namespace Rendering +} // namespace XCEngine diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index be53ebd7..7a6e978c 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -609,9 +609,19 @@ TEST(BuiltinForwardPipeline_Test, RecordsMainSceneGraphPassWithSampledShadowDepe String errorMessage; ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage)) << errorMessage.CStr(); - EXPECT_EQ(compiledGraph.GetPassCount(), 1u); - EXPECT_STREQ(compiledGraph.GetPassName(0).CStr(), "MainScene"); + EXPECT_EQ(compiledGraph.GetPassCount(), 3u); + EXPECT_STREQ(compiledGraph.GetPassName(0).CStr(), "MainScene.Opaque"); + EXPECT_STREQ(compiledGraph.GetPassName(1).CStr(), "MainScene.Skybox"); + EXPECT_STREQ(compiledGraph.GetPassName(2).CStr(), "MainScene.Transparent"); EXPECT_EQ(compiledGraph.GetPassType(0), RenderGraphPassType::Raster); + EXPECT_EQ(compiledGraph.GetPassType(1), RenderGraphPassType::Raster); + EXPECT_EQ(compiledGraph.GetPassType(2), RenderGraphPassType::Raster); + + RenderGraphTextureLifetime shadowLifetime = {}; + ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(shadowTarget, shadowLifetime)); + EXPECT_TRUE(shadowLifetime.used); + EXPECT_EQ(shadowLifetime.firstPassIndex, 0u); + EXPECT_EQ(shadowLifetime.lastPassIndex, 2u); RenderGraphTextureTransitionPlan shadowPlan = {}; ASSERT_TRUE(compiledGraph.TryGetTextureTransitionPlan(shadowTarget, shadowPlan));