Add shadow surface reuse coverage for camera renderer

This commit is contained in:
2026-04-05 14:07:13 +08:00
parent 551eefbaa1
commit daa54e0230
2 changed files with 147 additions and 19 deletions

View File

@@ -756,6 +756,132 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
}
TEST(CameraRenderer_Test, ReusesDirectionalShadowSurfaceWhenPlanMatches) {
Scene scene("CameraRendererDirectionalShadowReuseScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
auto pipelineState = std::make_shared<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowDevice device(allocationState);
RenderContext context = CreateValidContext();
context.device = &device;
CameraRenderer renderer(
std::make_unique<MockPipeline>(pipelineState),
std::make_unique<MockObjectIdPass>(pipelineState));
auto shadowPass = std::make_unique<MockScenePass>(pipelineState, "shadowCaster");
renderer.SetShadowCasterPass(std::move(shadowPass));
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = RenderSurface(320, 180);
request.cameraDepth = camera->GetDepth();
request.directionalShadow.enabled = true;
request.directionalShadow.mapWidth = 256;
request.directionalShadow.mapHeight = 128;
request.directionalShadow.cameraData.viewportWidth = 256;
request.directionalShadow.cameraData.viewportHeight = 128;
request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth;
ASSERT_TRUE(renderer.Render(request));
XCEngine::RHI::RHIResourceView* firstShadowMap = pipelineState->lastShadowMap;
ASSERT_NE(firstShadowMap, nullptr);
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createDepthViewCalls, 1);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 0);
EXPECT_EQ(allocationState->destroyTextureCalls, 0);
ASSERT_TRUE(renderer.Render(request));
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createDepthViewCalls, 1);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 0);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 0);
EXPECT_EQ(allocationState->shutdownTextureCalls, 0);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 0);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 0);
EXPECT_EQ(allocationState->destroyTextureCalls, 0);
EXPECT_EQ(pipelineState->lastShadowMap, firstShadowMap);
}
TEST(CameraRenderer_Test, RecreatesDirectionalShadowSurfaceWhenPlanSizeChanges) {
Scene scene("CameraRendererDirectionalShadowResizeScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
auto pipelineState = std::make_shared<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowDevice device(allocationState);
RenderContext context = CreateValidContext();
context.device = &device;
{
CameraRenderer renderer(
std::make_unique<MockPipeline>(pipelineState),
std::make_unique<MockObjectIdPass>(pipelineState));
auto shadowPass = std::make_unique<MockScenePass>(pipelineState, "shadowCaster");
renderer.SetShadowCasterPass(std::move(shadowPass));
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = RenderSurface(320, 180);
request.cameraDepth = camera->GetDepth();
request.directionalShadow.enabled = true;
request.directionalShadow.mapWidth = 256;
request.directionalShadow.mapHeight = 128;
request.directionalShadow.cameraData.viewportWidth = 256;
request.directionalShadow.cameraData.viewportHeight = 128;
request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth;
ASSERT_TRUE(renderer.Render(request));
XCEngine::RHI::RHIResourceView* firstShadowMap = pipelineState->lastShadowMap;
ASSERT_NE(firstShadowMap, nullptr);
request.directionalShadow.mapWidth = 512;
request.directionalShadow.mapHeight = 256;
request.directionalShadow.cameraData.viewportWidth = 512;
request.directionalShadow.cameraData.viewportHeight = 256;
ASSERT_TRUE(renderer.Render(request));
EXPECT_EQ(allocationState->createTextureCalls, 2);
EXPECT_EQ(allocationState->createDepthViewCalls, 2);
EXPECT_EQ(allocationState->createShaderViewCalls, 2);
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 1);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 1);
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
EXPECT_NE(pipelineState->lastShadowMap, firstShadowMap);
EXPECT_EQ(allocationState->lastTextureWidth, 512u);
EXPECT_EQ(allocationState->lastTextureHeight, 256u);
EXPECT_EQ(allocationState->lastTextureFormat, XCEngine::RHI::Format::D32_Float);
}
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 2);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 2);
EXPECT_EQ(allocationState->shutdownTextureCalls, 2);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 2);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 2);
EXPECT_EQ(allocationState->destroyTextureCalls, 2);
}
TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
Scene scene("CameraRendererInvalidShadowScene");