diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index cf1ca61f..f00da120 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -6,6 +6,7 @@ #include "Rendering/Graph/RenderGraph.h" #include "Rendering/Graph/RenderGraphCompiler.h" #include "Rendering/Graph/RenderGraphExecutor.h" +#include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Passes/BuiltinDepthOnlyPass.h" #include "Rendering/Passes/BuiltinObjectIdPass.h" #include "Rendering/Passes/BuiltinShadowCasterPass.h" @@ -492,6 +493,34 @@ RenderPass* ResolveStandaloneStagePass( } } +std::unique_ptr* ResolveStageSequenceState( + CameraFrameStage stage, + CameraFrameExecutionState& executionState) { + switch (stage) { + case CameraFrameStage::PreScenePasses: + return &executionState.preScenePasses; + case CameraFrameStage::PostProcess: + return &executionState.postProcessPasses; + case CameraFrameStage::FinalOutput: + return &executionState.finalOutputPasses; + case CameraFrameStage::PostScenePasses: + return &executionState.postScenePasses; + case CameraFrameStage::OverlayPasses: + return &executionState.overlayPasses; + default: + return nullptr; + } +} + +bool RecordSequencePass( + RenderPass& pass, + const RenderPassRenderGraphContext& context, + const Internal::RenderPassGraphIO& io) { + return pass.SupportsRenderGraph() + ? pass.RecordRenderGraph(context) + : Internal::RecordRasterRenderPass(pass, context, io); +} + bool ExecutePassSequenceStage( std::unique_ptr& activeSequence, RenderPassSequence* sequence, @@ -817,6 +846,187 @@ bool ExecuteFrameStage( } } +bool RecordRegularPassSequenceStage( + CameraFrameStage stage, + const Containers::String& stageName, + const RenderPassContext& stagePassContext, + const RenderGraphImportedSurface& outputSurface, + const RenderSceneData& sceneData, + RenderPassSequence* stageSequence, + CameraFrameExecutionState& executionState, + RenderGraphBuilder& graphBuilder, + RenderGraphBlackboard& blackboard, + bool& stageExecutionSucceeded) { + if (stageSequence == nullptr || stageSequence->GetPassCount() == 0u) { + return true; + } + + std::unique_ptr* const activeSequence = + ResolveStageSequenceState(stage, executionState); + RenderPassSequence* const sequence = stageSequence; + const RenderContext* const renderContext = &stagePassContext.renderContext; + const RenderPassGraphBeginCallback beginSequencePass = + [&, activeSequence, sequence, renderContext](const RenderPassContext&) -> bool { + if (activeSequence == nullptr || + !EnsureInitializedPassSequence( + *activeSequence, + sequence, + *renderContext)) { + stageExecutionSucceeded = false; + return false; + } + + return true; + }; + const bool writesColor = GetPrimaryColorTexture(outputSurface).IsValid(); + const bool writesDepth = outputSurface.depthTexture.IsValid(); + + for (size_t passIndex = 0u; passIndex < stageSequence->GetPassCount(); ++passIndex) { + RenderPass* const pass = stageSequence->GetPass(passIndex); + if (pass == nullptr) { + continue; + } + + const Containers::String passName = + stageSequence->GetPassCount() == 1u + ? stageName + : BuildRenderGraphSequencePassName(stageName, passIndex); + const RenderPassRenderGraphContext passContext = { + graphBuilder, + passName, + stagePassContext.renderContext, + sceneData, + stagePassContext.surface, + stagePassContext.sourceSurface, + stagePassContext.sourceColorView, + stagePassContext.sourceColorState, + {}, + outputSurface.colorTextures, + outputSurface.depthTexture, + &stageExecutionSucceeded, + beginSequencePass, + {}, + &blackboard + }; + if (!RecordSequencePass( + *pass, + passContext, + { + false, + writesColor, + writesDepth + })) { + return false; + } + } + + return true; +} + +bool RecordFullscreenPassSequenceStage( + CameraFrameStage stage, + const Containers::String& stageName, + const RenderPassContext& stagePassContext, + const RenderGraphImportedSurface& sourceSurface, + const RenderGraphImportedSurface& outputSurface, + const RenderSceneData& sceneData, + RenderPassSequence* stageSequence, + CameraFrameExecutionState& executionState, + RenderGraphBuilder& graphBuilder, + RenderGraphBlackboard& blackboard, + bool& stageExecutionSucceeded) { + if (stageSequence == nullptr || stageSequence->GetPassCount() == 0u) { + return true; + } + + std::unique_ptr* const activeSequence = + ResolveStageSequenceState(stage, executionState); + RenderPassSequence* const sequence = stageSequence; + const RenderContext* const renderContext = &stagePassContext.renderContext; + const RenderPassGraphBeginCallback beginSequencePass = + [&, activeSequence, sequence, renderContext](const RenderPassContext&) -> bool { + if (activeSequence == nullptr || + !EnsureInitializedPassSequence( + *activeSequence, + sequence, + *renderContext)) { + stageExecutionSucceeded = false; + return false; + } + + return true; + }; + + RenderGraphTextureHandle currentSourceColor = + GetPrimaryColorTexture(sourceSurface); + const RenderGraphTextureHandle finalOutputColor = + GetPrimaryColorTexture(outputSurface); + const RenderGraphTextureDesc transientDesc = + BuildFullscreenTransientTextureDesc(stagePassContext.surface); + + for (size_t passIndex = 0u; passIndex < stageSequence->GetPassCount(); ++passIndex) { + RenderPass* const pass = stageSequence->GetPass(passIndex); + if (pass == nullptr) { + continue; + } + + const bool isLastPass = (passIndex + 1u) == stageSequence->GetPassCount(); + const Containers::String passName = + stageSequence->GetPassCount() == 1u + ? stageName + : BuildRenderGraphSequencePassName(stageName, passIndex); + const RenderGraphTextureHandle passOutputColor = + isLastPass + ? finalOutputColor + : graphBuilder.CreateTransientTexture( + passName + ".Color", + transientDesc); + const RenderSurface* const sourceSurfaceTemplate = + passIndex == 0u + ? stagePassContext.sourceSurface + : &stagePassContext.surface; + RHI::RHIResourceView* const sourceColorView = + passIndex == 0u + ? stagePassContext.sourceColorView + : nullptr; + const RHI::ResourceStates sourceColorState = + passIndex == 0u + ? stagePassContext.sourceColorState + : RHI::ResourceStates::PixelShaderResource; + const RenderPassRenderGraphContext passContext = { + graphBuilder, + passName, + stagePassContext.renderContext, + sceneData, + stagePassContext.surface, + sourceSurfaceTemplate, + sourceColorView, + sourceColorState, + currentSourceColor, + std::vector{ passOutputColor }, + {}, + &stageExecutionSucceeded, + beginSequencePass, + {}, + &blackboard + }; + if (!RecordSequencePass( + *pass, + passContext, + { + true, + true, + false + })) { + return false; + } + + currentSourceColor = passOutputColor; + } + + return true; +} + bool ExecuteRenderGraphPlan( const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, @@ -841,157 +1051,6 @@ bool ExecuteRenderGraphPlan( const Containers::String stageName(GetCameraFrameStageName(stageInfo.stage)); RenderPassSequence* const stageSequence = plan.GetPassSequence(stage); - if (IsFullscreenSequenceStage(stage) && - stageSequence != nullptr && - stageSequence->GetPassCount() > 1u) { - const RenderPassContext stagePassContext = - BuildFrameStagePassContext(stage, plan, shadowState, sceneData); - const RenderGraphImportedSurface sourceSurface = - ImportRenderGraphSurface( - graphBuilder, - importedTextures, - stageName + ".Source", - stagePassContext.sourceSurface, - RenderGraphSurfaceImportUsage::Source, - true); - const RenderGraphImportedSurface outputSurface = - ImportRenderGraphSurface( - graphBuilder, - importedTextures, - stageName + ".Output", - &stagePassContext.surface, - RenderGraphSurfaceImportUsage::Output, - true); - RenderGraphTextureHandle currentSourceColor = GetPrimaryColorTexture(sourceSurface); - const RenderGraphTextureHandle finalOutputColor = - GetPrimaryColorTexture(outputSurface); - const RenderGraphTextureDesc transientDesc = - BuildFullscreenTransientTextureDesc(stagePassContext.surface); - - for (size_t passIndex = 0; passIndex < stageSequence->GetPassCount(); ++passIndex) { - const bool isLastPass = (passIndex + 1u) == stageSequence->GetPassCount(); - const size_t sequencePassIndex = passIndex; - const RenderGraphTextureHandle passSourceColor = currentSourceColor; - const RenderGraphTextureHandle passOutputColor = - isLastPass - ? finalOutputColor - : graphBuilder.CreateTransientTexture( - BuildRenderGraphSequencePassName(stageName, sequencePassIndex) + ".Color", - transientDesc); - RenderPass* const sequencePass = - stageSequence->GetPass(sequencePassIndex); - const Containers::String sequencePassName = - BuildRenderGraphSequencePassName(stageName, sequencePassIndex); - const RenderSurface* const passSourceSurfaceTemplate = - sequencePassIndex == 0u - ? stagePassContext.sourceSurface - : &stagePassContext.surface; - RHI::RHIResourceView* const passSourceColorView = - sequencePassIndex == 0u - ? stagePassContext.sourceColorView - : nullptr; - const RHI::ResourceStates passSourceColorState = - sequencePassIndex == 0u - ? stagePassContext.sourceColorState - : RHI::ResourceStates::PixelShaderResource; - - if (sequencePass != nullptr && - sequencePass->SupportsRenderGraph()) { - const RenderPassGraphBeginCallback beginSequencePass = - [&, stage](const RenderPassContext&) -> bool { - std::unique_ptr& activeSequence = - stage == CameraFrameStage::PostProcess - ? executionState.postProcessPasses - : executionState.finalOutputPasses; - if (!EnsureInitializedPassSequence( - activeSequence, - plan.GetPassSequence(stage), - plan.request.context)) { - stageExecutionSucceeded = false; - return false; - } - - return true; - }; - const RenderPassRenderGraphContext sequencePassContext = { - graphBuilder, - sequencePassName, - plan.request.context, - sceneData, - stagePassContext.surface, - passSourceSurfaceTemplate, - passSourceColorView, - passSourceColorState, - passSourceColor, - std::vector{ passOutputColor }, - {}, - &stageExecutionSucceeded, - beginSequencePass, - {}, - &blackboard - }; - if (!sequencePass->RecordRenderGraph(sequencePassContext)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - Containers::String("CameraRenderer::Render failed: RenderPass::RecordRenderGraph returned false for ") + - sequencePassName); - return false; - } - - currentSourceColor = passOutputColor; - continue; - } - - graphBuilder.AddRasterPass( - sequencePassName, - [&, stage, sequencePassIndex, passSourceColor, passOutputColor]( - RenderGraphPassBuilder& passBuilder) { - if (passSourceColor.IsValid()) { - passBuilder.ReadTexture(passSourceColor); - } - if (passOutputColor.IsValid()) { - passBuilder.WriteTexture(passOutputColor); - } - passBuilder.SetExecuteCallback( - [&, stage, sequencePassIndex, passSourceColor, passOutputColor]( - const RenderGraphExecutionContext& executionContext) { - if (!stageExecutionSucceeded) { - return; - } - - std::unique_ptr& activeSequence = - stage == CameraFrameStage::PostProcess - ? executionState.postProcessPasses - : executionState.finalOutputPasses; - if (!EnsureInitializedPassSequence( - activeSequence, - plan.GetPassSequence(stage), - plan.request.context)) { - stageExecutionSucceeded = false; - return; - } - - stageExecutionSucceeded = - ExecuteFullscreenPassSequencePass( - plan.GetPassSequence(stage), - sequencePassIndex, - BuildFrameStagePassContext( - stage, - plan, - shadowState, - sceneData), - executionContext, - passSourceColor, - passOutputColor); - }); - }); - - currentSourceColor = passOutputColor; - } - - continue; - } - const RenderPassContext stagePassContext = BuildFrameStagePassContext(stage, plan, shadowState, sceneData); const RenderGraphImportedSurface sourceSurface = @@ -1033,6 +1092,43 @@ bool ExecuteRenderGraphPlan( const RHI::RHIResourceView* const stageSourceColorView = stagePassContext.sourceColorView; const RHI::ResourceStates stageSourceColorState = stagePassContext.sourceColorState; + if (stageSequence != nullptr) { + const bool recordResult = + IsFullscreenSequenceStage(stage) + ? RecordFullscreenPassSequenceStage( + stage, + stageName, + stagePassContext, + sourceSurface, + outputSurface, + sceneData, + stageSequence, + executionState, + graphBuilder, + blackboard, + stageExecutionSucceeded) + : RecordRegularPassSequenceStage( + stage, + stageName, + stagePassContext, + outputSurface, + sceneData, + stageSequence, + executionState, + graphBuilder, + blackboard, + stageExecutionSucceeded); + if (!recordResult) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + Containers::String("CameraRenderer::Render failed: pass-sequence graph recording returned false for ") + + stageName); + return false; + } + + continue; + } + RenderPass* const standaloneStagePass = ResolveStandaloneStagePass(stage, executionState); if (standaloneStagePass != nullptr && diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index acb808df..3b40ddcd 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -779,10 +779,13 @@ public: m_state->eventLog.push_back(m_label); lastSurfaceWidth = context.surface.GetRenderAreaWidth(); lastSurfaceHeight = context.surface.GetRenderAreaHeight(); + lastSurfaceAutoTransitionEnabled = context.surface.IsAutoTransitionEnabled(); lastHasSourceSurface = context.sourceSurface != nullptr; if (context.sourceSurface != nullptr) { lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth(); lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight(); + lastSourceSurfaceAutoTransitionEnabled = + context.sourceSurface->IsAutoTransitionEnabled(); } lastSourceColorView = context.sourceColorView; lastSourceColorState = context.sourceColorState; @@ -803,9 +806,11 @@ private: public: uint32_t lastSurfaceWidth = 0; uint32_t lastSurfaceHeight = 0; + bool lastSurfaceAutoTransitionEnabled = true; bool lastHasSourceSurface = false; uint32_t lastSourceSurfaceWidth = 0; uint32_t lastSourceSurfaceHeight = 0; + bool lastSourceSurfaceAutoTransitionEnabled = true; XCEngine::RHI::RHIResourceView* lastSourceColorView = nullptr; XCEngine::RHI::ResourceStates lastSourceColorState = XCEngine::RHI::ResourceStates::Common; }; @@ -1676,6 +1681,114 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) { "overlay" })); } +TEST(CameraRenderer_Test, RecordsGraphCapableSinglePassSequenceStagesInDocumentedOrder) { + Scene scene("CameraRendererGraphSinglePassSequenceStages"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(3.0f); + + auto state = std::make_shared(); + CameraRenderer renderer(std::make_unique(state)); + + auto prePass = std::make_unique(state, "pre", true, true, true); + TrackingPass* prePassRaw = prePass.get(); + RenderPassSequence prePasses; + prePasses.AddPass(std::move(prePass)); + + auto postProcessPass = std::make_unique(state, "postProcess", true, true, true); + TrackingPass* postProcessPassRaw = postProcessPass.get(); + RenderPassSequence postProcessPasses; + postProcessPasses.AddPass(std::move(postProcessPass)); + + auto finalOutputPass = std::make_unique(state, "finalOutput", true, true, true); + TrackingPass* finalOutputPassRaw = finalOutputPass.get(); + RenderPassSequence finalOutputPasses; + finalOutputPasses.AddPass(std::move(finalOutputPass)); + + auto postPass = std::make_unique(state, "post", true, true, true); + TrackingPass* postPassRaw = postPass.get(); + RenderPassSequence postPasses; + postPasses.AddPass(std::move(postPass)); + + auto overlayPass = std::make_unique(state, "overlay", true, true, true); + TrackingPass* overlayPassRaw = overlayPass.get(); + RenderPassSequence overlayPasses; + overlayPasses.AddPass(std::move(overlayPass)); + + CameraRenderRequest request; + request.scene = &scene; + request.camera = camera; + request.context = CreateValidContext(); + request.surface = RenderSurface(320, 180); + request.surface.SetColorAttachment(reinterpret_cast(3)); + request.cameraDepth = camera->GetDepth(); + request.preScenePasses = &prePasses; + request.postProcess.passes = &postProcessPasses; + request.finalOutput.passes = &finalOutputPasses; + request.postScenePasses = &postPasses; + request.overlayPasses = &overlayPasses; + request.postProcess.sourceSurface = RenderSurface(256, 128); + request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast(4)); + request.postProcess.sourceColorView = reinterpret_cast(40); + request.postProcess.destinationSurface = RenderSurface(320, 180); + request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast(5)); + request.finalOutput.sourceSurface = request.postProcess.destinationSurface; + request.finalOutput.sourceColorView = reinterpret_cast(50); + request.finalOutput.destinationSurface = request.surface; + + ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request))); + + EXPECT_EQ( + state->eventLog, + (std::vector{ + "record:pre", + "record:postProcess", + "record:finalOutput", + "record:post", + "record:overlay", + "init:pre", + "pre", + "pipeline", + "init:postProcess", + "postProcess", + "init:finalOutput", + "finalOutput", + "init:post", + "post", + "init:overlay", + "overlay" })); + + ASSERT_NE(prePassRaw, nullptr); + EXPECT_TRUE(prePassRaw->lastHasSourceSurface == false); + EXPECT_FALSE(prePassRaw->lastSurfaceAutoTransitionEnabled); + ASSERT_NE(postProcessPassRaw, nullptr); + EXPECT_TRUE(postProcessPassRaw->lastHasSourceSurface); + EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceWidth, 256u); + EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceHeight, 128u); + EXPECT_FALSE(postProcessPassRaw->lastSourceSurfaceAutoTransitionEnabled); + EXPECT_EQ(postProcessPassRaw->lastSourceColorView, reinterpret_cast(40)); + EXPECT_EQ( + postProcessPassRaw->lastSourceColorState, + XCEngine::RHI::ResourceStates::PixelShaderResource); + EXPECT_FALSE(postProcessPassRaw->lastSurfaceAutoTransitionEnabled); + ASSERT_NE(finalOutputPassRaw, nullptr); + EXPECT_TRUE(finalOutputPassRaw->lastHasSourceSurface); + EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceWidth, 320u); + EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceHeight, 180u); + EXPECT_FALSE(finalOutputPassRaw->lastSourceSurfaceAutoTransitionEnabled); + EXPECT_EQ(finalOutputPassRaw->lastSourceColorView, reinterpret_cast(50)); + EXPECT_EQ( + finalOutputPassRaw->lastSourceColorState, + XCEngine::RHI::ResourceStates::PixelShaderResource); + EXPECT_FALSE(finalOutputPassRaw->lastSurfaceAutoTransitionEnabled); + ASSERT_NE(postPassRaw, nullptr); + EXPECT_FALSE(postPassRaw->lastSurfaceAutoTransitionEnabled); + ASSERT_NE(overlayPassRaw, nullptr); + EXPECT_FALSE(overlayPassRaw->lastSurfaceAutoTransitionEnabled); +} + TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipeline) { Scene scene("CameraRendererDepthAndShadowScene");