Track fullscreen surface states across frames

This commit is contained in:
2026-04-07 04:38:07 +08:00
parent 5bfe484f5d
commit 93ceb61483
2 changed files with 182 additions and 1 deletions

View File

@@ -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<std::unique_ptr<FullscreenPassSurfaceCache>>& 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<FullscreenPassSurfaceCache>& 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<CameraRenderRequest>& 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;
}

View File

@@ -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<CameraComponent>();
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<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
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<MockPipeline>(pipelineState));
std::vector<CameraRenderRequest> 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<MockScenePass>(pipelineState, "postProcess"));
firstFrameRequests[0].postProcess.passes = &postProcessPasses;
ASSERT_TRUE(renderer.Render(firstFrameRequests));
const std::vector<CameraRenderRequest> 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<CameraComponent>();
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<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
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<MockPipeline>(pipelineState));
std::vector<CameraRenderRequest> 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<MockScenePass>(pipelineState, "postProcess"));
RenderPassSequence finalOutputPasses;
finalOutputPasses.AddPass(std::make_unique<MockScenePass>(pipelineState, "finalOutput"));
firstFrameRequests[0].postProcess.passes = &postProcessPasses;
firstFrameRequests[0].finalOutput.passes = &finalOutputPasses;
ASSERT_TRUE(renderer.Render(firstFrameRequests));
const std::vector<CameraRenderRequest> 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");