diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index 4df788c5..ae8d2cb8 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -147,7 +147,8 @@ RenderGraphImportedSurface ImportRenderGraphSurface( const Containers::String& surfaceName, const RenderSurface* surface, RenderGraphSurfaceImportUsage usage, - bool graphOwnsColorTransitions = false) { + bool graphOwnsColorTransitions = false, + bool graphOwnsDepthTransitions = false) { RenderGraphImportedSurface importedSurface = {}; if (surface == nullptr) { return importedSurface; @@ -190,7 +191,7 @@ RenderGraphImportedSurface ImportRenderGraphSurface( *surface, true, usage, - false)); + graphOwnsDepthTransitions)); } return importedSurface; @@ -312,7 +313,8 @@ bool ExecuteScenePassRequest( RenderPass* pass, const ScenePassRenderRequest& request, const RenderContext& context, - const RenderSceneData& baseSceneData) { + const RenderSceneData& baseSceneData, + const RenderSurface* surfaceOverride = nullptr) { if (!request.IsRequested()) { return true; } @@ -326,8 +328,10 @@ bool ExecuteScenePassRequest( sceneData.cameraData = request.cameraDataOverride; } - sceneData.cameraData.viewportWidth = request.surface.GetRenderAreaWidth(); - sceneData.cameraData.viewportHeight = request.surface.GetRenderAreaHeight(); + const RenderSurface& requestSurface = + surfaceOverride != nullptr ? *surfaceOverride : request.surface; + sceneData.cameraData.viewportWidth = requestSurface.GetRenderAreaWidth(); + sceneData.cameraData.viewportHeight = requestSurface.GetRenderAreaHeight(); sceneData.cameraData.clearFlags = request.clearFlags; if (request.hasClearColorOverride) { sceneData.cameraData.clearColor = request.clearColorOverride; @@ -335,7 +339,7 @@ bool ExecuteScenePassRequest( const RenderPassContext passContext = { context, - request.surface, + requestSurface, sceneData, nullptr, nullptr, @@ -581,6 +585,73 @@ RenderSurface BuildGraphManagedImportedSurface( return surface; } +RenderSurface BuildGraphManagedPassSurface( + const RenderSurface& templateSurface) { + RenderSurface surface = templateSurface; + surface.SetAutoTransitionEnabled(false); + return surface; +} + +const RenderSurface* ResolveFrameStageOutputSurface( + CameraFrameStage stage, + const CameraFramePlan& plan, + const DirectionalShadowExecutionState& shadowState) { + switch (stage) { + case CameraFrameStage::ShadowCaster: + return shadowState.shadowCasterRequest.IsRequested() + ? &shadowState.shadowCasterRequest.surface + : nullptr; + case CameraFrameStage::DepthOnly: + return plan.request.depthOnly.IsRequested() + ? &plan.request.depthOnly.surface + : nullptr; + case CameraFrameStage::ObjectId: + return plan.request.objectId.IsRequested() + ? &plan.request.objectId.surface + : nullptr; + default: + return plan.GetOutputSurface(stage); + } +} + +bool ShouldGraphOwnStageColorTransitions( + CameraFrameStage stage) { + return stage == CameraFrameStage::ObjectId; +} + +bool ShouldGraphOwnStageDepthTransitions( + CameraFrameStage stage) { + return stage == CameraFrameStage::ShadowCaster || + stage == CameraFrameStage::DepthOnly || + stage == CameraFrameStage::ObjectId; +} + +bool CanUseGraphManagedImportedSurface( + const RenderGraphImportedSurface& surface, + const RenderGraphExecutionContext& graphContext) { + bool hasAnyTexture = false; + + for (RenderGraphTextureHandle texture : surface.colorTextures) { + if (!texture.IsValid()) { + continue; + } + + hasAnyTexture = true; + if (!graphContext.OwnsTextureTransitions(texture)) { + return false; + } + } + + if (surface.depthTexture.IsValid()) { + hasAnyTexture = true; + if (!graphContext.OwnsTextureTransitions(surface.depthTexture)) { + return false; + } + } + + return hasAnyTexture; +} + bool ExecuteFullscreenPassSequencePass( RenderPassSequence* sequence, size_t passIndex, @@ -592,8 +663,10 @@ bool ExecuteFullscreenPassSequencePass( RenderPassContext BuildFrameStagePassContext( CameraFrameStage stage, const CameraFramePlan& plan, + const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData) { - const RenderSurface* outputSurface = plan.GetOutputSurface(stage); + const RenderSurface* outputSurface = + ResolveFrameStageOutputSurface(stage, plan, shadowState); return { plan.request.context, outputSurface != nullptr ? *outputSurface : plan.request.surface, @@ -609,8 +682,21 @@ bool ExecuteFrameStage( const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, - CameraFrameExecutionState& executionState) { - const RenderPassContext passContext = BuildFrameStagePassContext(stage, plan, sceneData); + CameraFrameExecutionState& executionState, + const RenderSurface* outputSurfaceOverride = nullptr) { + const RenderPassContext defaultPassContext = + BuildFrameStagePassContext(stage, plan, shadowState, sceneData); + const RenderPassContext overridePassContext = { + defaultPassContext.renderContext, + outputSurfaceOverride != nullptr + ? *outputSurfaceOverride + : defaultPassContext.surface, + defaultPassContext.sceneData, + defaultPassContext.sourceSurface, + defaultPassContext.sourceColorView, + defaultPassContext.sourceColorState + }; + const RenderPassContext& passContext = overridePassContext; switch (stage) { case CameraFrameStage::PreScenePasses: @@ -624,13 +710,15 @@ bool ExecuteFrameStage( executionState.shadowCasterPass, shadowState.shadowCasterRequest, plan.request.context, - sceneData); + sceneData, + outputSurfaceOverride); case CameraFrameStage::DepthOnly: return ExecuteScenePassRequest( executionState.depthOnlyPass, plan.request.depthOnly, plan.request.context, - sceneData); + sceneData, + outputSurfaceOverride); case CameraFrameStage::MainScene: return executionState.pipeline != nullptr && executionState.pipeline->Render( @@ -660,7 +748,9 @@ bool ExecuteFrameStage( ExecuteStandalonePass( executionState.objectIdPass, plan.request.context, - plan.request.objectId.surface, + outputSurfaceOverride != nullptr + ? *outputSurfaceOverride + : plan.request.objectId.surface, sceneData); case CameraFrameStage::PostScenePasses: return ExecutePassSequenceStage( @@ -703,7 +793,7 @@ bool ExecuteRenderGraphPlan( stageSequence != nullptr && stageSequence->GetPassCount() > 1u) { const RenderPassContext stagePassContext = - BuildFrameStagePassContext(stage, plan, sceneData); + BuildFrameStagePassContext(stage, plan, shadowState, sceneData); const RenderGraphImportedSurface sourceSurface = ImportRenderGraphSurface( graphBuilder, @@ -738,7 +828,7 @@ bool ExecuteRenderGraphPlan( transientDesc); graphBuilder.AddRasterPass( - BuildRenderGraphSequencePassName(stageName, sequencePassIndex), + BuildRenderGraphSequencePassName(stageName, sequencePassIndex), [&, stage, sequencePassIndex, passSourceColor, passOutputColor]( RenderGraphPassBuilder& passBuilder) { if (passSourceColor.IsValid()) { @@ -770,7 +860,11 @@ bool ExecuteRenderGraphPlan( ExecuteFullscreenPassSequencePass( plan.GetPassSequence(stage), sequencePassIndex, - BuildFrameStagePassContext(stage, plan, sceneData), + BuildFrameStagePassContext( + stage, + plan, + shadowState, + sceneData), executionContext, passSourceColor, passOutputColor); @@ -783,24 +877,30 @@ bool ExecuteRenderGraphPlan( continue; } + const RenderPassContext stagePassContext = + BuildFrameStagePassContext(stage, plan, shadowState, sceneData); const RenderGraphImportedSurface sourceSurface = ImportRenderGraphSurface( graphBuilder, importedTextures, stageName + ".Source", - plan.GetSourceSurface(stageInfo.stage), + stagePassContext.sourceSurface, RenderGraphSurfaceImportUsage::Source); const RenderGraphImportedSurface outputSurface = ImportRenderGraphSurface( graphBuilder, importedTextures, stageName + ".Output", - plan.GetOutputSurface(stageInfo.stage), - RenderGraphSurfaceImportUsage::Output); + &stagePassContext.surface, + RenderGraphSurfaceImportUsage::Output, + ShouldGraphOwnStageColorTransitions(stage), + ShouldGraphOwnStageDepthTransitions(stage)); + const RenderSurface stageSurfaceTemplate = stagePassContext.surface; graphBuilder.AddRasterPass( stageName, - [&, sourceSurface, outputSurface, stage](RenderGraphPassBuilder& passBuilder) { + [&, sourceSurface, outputSurface, stage, stageSurfaceTemplate]( + RenderGraphPassBuilder& passBuilder) { if (IsFullscreenSequenceStage(stage)) { ReadRenderGraphColorSurface(passBuilder, sourceSurface); WriteRenderGraphColorSurface(passBuilder, outputSurface); @@ -809,17 +909,27 @@ bool ExecuteRenderGraphPlan( WriteRenderGraphSurface(passBuilder, outputSurface); } passBuilder.SetExecuteCallback( - [&, stage](const RenderGraphExecutionContext&) { + [&, stage, outputSurface, stageSurfaceTemplate]( + const RenderGraphExecutionContext& executionContext) { if (!stageExecutionSucceeded) { return; } + const RenderSurface* outputSurfaceOverride = nullptr; + RenderSurface graphManagedOutputSurface = {}; + if (CanUseGraphManagedImportedSurface(outputSurface, executionContext)) { + graphManagedOutputSurface = + BuildGraphManagedPassSurface(stageSurfaceTemplate); + outputSurfaceOverride = &graphManagedOutputSurface; + } + stageExecutionSucceeded = ExecuteFrameStage( stage, plan, shadowState, sceneData, - executionState); + executionState, + outputSurfaceOverride); }); }); } diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index ed35979d..3163663f 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -472,8 +472,11 @@ public: return "MockObjectIdPass"; } - bool Execute(const RenderPassContext&) override { + bool Execute(const RenderPassContext& context) override { m_state->eventLog.push_back("objectId"); + lastSurfaceAutoTransitionEnabled = context.surface.IsAutoTransitionEnabled(); + lastSurfaceWidth = context.surface.GetRenderAreaWidth(); + lastSurfaceHeight = context.surface.GetRenderAreaHeight(); return m_renderResult; } @@ -484,6 +487,11 @@ public: private: std::shared_ptr m_state; bool m_renderResult = true; + +public: + bool lastSurfaceAutoTransitionEnabled = true; + uint32_t lastSurfaceWidth = 0; + uint32_t lastSurfaceHeight = 0; }; class MockScenePass final : public RenderPass { @@ -908,9 +916,11 @@ TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRe camera->SetDepth(3.0f); auto state = std::make_shared(); + auto objectIdPass = std::make_unique(state); + MockObjectIdPass* objectIdPassRaw = objectIdPass.get(); CameraRenderer renderer( std::make_unique(state), - std::make_unique(state)); + std::move(objectIdPass)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); @@ -940,6 +950,10 @@ TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRe "objectId", "init:post", "post" })); + ASSERT_NE(objectIdPassRaw, nullptr); + EXPECT_FALSE(objectIdPassRaw->lastSurfaceAutoTransitionEnabled); + EXPECT_EQ(objectIdPassRaw->lastSurfaceWidth, 320u); + EXPECT_EQ(objectIdPassRaw->lastSurfaceHeight, 180u); } TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages) { @@ -1386,10 +1400,12 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe EXPECT_EQ(shadowPassRaw->lastViewportHeight, 64u); EXPECT_EQ(shadowPassRaw->lastSurfaceWidth, 128u); EXPECT_EQ(shadowPassRaw->lastSurfaceHeight, 64u); + EXPECT_FALSE(shadowPassRaw->lastSurfaceAutoTransitionEnabled); EXPECT_EQ(shadowPassRaw->lastClearFlags, RenderClearFlags::Depth); EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f)); EXPECT_EQ(depthPassRaw->lastViewportWidth, 96u); EXPECT_EQ(depthPassRaw->lastViewportHeight, 48u); + EXPECT_FALSE(depthPassRaw->lastSurfaceAutoTransitionEnabled); EXPECT_EQ(depthPassRaw->lastClearFlags, RenderClearFlags::Depth); EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.r, 0.3f); EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.g, 0.2f); @@ -1448,6 +1464,7 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) { EXPECT_EQ(shadowPassRaw->lastViewportHeight, 128u); EXPECT_EQ(shadowPassRaw->lastSurfaceWidth, 256u); EXPECT_EQ(shadowPassRaw->lastSurfaceHeight, 128u); + EXPECT_FALSE(shadowPassRaw->lastSurfaceAutoTransitionEnabled); EXPECT_EQ(shadowPassRaw->lastClearFlags, RenderClearFlags::Depth); EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f)); EXPECT_EQ(allocationState->createTextureCalls, 1);