Graph-ify camera stage render passes
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
#include <XCEngine/Resources/Shader/ShaderKeywordTypes.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "Rendering/Internal/RenderPassGraphUtils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -591,15 +593,33 @@ class MockObjectIdPass final : public RenderPass {
|
||||
public:
|
||||
MockObjectIdPass(
|
||||
std::shared_ptr<MockPipelineState> state,
|
||||
bool renderResult = true)
|
||||
bool renderResult = true,
|
||||
bool supportsRenderGraph = false)
|
||||
: m_state(std::move(state))
|
||||
, m_renderResult(renderResult) {
|
||||
, m_renderResult(renderResult)
|
||||
, m_supportsRenderGraph(supportsRenderGraph) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return "MockObjectIdPass";
|
||||
}
|
||||
|
||||
bool SupportsRenderGraph() const override {
|
||||
return m_supportsRenderGraph;
|
||||
}
|
||||
|
||||
bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override {
|
||||
m_state->eventLog.push_back("record:objectId");
|
||||
return Internal::RecordRasterRenderPass(
|
||||
*this,
|
||||
context,
|
||||
{
|
||||
false,
|
||||
true,
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext& context) override {
|
||||
m_state->eventLog.push_back("objectId");
|
||||
lastSurfaceAutoTransitionEnabled = context.surface.IsAutoTransitionEnabled();
|
||||
@@ -615,6 +635,7 @@ public:
|
||||
private:
|
||||
std::shared_ptr<MockPipelineState> m_state;
|
||||
bool m_renderResult = true;
|
||||
bool m_supportsRenderGraph = false;
|
||||
|
||||
public:
|
||||
bool lastSurfaceAutoTransitionEnabled = true;
|
||||
@@ -628,17 +649,35 @@ public:
|
||||
std::shared_ptr<MockPipelineState> state,
|
||||
const char* label,
|
||||
bool initializeResult = true,
|
||||
bool executeResult = true)
|
||||
bool executeResult = true,
|
||||
bool supportsRenderGraph = false)
|
||||
: m_state(std::move(state))
|
||||
, m_label(label)
|
||||
, m_initializeResult(initializeResult)
|
||||
, m_executeResult(executeResult) {
|
||||
, m_executeResult(executeResult)
|
||||
, m_supportsRenderGraph(supportsRenderGraph) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return m_label;
|
||||
}
|
||||
|
||||
bool SupportsRenderGraph() const override {
|
||||
return m_supportsRenderGraph;
|
||||
}
|
||||
|
||||
bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override {
|
||||
m_state->eventLog.push_back(std::string("record:") + m_label);
|
||||
return Internal::RecordRasterRenderPass(
|
||||
*this,
|
||||
context,
|
||||
{
|
||||
false,
|
||||
true,
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext&) override {
|
||||
m_state->eventLog.push_back(std::string("init:") + m_label);
|
||||
return m_initializeResult;
|
||||
@@ -690,6 +729,7 @@ private:
|
||||
const char* m_label = "";
|
||||
bool m_initializeResult = true;
|
||||
bool m_executeResult = true;
|
||||
bool m_supportsRenderGraph = false;
|
||||
};
|
||||
|
||||
class TrackingPass final : public RenderPass {
|
||||
@@ -698,17 +738,38 @@ public:
|
||||
std::shared_ptr<MockPipelineState> state,
|
||||
const char* label,
|
||||
bool initializeResult = true,
|
||||
bool executeResult = true)
|
||||
bool executeResult = true,
|
||||
bool supportsRenderGraph = false)
|
||||
: m_state(std::move(state))
|
||||
, m_label(label)
|
||||
, m_initializeResult(initializeResult)
|
||||
, m_executeResult(executeResult) {
|
||||
, m_executeResult(executeResult)
|
||||
, m_supportsRenderGraph(supportsRenderGraph) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return m_label;
|
||||
}
|
||||
|
||||
bool SupportsRenderGraph() const override {
|
||||
return m_supportsRenderGraph;
|
||||
}
|
||||
|
||||
bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override {
|
||||
m_state->eventLog.push_back(std::string("record:") + m_label);
|
||||
const bool writesColor =
|
||||
context.sourceSurface != nullptr ||
|
||||
!context.colorTargets.empty();
|
||||
return Internal::RecordRasterRenderPass(
|
||||
*this,
|
||||
context,
|
||||
{
|
||||
context.sourceSurface != nullptr,
|
||||
writesColor,
|
||||
context.depthTarget.IsValid()
|
||||
});
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext&) override {
|
||||
m_state->eventLog.push_back(std::string("init:") + m_label);
|
||||
return m_initializeResult;
|
||||
@@ -737,6 +798,7 @@ private:
|
||||
const char* m_label = "";
|
||||
bool m_initializeResult = true;
|
||||
bool m_executeResult = true;
|
||||
bool m_supportsRenderGraph = false;
|
||||
|
||||
public:
|
||||
uint32_t lastSurfaceWidth = 0;
|
||||
@@ -1403,6 +1465,133 @@ TEST(CameraRenderer_Test, KeepsPostProcessAndFinalOutputScratchSurfacesIndepende
|
||||
delete sourceColorAttachment;
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, RecordsGraphCapableFullscreenSequencePassesInStageOrder) {
|
||||
Scene scene("CameraRendererGraphFullscreenSequences");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetDepth(5.0f);
|
||||
|
||||
auto pipelineState = std::make_shared<MockPipelineState>();
|
||||
auto allocationState = std::make_shared<MockShadowAllocationState>();
|
||||
MockShadowDevice device(allocationState);
|
||||
CameraRenderer renderer(std::make_unique<MockPipeline>(pipelineState));
|
||||
|
||||
auto postFirstPass = std::make_unique<TrackingPass>(pipelineState, "postA", true, true, true);
|
||||
auto postSecondPass = std::make_unique<TrackingPass>(pipelineState, "postB", true, true, true);
|
||||
TrackingPass* postSecondPassRaw = postSecondPass.get();
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::move(postFirstPass));
|
||||
postProcessPasses.AddPass(std::move(postSecondPass));
|
||||
|
||||
auto finalFirstPass = std::make_unique<TrackingPass>(pipelineState, "finalA", true, true, true);
|
||||
TrackingPass* finalFirstPassRaw = finalFirstPass.get();
|
||||
auto finalSecondPass = std::make_unique<TrackingPass>(pipelineState, "finalB", true, true, true);
|
||||
TrackingPass* finalSecondPassRaw = finalSecondPass.get();
|
||||
RenderPassSequence finalOutputPasses;
|
||||
finalOutputPasses.AddPass(std::move(finalFirstPass));
|
||||
finalOutputPasses.AddPass(std::move(finalSecondPass));
|
||||
|
||||
auto* sourceColorAttachment = new MockShadowView(
|
||||
allocationState,
|
||||
XCEngine::RHI::ResourceViewType::RenderTarget,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D);
|
||||
auto* sourceColorShaderView = new MockShadowView(
|
||||
allocationState,
|
||||
XCEngine::RHI::ResourceViewType::ShaderResource,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D);
|
||||
auto* postProcessDestination = new MockShadowView(
|
||||
allocationState,
|
||||
XCEngine::RHI::ResourceViewType::RenderTarget,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D);
|
||||
auto* finalOutputSource = new MockShadowView(
|
||||
allocationState,
|
||||
XCEngine::RHI::ResourceViewType::ShaderResource,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D);
|
||||
auto* finalOutputDestination = new MockShadowView(
|
||||
allocationState,
|
||||
XCEngine::RHI::ResourceViewType::RenderTarget,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D);
|
||||
|
||||
RenderContext context = CreateValidContext();
|
||||
context.device = &device;
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = context;
|
||||
request.surface = RenderSurface(800, 600);
|
||||
request.surface.SetColorAttachment(finalOutputDestination);
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
request.postProcess.sourceSurface = RenderSurface(256, 128);
|
||||
request.postProcess.sourceSurface.SetColorAttachment(sourceColorAttachment);
|
||||
request.postProcess.sourceColorView = sourceColorShaderView;
|
||||
request.postProcess.destinationSurface = RenderSurface(512, 256);
|
||||
request.postProcess.destinationSurface.SetColorAttachment(postProcessDestination);
|
||||
request.postProcess.passes = &postProcessPasses;
|
||||
request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
|
||||
request.finalOutput.sourceColorView = finalOutputSource;
|
||||
request.finalOutput.destinationSurface = RenderSurface(800, 600);
|
||||
request.finalOutput.destinationSurface.SetColorAttachment(finalOutputDestination);
|
||||
request.finalOutput.passes = &finalOutputPasses;
|
||||
|
||||
ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request)));
|
||||
|
||||
ASSERT_NE(postSecondPassRaw, nullptr);
|
||||
EXPECT_TRUE(postSecondPassRaw->lastHasSourceSurface);
|
||||
EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceWidth, 512u);
|
||||
EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceHeight, 256u);
|
||||
EXPECT_EQ(
|
||||
postSecondPassRaw->lastSourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
ASSERT_NE(finalFirstPassRaw, nullptr);
|
||||
EXPECT_TRUE(finalFirstPassRaw->lastHasSourceSurface);
|
||||
EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceWidth, 512u);
|
||||
EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceHeight, 256u);
|
||||
EXPECT_EQ(finalFirstPassRaw->lastSourceColorView, finalOutputSource);
|
||||
EXPECT_EQ(
|
||||
finalFirstPassRaw->lastSourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
ASSERT_NE(finalSecondPassRaw, nullptr);
|
||||
EXPECT_TRUE(finalSecondPassRaw->lastHasSourceSurface);
|
||||
EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceWidth, 800u);
|
||||
EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceHeight, 600u);
|
||||
EXPECT_NE(finalSecondPassRaw->lastSourceColorView, nullptr);
|
||||
EXPECT_NE(finalSecondPassRaw->lastSourceColorView, finalOutputSource);
|
||||
EXPECT_EQ(
|
||||
finalSecondPassRaw->lastSourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
EXPECT_EQ(
|
||||
pipelineState->eventLog,
|
||||
(std::vector<std::string>{
|
||||
"record:postA",
|
||||
"record:postB",
|
||||
"record:finalA",
|
||||
"record:finalB",
|
||||
"pipeline",
|
||||
"init:postA",
|
||||
"init:postB",
|
||||
"postA",
|
||||
"postB",
|
||||
"init:finalA",
|
||||
"init:finalB",
|
||||
"finalA",
|
||||
"finalB" }));
|
||||
|
||||
delete finalOutputDestination;
|
||||
delete finalOutputSource;
|
||||
delete postProcessDestination;
|
||||
delete sourceColorShaderView;
|
||||
delete sourceColorAttachment;
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
Scene scene("CameraRendererFormalFrameStageScene");
|
||||
|
||||
@@ -1551,6 +1740,78 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, RecordsGraphCapableStandalonePassStagesBeforeExecution) {
|
||||
Scene scene("CameraRendererGraphStandaloneStages");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetDepth(3.0f);
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
CameraRenderer renderer(
|
||||
std::make_unique<MockPipeline>(state),
|
||||
std::make_unique<MockObjectIdPass>(state, true, true));
|
||||
|
||||
auto shadowPass = std::make_unique<MockScenePass>(state, "shadowCaster", true, true, true);
|
||||
MockScenePass* shadowPassRaw = shadowPass.get();
|
||||
renderer.SetShadowCasterPass(std::move(shadowPass));
|
||||
|
||||
auto depthPass = std::make_unique<MockScenePass>(state, "depthOnly", true, true, true);
|
||||
MockScenePass* depthPassRaw = depthPass.get();
|
||||
renderer.SetDepthOnlyPass(std::move(depthPass));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
|
||||
request.shadowCaster.surface = RenderSurface(128, 64);
|
||||
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.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);
|
||||
|
||||
request.objectId.surface = RenderSurface(320, 180);
|
||||
request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(6));
|
||||
request.objectId.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(7));
|
||||
|
||||
ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request)));
|
||||
|
||||
EXPECT_EQ(
|
||||
state->eventLog,
|
||||
(std::vector<std::string>{
|
||||
"record:shadowCaster",
|
||||
"record:depthOnly",
|
||||
"record:objectId",
|
||||
"init:shadowCaster",
|
||||
"shadowCaster",
|
||||
"init:depthOnly",
|
||||
"depthOnly",
|
||||
"pipeline",
|
||||
"objectId" }));
|
||||
ASSERT_NE(shadowPassRaw, nullptr);
|
||||
EXPECT_EQ(shadowPassRaw->lastViewportWidth, 128u);
|
||||
EXPECT_EQ(shadowPassRaw->lastViewportHeight, 64u);
|
||||
EXPECT_FALSE(shadowPassRaw->lastSurfaceAutoTransitionEnabled);
|
||||
EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f));
|
||||
ASSERT_NE(depthPassRaw, nullptr);
|
||||
EXPECT_EQ(depthPassRaw->lastViewportWidth, 96u);
|
||||
EXPECT_EQ(depthPassRaw->lastViewportHeight, 48u);
|
||||
EXPECT_FALSE(depthPassRaw->lastSurfaceAutoTransitionEnabled);
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.r, 0.3f);
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.g, 0.2f);
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.b, 0.1f);
|
||||
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, UsesGraphManagedSurfaceForMainSceneWhenOutputAttachmentsExist) {
|
||||
Scene scene("CameraRendererGraphManagedMainScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user