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

@@ -20,7 +20,7 @@ struct CameraRenderer::DirectionalShadowSurfaceResources {
uint32_t height = 0; uint32_t height = 0;
RHI::RHITexture* depthTexture = nullptr; RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr; RHI::RHIResourceView* depthView = nullptr;
RHI::RHIResourceView* shaderView = nullptr; RHI::RHIResourceView* depthShaderView = nullptr;
RenderSurface surface = {}; RenderSurface surface = {};
DirectionalShadowSurfaceResources() = default; DirectionalShadowSurfaceResources() = default;
@@ -37,22 +37,22 @@ struct CameraRenderer::DirectionalShadowSurfaceResources {
height == plan.mapHeight && height == plan.mapHeight &&
depthTexture != nullptr && depthTexture != nullptr &&
depthView != nullptr && depthView != nullptr &&
shaderView != nullptr; depthShaderView != nullptr;
} }
void Reset() { void Reset() {
if (shaderView != nullptr) {
shaderView->Shutdown();
delete shaderView;
shaderView = nullptr;
}
if (depthView != nullptr) { if (depthView != nullptr) {
depthView->Shutdown(); depthView->Shutdown();
delete depthView; delete depthView;
depthView = nullptr; depthView = nullptr;
} }
if (depthShaderView != nullptr) {
depthShaderView->Shutdown();
delete depthShaderView;
depthShaderView = nullptr;
}
if (depthTexture != nullptr) { if (depthTexture != nullptr) {
depthTexture->Shutdown(); depthTexture->Shutdown();
delete depthTexture; delete depthTexture;
@@ -68,6 +68,8 @@ struct CameraRenderer::DirectionalShadowSurfaceResources {
namespace { namespace {
constexpr RHI::Format kDirectionalShadowMapFormat = RHI::Format::D32_Float;
bool InitializePassSequence( bool InitializePassSequence(
RenderPassSequence* sequence, RenderPassSequence* sequence,
const RenderContext& context, const RenderContext& context,
@@ -315,7 +317,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect
depthDesc.depth = 1; depthDesc.depth = 1;
depthDesc.mipLevels = 1; depthDesc.mipLevels = 1;
depthDesc.arraySize = 1; depthDesc.arraySize = 1;
depthDesc.format = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt); depthDesc.format = static_cast<uint32_t>(kDirectionalShadowMapFormat);
depthDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D); depthDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
depthDesc.sampleCount = 1; depthDesc.sampleCount = 1;
depthDesc.sampleQuality = 0; depthDesc.sampleQuality = 0;
@@ -327,7 +329,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect
} }
RHI::ResourceViewDesc depthViewDesc = {}; RHI::ResourceViewDesc depthViewDesc = {};
depthViewDesc.format = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt); depthViewDesc.format = static_cast<uint32_t>(kDirectionalShadowMapFormat);
depthViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; depthViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
depthViewDesc.mipLevel = 0; depthViewDesc.mipLevel = 0;
RHI::RHIResourceView* depthView = RHI::RHIResourceView* depthView =
@@ -338,13 +340,12 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect
return nullptr; return nullptr;
} }
RHI::ResourceViewDesc shaderViewDesc = {}; RHI::ResourceViewDesc depthShaderViewDesc = {};
shaderViewDesc.format = depthDesc.format; depthShaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
shaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; depthShaderViewDesc.mipLevel = 0;
shaderViewDesc.mipLevel = 0; RHI::RHIResourceView* depthShaderView =
RHI::RHIResourceView* shaderView = context.device->CreateShaderResourceView(depthTexture, depthShaderViewDesc);
context.device->CreateShaderResourceView(depthTexture, shaderViewDesc); if (depthShaderView == nullptr) {
if (shaderView == nullptr) {
depthView->Shutdown(); depthView->Shutdown();
delete depthView; delete depthView;
depthTexture->Shutdown(); depthTexture->Shutdown();
@@ -358,7 +359,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect
m_directionalShadowSurface->height = plan.mapHeight; m_directionalShadowSurface->height = plan.mapHeight;
m_directionalShadowSurface->depthTexture = depthTexture; m_directionalShadowSurface->depthTexture = depthTexture;
m_directionalShadowSurface->depthView = depthView; m_directionalShadowSurface->depthView = depthView;
m_directionalShadowSurface->shaderView = shaderView; m_directionalShadowSurface->depthShaderView = depthShaderView;
m_directionalShadowSurface->surface = RenderSurface(plan.mapWidth, plan.mapHeight); m_directionalShadowSurface->surface = RenderSurface(plan.mapWidth, plan.mapHeight);
m_directionalShadowSurface->surface.SetDepthAttachment(depthView); m_directionalShadowSurface->surface.SetDepthAttachment(depthView);
} }
@@ -400,6 +401,7 @@ bool CameraRenderer::Render(
} }
resolvedShadowCaster.surface = directionalShadowSurface->surface; resolvedShadowCaster.surface = directionalShadowSurface->surface;
resolvedShadowCaster.clearFlags = RenderClearFlags::Depth;
if (!resolvedShadowCaster.hasCameraDataOverride) { if (!resolvedShadowCaster.hasCameraDataOverride) {
resolvedShadowCaster.hasCameraDataOverride = true; resolvedShadowCaster.hasCameraDataOverride = true;
resolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData; resolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData;
@@ -417,7 +419,7 @@ bool CameraRenderer::Render(
if (request.directionalShadow.IsValid()) { if (request.directionalShadow.IsValid()) {
RHI::RHIResourceView* shadowMapView = RHI::RHIResourceView* shadowMapView =
directionalShadowSurface != nullptr ? directionalShadowSurface->shaderView : nullptr; directionalShadowSurface != nullptr ? directionalShadowSurface->depthShaderView : nullptr;
sceneData.lighting.mainDirectionalShadow = sceneData.lighting.mainDirectionalShadow =
BuildDirectionalShadowData(request.directionalShadow, shadowMapView); BuildDirectionalShadowData(request.directionalShadow, shadowMapView);
} }

View File

@@ -756,6 +756,132 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
EXPECT_EQ(allocationState->destroyTextureCalls, 1); 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) { TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
Scene scene("CameraRendererInvalidShadowScene"); Scene scene("CameraRendererInvalidShadowScene");