Add shadow surface reuse coverage for camera renderer
This commit is contained in:
@@ -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<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.sampleCount = 1;
|
||||
depthDesc.sampleQuality = 0;
|
||||
@@ -327,7 +329,7 @@ CameraRenderer::DirectionalShadowSurfaceResources* CameraRenderer::AcquireDirect
|
||||
}
|
||||
|
||||
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.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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user