From daa54e0230f10c8f4d126e24193647a19178093b Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 5 Apr 2026 14:07:13 +0800 Subject: [PATCH] Add shadow surface reuse coverage for camera renderer --- engine/src/Rendering/CameraRenderer.cpp | 40 +++--- .../unit/test_camera_scene_renderer.cpp | 126 ++++++++++++++++++ 2 files changed, 147 insertions(+), 19 deletions(-) diff --git a/engine/src/Rendering/CameraRenderer.cpp b/engine/src/Rendering/CameraRenderer.cpp index a57e3d16..39125061 100644 --- a/engine/src/Rendering/CameraRenderer.cpp +++ b/engine/src/Rendering/CameraRenderer.cpp @@ -20,7 +20,7 @@ struct CameraRenderer::DirectionalShadowSurfaceResources { uint32_t height = 0; RHI::RHITexture* depthTexture = nullptr; RHI::RHIResourceView* depthView = nullptr; - RHI::RHIResourceView* shaderView = nullptr; + RHI::RHIResourceView* depthShaderView = nullptr; RenderSurface surface = {}; DirectionalShadowSurfaceResources() = default; @@ -37,22 +37,22 @@ struct CameraRenderer::DirectionalShadowSurfaceResources { height == plan.mapHeight && depthTexture != nullptr && depthView != nullptr && - shaderView != nullptr; + depthShaderView != nullptr; } void Reset() { - if (shaderView != nullptr) { - shaderView->Shutdown(); - delete shaderView; - shaderView = nullptr; - } - if (depthView != nullptr) { depthView->Shutdown(); delete depthView; depthView = nullptr; } + if (depthShaderView != nullptr) { + depthShaderView->Shutdown(); + delete depthShaderView; + depthShaderView = nullptr; + } + if (depthTexture != nullptr) { depthTexture->Shutdown(); delete depthTexture; @@ -68,6 +68,8 @@ struct CameraRenderer::DirectionalShadowSurfaceResources { namespace { +constexpr RHI::Format kDirectionalShadowMapFormat = RHI::Format::D32_Float; + bool InitializePassSequence( RenderPassSequence* sequence, const RenderContext& context, @@ -315,7 +317,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect depthDesc.depth = 1; depthDesc.mipLevels = 1; depthDesc.arraySize = 1; - depthDesc.format = static_cast(RHI::Format::D24_UNorm_S8_UInt); + depthDesc.format = static_cast(kDirectionalShadowMapFormat); depthDesc.textureType = static_cast(RHI::TextureType::Texture2D); depthDesc.sampleCount = 1; depthDesc.sampleQuality = 0; @@ -327,7 +329,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect } RHI::ResourceViewDesc depthViewDesc = {}; - depthViewDesc.format = static_cast(RHI::Format::D24_UNorm_S8_UInt); + depthViewDesc.format = static_cast(kDirectionalShadowMapFormat); depthViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; depthViewDesc.mipLevel = 0; RHI::RHIResourceView* depthView = @@ -338,13 +340,12 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect return nullptr; } - RHI::ResourceViewDesc shaderViewDesc = {}; - shaderViewDesc.format = depthDesc.format; - shaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; - shaderViewDesc.mipLevel = 0; - RHI::RHIResourceView* shaderView = - context.device->CreateShaderResourceView(depthTexture, shaderViewDesc); - if (shaderView == nullptr) { + RHI::ResourceViewDesc depthShaderViewDesc = {}; + depthShaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; + depthShaderViewDesc.mipLevel = 0; + RHI::RHIResourceView* depthShaderView = + context.device->CreateShaderResourceView(depthTexture, depthShaderViewDesc); + if (depthShaderView == nullptr) { depthView->Shutdown(); delete depthView; depthTexture->Shutdown(); @@ -358,7 +359,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect m_directionalShadowSurface->height = plan.mapHeight; m_directionalShadowSurface->depthTexture = depthTexture; m_directionalShadowSurface->depthView = depthView; - m_directionalShadowSurface->shaderView = shaderView; + m_directionalShadowSurface->depthShaderView = depthShaderView; m_directionalShadowSurface->surface = RenderSurface(plan.mapWidth, plan.mapHeight); m_directionalShadowSurface->surface.SetDepthAttachment(depthView); } @@ -400,6 +401,7 @@ bool CameraRenderer::Render( } resolvedShadowCaster.surface = directionalShadowSurface->surface; + resolvedShadowCaster.clearFlags = RenderClearFlags::Depth; if (!resolvedShadowCaster.hasCameraDataOverride) { resolvedShadowCaster.hasCameraDataOverride = true; resolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData; @@ -417,7 +419,7 @@ bool CameraRenderer::Render( if (request.directionalShadow.IsValid()) { RHI::RHIResourceView* shadowMapView = - directionalShadowSurface != nullptr ? directionalShadowSurface->shaderView : nullptr; + directionalShadowSurface != nullptr ? directionalShadowSurface->depthShaderView : nullptr; sceneData.lighting.mainDirectionalShadow = BuildDirectionalShadowData(request.directionalShadow, shadowMapView); } diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index d5cd4bf4..75ff366d 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -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(); + camera->SetPrimary(true); + camera->SetDepth(2.0f); + + auto pipelineState = std::make_shared(); + auto allocationState = std::make_shared(); + MockShadowDevice device(allocationState); + + RenderContext context = CreateValidContext(); + context.device = &device; + + CameraRenderer renderer( + std::make_unique(pipelineState), + std::make_unique(pipelineState)); + + auto shadowPass = std::make_unique(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(); + camera->SetPrimary(true); + camera->SetDepth(2.0f); + + auto pipelineState = std::make_shared(); + auto allocationState = std::make_shared(); + MockShadowDevice device(allocationState); + + RenderContext context = CreateValidContext(); + context.device = &device; + + { + CameraRenderer renderer( + std::make_unique(pipelineState), + std::make_unique(pipelineState)); + + auto shadowPass = std::make_unique(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");