From 93ceb61483dec418b8ca98333e073d2a987ca195 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 7 Apr 2026 04:38:07 +0800 Subject: [PATCH] Track fullscreen surface states across frames --- .../src/Rendering/Execution/SceneRenderer.cpp | 41 ++++- .../unit/test_camera_scene_renderer.cpp | 142 ++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/engine/src/Rendering/Execution/SceneRenderer.cpp b/engine/src/Rendering/Execution/SceneRenderer.cpp index 9b685ef1..1d7d2ff8 100644 --- a/engine/src/Rendering/Execution/SceneRenderer.cpp +++ b/engine/src/Rendering/Execution/SceneRenderer.cpp @@ -30,11 +30,40 @@ RenderSurface ConfigureFullscreenStageSurface( surface.ResetRenderArea(); } - surface.SetColorStateBefore(RHI::ResourceStates::Common); + surface.SetColorStateBefore(entry.currentColorState); surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource); return surface; } +void UpdateTrackedFullscreenSurfaceState( + std::vector>& surfaceCaches, + const RenderSurface* surface) { + if (surface == nullptr || surface->GetColorAttachments().empty()) { + return; + } + + RHI::RHIResourceView* colorAttachment = surface->GetColorAttachments()[0]; + if (colorAttachment == nullptr) { + return; + } + + for (const std::unique_ptr& cache : surfaceCaches) { + if (cache == nullptr) { + continue; + } + + for (size_t entryIndex = 0; entryIndex < cache->GetSurfaceCount(); ++entryIndex) { + FullscreenPassSurfaceCache::SurfaceEntry* entry = cache->GetSurfaceEntry(entryIndex); + if (entry == nullptr || entry->renderTargetView != colorAttachment) { + continue; + } + + entry->currentColorState = surface->GetColorStateAfter(); + return; + } + } +} + } // namespace SceneRenderer::SceneRenderer() = default; @@ -92,6 +121,16 @@ bool SceneRenderer::Render(const std::vector& requests) { if (!m_cameraRenderer.Render(request)) { return false; } + + UpdateTrackedFullscreenSurfaceState( + m_ownedFullscreenStageSurfaces, + &request.GetMainSceneSurface()); + if (request.postProcess.IsRequested()) { + UpdateTrackedFullscreenSurfaceState( + m_ownedFullscreenStageSurfaces, + &request.postProcess.destinationSurface); + } + rendered = true; } diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index bdc5f171..baae8ba8 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -2057,6 +2057,148 @@ TEST(SceneRenderer_Test, RoutesPostProcessIntoIntermediateSurfaceBeforeFinalOutp delete backBufferColorView; } +TEST(SceneRenderer_Test, ReusesTrackedSceneColorStateAcrossFramesWhenPostProcessIsEnabled) { + Scene scene("SceneRendererTrackedSceneColorStateScene"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(2.0f); + camera->SetPostProcessPasses({ + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(1.0f, 0.9f, 0.8f, 1.0f)) + }); + + auto pipelineState = std::make_shared(); + auto allocationState = std::make_shared(); + MockShadowDevice device(allocationState); + + RenderContext context = CreateValidContext(); + context.device = &device; + + auto* backBufferColorView = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::RenderTarget, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* depthView = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::DepthStencil, + XCEngine::RHI::Format::D24_UNorm_S8_UInt, + XCEngine::RHI::ResourceViewDimension::Texture2D); + + RenderSurface surface(800, 600); + surface.SetColorAttachment(backBufferColorView); + surface.SetDepthAttachment(depthView); + + SceneRenderer renderer(std::make_unique(pipelineState)); + + std::vector firstFrameRequests = + renderer.BuildRenderRequests(scene, nullptr, context, surface); + ASSERT_EQ(firstFrameRequests.size(), 1u); + EXPECT_EQ( + firstFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(), + XCEngine::RHI::ResourceStates::Common); + + RenderPassSequence postProcessPasses; + postProcessPasses.AddPass(std::make_unique(pipelineState, "postProcess")); + firstFrameRequests[0].postProcess.passes = &postProcessPasses; + + ASSERT_TRUE(renderer.Render(firstFrameRequests)); + + const std::vector secondFrameRequests = + renderer.BuildRenderRequests(scene, nullptr, context, surface); + ASSERT_EQ(secondFrameRequests.size(), 1u); + EXPECT_EQ( + secondFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(), + XCEngine::RHI::ResourceStates::PixelShaderResource); + EXPECT_EQ( + secondFrameRequests[0].GetMainSceneSurface().GetColorStateAfter(), + XCEngine::RHI::ResourceStates::PixelShaderResource); + + delete depthView; + delete backBufferColorView; +} + +TEST(SceneRenderer_Test, ReusesTrackedPostProcessOutputStateAcrossFramesWhenFinalOutputIsEnabled) { + Scene scene("SceneRendererTrackedPostProcessOutputStateScene"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(2.0f); + camera->SetPostProcessPasses({ + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(1.0f, 0.9f, 0.8f, 1.0f)) + }); + + FinalColorOverrideSettings finalColorOverrides = {}; + finalColorOverrides.overrideOutputTransferMode = true; + finalColorOverrides.outputTransferMode = FinalColorOutputTransferMode::LinearToSRGB; + camera->SetFinalColorOverrides(finalColorOverrides); + + auto pipelineState = std::make_shared(); + auto allocationState = std::make_shared(); + MockShadowDevice device(allocationState); + + RenderContext context = CreateValidContext(); + context.device = &device; + + auto* backBufferColorView = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::RenderTarget, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* depthView = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::DepthStencil, + XCEngine::RHI::Format::D24_UNorm_S8_UInt, + XCEngine::RHI::ResourceViewDimension::Texture2D); + + RenderSurface surface(800, 600); + surface.SetColorAttachment(backBufferColorView); + surface.SetDepthAttachment(depthView); + + SceneRenderer renderer(std::make_unique(pipelineState)); + + std::vector firstFrameRequests = + renderer.BuildRenderRequests(scene, nullptr, context, surface); + ASSERT_EQ(firstFrameRequests.size(), 1u); + EXPECT_TRUE(firstFrameRequests[0].postProcess.IsRequested()); + EXPECT_TRUE(firstFrameRequests[0].finalOutput.IsRequested()); + EXPECT_EQ( + firstFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(), + XCEngine::RHI::ResourceStates::Common); + EXPECT_EQ( + firstFrameRequests[0].postProcess.destinationSurface.GetColorStateBefore(), + XCEngine::RHI::ResourceStates::Common); + + RenderPassSequence postProcessPasses; + postProcessPasses.AddPass(std::make_unique(pipelineState, "postProcess")); + RenderPassSequence finalOutputPasses; + finalOutputPasses.AddPass(std::make_unique(pipelineState, "finalOutput")); + firstFrameRequests[0].postProcess.passes = &postProcessPasses; + firstFrameRequests[0].finalOutput.passes = &finalOutputPasses; + + ASSERT_TRUE(renderer.Render(firstFrameRequests)); + + const std::vector secondFrameRequests = + renderer.BuildRenderRequests(scene, nullptr, context, surface); + ASSERT_EQ(secondFrameRequests.size(), 1u); + EXPECT_EQ( + secondFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(), + XCEngine::RHI::ResourceStates::PixelShaderResource); + EXPECT_EQ( + secondFrameRequests[0].postProcess.destinationSurface.GetColorStateBefore(), + XCEngine::RHI::ResourceStates::PixelShaderResource); + EXPECT_EQ( + secondFrameRequests[0].finalOutput.sourceSurface.GetColorStateBefore(), + XCEngine::RHI::ResourceStates::PixelShaderResource); + + delete depthView; + delete backBufferColorView; +} + TEST(CameraRenderer_Test, UsesResolvedRenderAreaForCameraViewportDimensions) { Scene scene("CameraRendererViewportRectScene");