Enable depth-only shadow pass execution
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/RHI/RHICommandList.h>
|
||||
#include <XCEngine/RHI/RHICommandQueue.h>
|
||||
#include <XCEngine/RHI/RHIDevice.h>
|
||||
#include <XCEngine/RHI/RHIResourceView.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Rendering/CameraRenderer.h>
|
||||
#include <XCEngine/Rendering/ObjectIdPass.h>
|
||||
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
||||
@@ -40,6 +45,193 @@ struct MockPipelineState {
|
||||
std::vector<std::string> eventLog;
|
||||
};
|
||||
|
||||
struct MockShadowAllocationState {
|
||||
int createTextureCalls = 0;
|
||||
int shutdownTextureCalls = 0;
|
||||
int destroyTextureCalls = 0;
|
||||
int createDepthViewCalls = 0;
|
||||
int shutdownDepthViewCalls = 0;
|
||||
int destroyDepthViewCalls = 0;
|
||||
uint32_t lastTextureWidth = 0;
|
||||
uint32_t lastTextureHeight = 0;
|
||||
XCEngine::RHI::Format lastTextureFormat = XCEngine::RHI::Format::Unknown;
|
||||
XCEngine::RHI::Format lastDepthViewFormat = XCEngine::RHI::Format::Unknown;
|
||||
};
|
||||
|
||||
class MockShadowTexture final : public XCEngine::RHI::RHITexture {
|
||||
public:
|
||||
MockShadowTexture(
|
||||
std::shared_ptr<MockShadowAllocationState> state,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
XCEngine::RHI::Format format)
|
||||
: m_state(std::move(state))
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
, m_format(format) {
|
||||
}
|
||||
|
||||
~MockShadowTexture() override {
|
||||
++m_state->destroyTextureCalls;
|
||||
}
|
||||
|
||||
uint32_t GetWidth() const override { return m_width; }
|
||||
uint32_t GetHeight() const override { return m_height; }
|
||||
uint32_t GetDepth() const override { return 1; }
|
||||
uint32_t GetMipLevels() const override { return 1; }
|
||||
XCEngine::RHI::Format GetFormat() const override { return m_format; }
|
||||
XCEngine::RHI::TextureType GetTextureType() const override { return XCEngine::RHI::TextureType::Texture2D; }
|
||||
XCEngine::RHI::ResourceStates GetState() const override { return m_stateValue; }
|
||||
void SetState(XCEngine::RHI::ResourceStates state) override { m_stateValue = state; }
|
||||
void* GetNativeHandle() override { return nullptr; }
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
void SetName(const std::string& name) override { m_name = name; }
|
||||
|
||||
void Shutdown() override {
|
||||
++m_state->shutdownTextureCalls;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockShadowAllocationState> m_state;
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown;
|
||||
XCEngine::RHI::ResourceStates m_stateValue = XCEngine::RHI::ResourceStates::DepthWrite;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class MockShadowView final : public XCEngine::RHI::RHIResourceView {
|
||||
public:
|
||||
MockShadowView(
|
||||
std::shared_ptr<MockShadowAllocationState> state,
|
||||
XCEngine::RHI::ResourceViewType viewType,
|
||||
XCEngine::RHI::Format format,
|
||||
XCEngine::RHI::ResourceViewDimension dimension)
|
||||
: m_state(std::move(state))
|
||||
, m_viewType(viewType)
|
||||
, m_format(format)
|
||||
, m_dimension(dimension) {
|
||||
}
|
||||
|
||||
~MockShadowView() override {
|
||||
++m_state->destroyDepthViewCalls;
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
++m_state->shutdownDepthViewCalls;
|
||||
}
|
||||
|
||||
void* GetNativeHandle() override { return nullptr; }
|
||||
bool IsValid() const override { return true; }
|
||||
XCEngine::RHI::ResourceViewType GetViewType() const override { return m_viewType; }
|
||||
XCEngine::RHI::ResourceViewDimension GetDimension() const override { return m_dimension; }
|
||||
XCEngine::RHI::Format GetFormat() const override { return m_format; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockShadowAllocationState> m_state;
|
||||
XCEngine::RHI::ResourceViewType m_viewType = XCEngine::RHI::ResourceViewType::DepthStencil;
|
||||
XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown;
|
||||
XCEngine::RHI::ResourceViewDimension m_dimension = XCEngine::RHI::ResourceViewDimension::Unknown;
|
||||
};
|
||||
|
||||
class MockShadowDevice final : public XCEngine::RHI::RHIDevice {
|
||||
public:
|
||||
explicit MockShadowDevice(std::shared_ptr<MockShadowAllocationState> state)
|
||||
: m_state(std::move(state)) {
|
||||
}
|
||||
|
||||
bool Initialize(const XCEngine::RHI::RHIDeviceDesc&) override { return true; }
|
||||
void Shutdown() override {}
|
||||
|
||||
XCEngine::RHI::RHIBuffer* CreateBuffer(const XCEngine::RHI::BufferDesc&) override { return nullptr; }
|
||||
|
||||
XCEngine::RHI::RHITexture* CreateTexture(const XCEngine::RHI::TextureDesc& desc) override {
|
||||
++m_state->createTextureCalls;
|
||||
m_state->lastTextureWidth = desc.width;
|
||||
m_state->lastTextureHeight = desc.height;
|
||||
m_state->lastTextureFormat = static_cast<XCEngine::RHI::Format>(desc.format);
|
||||
return new MockShadowTexture(
|
||||
m_state,
|
||||
desc.width,
|
||||
desc.height,
|
||||
static_cast<XCEngine::RHI::Format>(desc.format));
|
||||
}
|
||||
|
||||
XCEngine::RHI::RHITexture* CreateTexture(
|
||||
const XCEngine::RHI::TextureDesc& desc,
|
||||
const void*,
|
||||
size_t,
|
||||
uint32_t) override {
|
||||
return CreateTexture(desc);
|
||||
}
|
||||
|
||||
XCEngine::RHI::RHISwapChain* CreateSwapChain(
|
||||
const XCEngine::RHI::SwapChainDesc&,
|
||||
XCEngine::RHI::RHICommandQueue*) override { return nullptr; }
|
||||
XCEngine::RHI::RHICommandList* CreateCommandList(const XCEngine::RHI::CommandListDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHICommandQueue* CreateCommandQueue(const XCEngine::RHI::CommandQueueDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIShader* CreateShader(const XCEngine::RHI::ShaderCompileDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIPipelineState* CreatePipelineState(const XCEngine::RHI::GraphicsPipelineDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIPipelineLayout* CreatePipelineLayout(const XCEngine::RHI::RHIPipelineLayoutDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIFence* CreateFence(const XCEngine::RHI::FenceDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHISampler* CreateSampler(const XCEngine::RHI::SamplerDesc&) override { return nullptr; }
|
||||
|
||||
XCEngine::RHI::RHIRenderPass* CreateRenderPass(
|
||||
uint32_t,
|
||||
const XCEngine::RHI::AttachmentDesc*,
|
||||
const XCEngine::RHI::AttachmentDesc*) override { return nullptr; }
|
||||
XCEngine::RHI::RHIFramebuffer* CreateFramebuffer(
|
||||
XCEngine::RHI::RHIRenderPass*,
|
||||
uint32_t,
|
||||
uint32_t,
|
||||
uint32_t,
|
||||
XCEngine::RHI::RHIResourceView**,
|
||||
XCEngine::RHI::RHIResourceView*) override { return nullptr; }
|
||||
|
||||
XCEngine::RHI::RHIDescriptorPool* CreateDescriptorPool(const XCEngine::RHI::DescriptorPoolDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIDescriptorSet* CreateDescriptorSet(
|
||||
XCEngine::RHI::RHIDescriptorPool*,
|
||||
const XCEngine::RHI::DescriptorSetLayoutDesc&) override { return nullptr; }
|
||||
|
||||
XCEngine::RHI::RHIResourceView* CreateVertexBufferView(
|
||||
XCEngine::RHI::RHIBuffer*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIResourceView* CreateIndexBufferView(
|
||||
XCEngine::RHI::RHIBuffer*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIResourceView* CreateRenderTargetView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
|
||||
XCEngine::RHI::RHIResourceView* CreateDepthStencilView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc& desc) override {
|
||||
++m_state->createDepthViewCalls;
|
||||
m_state->lastDepthViewFormat = static_cast<XCEngine::RHI::Format>(desc.format);
|
||||
return new MockShadowView(
|
||||
m_state,
|
||||
XCEngine::RHI::ResourceViewType::DepthStencil,
|
||||
static_cast<XCEngine::RHI::Format>(desc.format),
|
||||
desc.dimension);
|
||||
}
|
||||
|
||||
XCEngine::RHI::RHIResourceView* CreateShaderResourceView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
|
||||
const XCEngine::RHI::RHICapabilities& GetCapabilities() const override { return m_capabilities; }
|
||||
const XCEngine::RHI::RHIDeviceInfo& GetDeviceInfo() const override { return m_deviceInfo; }
|
||||
void* GetNativeDevice() override { return nullptr; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockShadowAllocationState> m_state;
|
||||
XCEngine::RHI::RHICapabilities m_capabilities = {};
|
||||
XCEngine::RHI::RHIDeviceInfo m_deviceInfo = {};
|
||||
};
|
||||
|
||||
struct MockPipelineAssetState {
|
||||
int createCalls = 0;
|
||||
std::shared_ptr<MockPipelineState> lastCreatedPipelineState;
|
||||
@@ -416,13 +608,11 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
|
||||
request.shadowCaster.surface = RenderSurface(128, 64);
|
||||
request.shadowCaster.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.shadowCaster.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.shadowCaster.hasCameraDataOverride = true;
|
||||
request.shadowCaster.cameraDataOverride.worldPosition = XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f);
|
||||
|
||||
request.depthOnly.surface = RenderSurface(96, 48);
|
||||
request.depthOnly.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
|
||||
request.depthOnly.hasClearColorOverride = true;
|
||||
request.depthOnly.clearColorOverride = XCEngine::Math::Color(0.3f, 0.2f, 0.1f, 1.0f);
|
||||
@@ -451,6 +641,68 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
|
||||
Scene scene("CameraRendererAutoDirectionalShadowScene");
|
||||
|
||||
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);
|
||||
|
||||
CameraRenderer renderer(
|
||||
std::make_unique<MockPipeline>(pipelineState),
|
||||
std::make_unique<MockObjectIdPass>(pipelineState));
|
||||
|
||||
auto shadowPass = std::make_unique<MockScenePass>(pipelineState, "shadowCaster");
|
||||
MockScenePass* shadowPassRaw = shadowPass.get();
|
||||
renderer.SetShadowCasterPass(std::move(shadowPass));
|
||||
|
||||
RenderContext context = CreateValidContext();
|
||||
context.device = &device;
|
||||
|
||||
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;
|
||||
request.directionalShadow.cameraData.worldPosition = XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f);
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_EQ(
|
||||
pipelineState->eventLog,
|
||||
(std::vector<std::string>{
|
||||
"init:shadowCaster",
|
||||
"shadowCaster",
|
||||
"pipeline" }));
|
||||
EXPECT_EQ(shadowPassRaw->lastViewportWidth, 256u);
|
||||
EXPECT_EQ(shadowPassRaw->lastViewportHeight, 128u);
|
||||
EXPECT_EQ(shadowPassRaw->lastSurfaceWidth, 256u);
|
||||
EXPECT_EQ(shadowPassRaw->lastSurfaceHeight, 128u);
|
||||
EXPECT_EQ(shadowPassRaw->lastClearFlags, RenderClearFlags::Depth);
|
||||
EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->createDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->lastTextureWidth, 256u);
|
||||
EXPECT_EQ(allocationState->lastTextureHeight, 128u);
|
||||
EXPECT_EQ(allocationState->lastTextureFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||
EXPECT_EQ(allocationState->lastDepthViewFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->destroyDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
|
||||
Scene scene("CameraRendererInvalidShadowScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user