Add shadow surface reuse coverage for camera renderer
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user