Formalize camera post-process frame contract
This commit is contained in:
@@ -21,6 +21,8 @@ enum class CameraFrameStage : uint8_t {
|
||||
ShadowCaster,
|
||||
DepthOnly,
|
||||
MainScene,
|
||||
PostProcess,
|
||||
FinalOutput,
|
||||
ObjectId,
|
||||
PostScenePasses,
|
||||
OverlayPasses
|
||||
@@ -32,11 +34,13 @@ struct CameraFrameStageInfo {
|
||||
bool runtimeStage = true;
|
||||
};
|
||||
|
||||
inline constexpr std::array<CameraFrameStageInfo, 7> kOrderedCameraFrameStages = {{
|
||||
inline constexpr std::array<CameraFrameStageInfo, 9> kOrderedCameraFrameStages = {{
|
||||
{ CameraFrameStage::PreScenePasses, "PreScenePasses", false },
|
||||
{ CameraFrameStage::ShadowCaster, "ShadowCaster", true },
|
||||
{ CameraFrameStage::DepthOnly, "DepthOnly", true },
|
||||
{ CameraFrameStage::MainScene, "MainScene", true },
|
||||
{ CameraFrameStage::PostProcess, "PostProcess", true },
|
||||
{ CameraFrameStage::FinalOutput, "FinalOutput", true },
|
||||
{ CameraFrameStage::ObjectId, "ObjectId", false },
|
||||
{ CameraFrameStage::PostScenePasses, "PostScenePasses", false },
|
||||
{ CameraFrameStage::OverlayPasses, "OverlayPasses", false }
|
||||
@@ -52,6 +56,10 @@ inline constexpr const char* GetCameraFrameStageName(CameraFrameStage stage) {
|
||||
return "DepthOnly";
|
||||
case CameraFrameStage::MainScene:
|
||||
return "MainScene";
|
||||
case CameraFrameStage::PostProcess:
|
||||
return "PostProcess";
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return "FinalOutput";
|
||||
case CameraFrameStage::ObjectId:
|
||||
return "ObjectId";
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
@@ -88,6 +96,14 @@ struct ScenePassRenderRequest {
|
||||
using DepthOnlyRenderRequest = ScenePassRenderRequest;
|
||||
using ShadowCasterRenderRequest = ScenePassRenderRequest;
|
||||
|
||||
inline bool HasValidColorTarget(const RenderSurface& surface) {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return !colorAttachments.empty() &&
|
||||
colorAttachments[0] != nullptr &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
|
||||
struct DirectionalShadowRenderPlan {
|
||||
bool enabled = false;
|
||||
Math::Vector3 lightDirection = Math::Vector3::Back();
|
||||
@@ -125,6 +141,25 @@ struct ObjectIdRenderRequest {
|
||||
}
|
||||
};
|
||||
|
||||
struct FullscreenPassRenderRequest {
|
||||
RenderSurface sourceSurface;
|
||||
RenderSurface destinationSurface;
|
||||
RenderPassSequence* passes = nullptr;
|
||||
|
||||
bool IsRequested() const {
|
||||
return passes != nullptr;
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return passes != nullptr &&
|
||||
HasValidColorTarget(sourceSurface) &&
|
||||
HasValidColorTarget(destinationSurface);
|
||||
}
|
||||
};
|
||||
|
||||
using PostProcessRenderRequest = FullscreenPassRenderRequest;
|
||||
using FinalOutputRenderRequest = FullscreenPassRenderRequest;
|
||||
|
||||
struct CameraRenderRequest {
|
||||
const Components::Scene* scene = nullptr;
|
||||
Components::CameraComponent* camera = nullptr;
|
||||
@@ -133,6 +168,8 @@ struct CameraRenderRequest {
|
||||
DepthOnlyRenderRequest depthOnly;
|
||||
ShadowCasterRenderRequest shadowCaster;
|
||||
DirectionalShadowRenderPlan directionalShadow;
|
||||
PostProcessRenderRequest postProcess;
|
||||
FinalOutputRenderRequest finalOutput;
|
||||
ObjectIdRenderRequest objectId;
|
||||
float cameraDepth = 0.0f;
|
||||
uint8_t cameraStackOrder = 0;
|
||||
@@ -153,6 +190,10 @@ struct CameraRenderRequest {
|
||||
return depthOnly.IsRequested();
|
||||
case CameraFrameStage::MainScene:
|
||||
return true;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested();
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested();
|
||||
case CameraFrameStage::ObjectId:
|
||||
return objectId.IsRequested();
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
@@ -168,6 +209,10 @@ struct CameraRenderRequest {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return preScenePasses;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.passes;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.passes;
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return postScenePasses;
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
@@ -192,6 +237,68 @@ struct CameraRenderRequest {
|
||||
return stage == CameraFrameStage::ObjectId ? &objectId : nullptr;
|
||||
}
|
||||
|
||||
const RenderSurface& GetMainSceneSurface() const {
|
||||
if (postProcess.IsRequested()) {
|
||||
return postProcess.sourceSurface;
|
||||
}
|
||||
|
||||
if (finalOutput.IsRequested()) {
|
||||
return finalOutput.sourceSurface;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
const RenderSurface& GetFinalCompositedSurface() const {
|
||||
if (finalOutput.IsRequested()) {
|
||||
return finalOutput.destinationSurface;
|
||||
}
|
||||
|
||||
if (postProcess.IsRequested()) {
|
||||
return postProcess.destinationSurface;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
const RenderSurface* GetOutputSurface(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
case CameraFrameStage::MainScene:
|
||||
return &GetMainSceneSurface();
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return shadowCaster.IsRequested() ? &shadowCaster.surface : nullptr;
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return depthOnly.IsRequested() ? &depthOnly.surface : nullptr;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? &postProcess.destinationSurface : nullptr;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? &finalOutput.destinationSurface : nullptr;
|
||||
case CameraFrameStage::ObjectId:
|
||||
return objectId.IsRequested() ? &objectId.surface : nullptr;
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return &GetFinalCompositedSurface();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const RenderSurface* GetSourceSurface(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? &postProcess.sourceSurface : nullptr;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? &finalOutput.sourceSurface : nullptr;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequiresIntermediateSceneColor() const {
|
||||
return postProcess.IsRequested() || finalOutput.IsRequested();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return scene != nullptr &&
|
||||
camera != nullptr &&
|
||||
|
||||
@@ -16,6 +16,7 @@ struct RenderPassContext {
|
||||
const RenderContext& renderContext;
|
||||
const RenderSurface& surface;
|
||||
const RenderSceneData& sceneData;
|
||||
const RenderSurface* sourceSurface = nullptr;
|
||||
};
|
||||
|
||||
class RenderPass {
|
||||
|
||||
@@ -85,7 +85,8 @@ bool ExecuteScenePassRequest(
|
||||
const RenderPassContext passContext = {
|
||||
context,
|
||||
request.surface,
|
||||
sceneData
|
||||
sceneData,
|
||||
nullptr
|
||||
};
|
||||
return pass->Execute(passContext);
|
||||
}
|
||||
@@ -106,7 +107,8 @@ bool ExecuteStandalonePass(
|
||||
const RenderPassContext passContext = {
|
||||
context,
|
||||
surface,
|
||||
sceneData
|
||||
sceneData,
|
||||
nullptr
|
||||
};
|
||||
return pass->Execute(passContext);
|
||||
}
|
||||
@@ -158,6 +160,8 @@ struct CameraFrameExecutionState {
|
||||
RenderPass* depthOnlyPass = nullptr;
|
||||
RenderPass* shadowCasterPass = nullptr;
|
||||
std::unique_ptr<ScopedInitializedPassSequence> preScenePasses;
|
||||
std::unique_ptr<ScopedInitializedPassSequence> postProcessPasses;
|
||||
std::unique_ptr<ScopedInitializedPassSequence> finalOutputPasses;
|
||||
std::unique_ptr<ScopedInitializedPassSequence> postScenePasses;
|
||||
std::unique_ptr<ScopedInitializedPassSequence> overlayPasses;
|
||||
};
|
||||
@@ -171,12 +175,27 @@ bool ExecutePassSequenceStage(
|
||||
return activeSequence->IsReady() && activeSequence->Execute(passContext);
|
||||
}
|
||||
|
||||
RenderPassContext BuildFrameStagePassContext(
|
||||
CameraFrameStage stage,
|
||||
const CameraRenderRequest& request,
|
||||
const RenderSceneData& sceneData) {
|
||||
const RenderSurface* outputSurface = request.GetOutputSurface(stage);
|
||||
return {
|
||||
request.context,
|
||||
outputSurface != nullptr ? *outputSurface : request.surface,
|
||||
sceneData,
|
||||
request.GetSourceSurface(stage)
|
||||
};
|
||||
}
|
||||
|
||||
bool ExecuteFrameStage(
|
||||
CameraFrameStage stage,
|
||||
const CameraRenderRequest& request,
|
||||
const ShadowCasterRenderRequest& resolvedShadowCaster,
|
||||
const RenderPassContext& passContext,
|
||||
const RenderSceneData& sceneData,
|
||||
CameraFrameExecutionState& executionState) {
|
||||
const RenderPassContext passContext = BuildFrameStagePassContext(stage, request, sceneData);
|
||||
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return ExecutePassSequenceStage(
|
||||
@@ -189,23 +208,35 @@ bool ExecuteFrameStage(
|
||||
executionState.shadowCasterPass,
|
||||
resolvedShadowCaster,
|
||||
request.context,
|
||||
passContext.sceneData);
|
||||
sceneData);
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return ExecuteScenePassRequest(
|
||||
executionState.depthOnlyPass,
|
||||
request.depthOnly,
|
||||
request.context,
|
||||
passContext.sceneData);
|
||||
sceneData);
|
||||
case CameraFrameStage::MainScene:
|
||||
return executionState.pipeline != nullptr &&
|
||||
executionState.pipeline->Render(request.context, request.surface, passContext.sceneData);
|
||||
executionState.pipeline->Render(request.context, passContext.surface, sceneData);
|
||||
case CameraFrameStage::PostProcess:
|
||||
return ExecutePassSequenceStage(
|
||||
executionState.postProcessPasses,
|
||||
request.GetPassSequence(stage),
|
||||
request.context,
|
||||
passContext);
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return ExecutePassSequenceStage(
|
||||
executionState.finalOutputPasses,
|
||||
request.GetPassSequence(stage),
|
||||
request.context,
|
||||
passContext);
|
||||
case CameraFrameStage::ObjectId:
|
||||
return !request.objectId.IsRequested() ||
|
||||
ExecuteStandalonePass(
|
||||
executionState.objectIdPass,
|
||||
request.context,
|
||||
request.objectId.surface,
|
||||
passContext.sceneData);
|
||||
sceneData);
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return ExecutePassSequenceStage(
|
||||
executionState.postScenePasses,
|
||||
@@ -244,8 +275,9 @@ RenderDirectionalShadowData BuildDirectionalShadowData(
|
||||
|
||||
RenderEnvironmentData BuildEnvironmentData(const CameraRenderRequest& request) {
|
||||
RenderEnvironmentData environment = {};
|
||||
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
|
||||
if (request.camera == nullptr ||
|
||||
request.surface.GetDepthAttachment() == nullptr ||
|
||||
mainSceneSurface.GetDepthAttachment() == nullptr ||
|
||||
!HasRenderClearFlag(request.clearFlags, RenderClearFlags::Color) ||
|
||||
!request.camera->IsSkyboxEnabled() ||
|
||||
request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) {
|
||||
@@ -416,11 +448,12 @@ bool CameraRenderer::BuildSceneDataForRequest(
|
||||
const CameraRenderRequest& request,
|
||||
RHI::RHIResourceView* shadowMapView,
|
||||
RenderSceneData& outSceneData) {
|
||||
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
|
||||
outSceneData = m_sceneExtractor.ExtractForCamera(
|
||||
*request.scene,
|
||||
*request.camera,
|
||||
request.surface.GetRenderAreaWidth(),
|
||||
request.surface.GetRenderAreaHeight());
|
||||
mainSceneSurface.GetRenderAreaWidth(),
|
||||
mainSceneSurface.GetRenderAreaHeight());
|
||||
if (!outSceneData.HasCamera()) {
|
||||
return false;
|
||||
}
|
||||
@@ -443,11 +476,6 @@ bool CameraRenderer::ExecuteRenderPlan(
|
||||
const CameraRenderRequest& request,
|
||||
const ShadowCasterRenderRequest& resolvedShadowCaster,
|
||||
const RenderSceneData& sceneData) {
|
||||
const RenderPassContext passContext = {
|
||||
request.context,
|
||||
request.surface,
|
||||
sceneData
|
||||
};
|
||||
CameraFrameExecutionState executionState = {};
|
||||
executionState.pipeline = m_pipeline.get();
|
||||
executionState.objectIdPass = m_objectIdPass.get();
|
||||
@@ -464,7 +492,7 @@ bool CameraRenderer::ExecuteRenderPlan(
|
||||
stageInfo.stage,
|
||||
request,
|
||||
resolvedShadowCaster,
|
||||
passContext,
|
||||
sceneData,
|
||||
executionState)) {
|
||||
return false;
|
||||
}
|
||||
@@ -479,14 +507,23 @@ bool CameraRenderer::Render(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.surface.GetRenderAreaWidth() == 0 ||
|
||||
request.surface.GetRenderAreaHeight() == 0) {
|
||||
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
|
||||
if (mainSceneSurface.GetRenderAreaWidth() == 0 ||
|
||||
mainSceneSurface.GetRenderAreaHeight() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (request.depthOnly.IsRequested() &&
|
||||
!request.depthOnly.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (request.postProcess.IsRequested() &&
|
||||
!request.postProcess.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (request.finalOutput.IsRequested() &&
|
||||
!request.finalOutput.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (request.objectId.IsRequested() &&
|
||||
!request.objectId.IsValid()) {
|
||||
return false;
|
||||
|
||||
@@ -390,6 +390,11 @@ public:
|
||||
lastWorldPosition = context.sceneData.cameraData.worldPosition;
|
||||
lastSurfaceWidth = context.surface.GetRenderAreaWidth();
|
||||
lastSurfaceHeight = context.surface.GetRenderAreaHeight();
|
||||
lastHasSourceSurface = context.sourceSurface != nullptr;
|
||||
if (context.sourceSurface != nullptr) {
|
||||
lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth();
|
||||
lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight();
|
||||
}
|
||||
return m_executeResult;
|
||||
}
|
||||
|
||||
@@ -404,6 +409,9 @@ public:
|
||||
XCEngine::Math::Vector3 lastWorldPosition = XCEngine::Math::Vector3::Zero();
|
||||
uint32_t lastSurfaceWidth = 0;
|
||||
uint32_t lastSurfaceHeight = 0;
|
||||
bool lastHasSourceSurface = false;
|
||||
uint32_t lastSourceSurfaceWidth = 0;
|
||||
uint32_t lastSourceSurfaceHeight = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockPipelineState> m_state;
|
||||
@@ -434,8 +442,15 @@ public:
|
||||
return m_initializeResult;
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext&) override {
|
||||
bool Execute(const RenderPassContext& context) override {
|
||||
m_state->eventLog.push_back(m_label);
|
||||
lastSurfaceWidth = context.surface.GetRenderAreaWidth();
|
||||
lastSurfaceHeight = context.surface.GetRenderAreaHeight();
|
||||
lastHasSourceSurface = context.sourceSurface != nullptr;
|
||||
if (context.sourceSurface != nullptr) {
|
||||
lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth();
|
||||
lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight();
|
||||
}
|
||||
return m_executeResult;
|
||||
}
|
||||
|
||||
@@ -448,6 +463,13 @@ private:
|
||||
const char* m_label = "";
|
||||
bool m_initializeResult = true;
|
||||
bool m_executeResult = true;
|
||||
|
||||
public:
|
||||
uint32_t lastSurfaceWidth = 0;
|
||||
uint32_t lastSurfaceHeight = 0;
|
||||
bool lastHasSourceSurface = false;
|
||||
uint32_t lastSourceSurfaceWidth = 0;
|
||||
uint32_t lastSourceSurfaceHeight = 0;
|
||||
};
|
||||
|
||||
RenderContext CreateValidContext() {
|
||||
@@ -464,9 +486,13 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
|
||||
CameraRenderRequest request;
|
||||
|
||||
RenderPassSequence prePasses;
|
||||
RenderPassSequence postProcessPasses;
|
||||
RenderPassSequence finalOutputPasses;
|
||||
RenderPassSequence postPasses;
|
||||
RenderPassSequence overlayPasses;
|
||||
request.preScenePasses = &prePasses;
|
||||
request.postProcess.passes = &postProcessPasses;
|
||||
request.finalOutput.passes = &finalOutputPasses;
|
||||
request.postScenePasses = &postPasses;
|
||||
request.overlayPasses = &overlayPasses;
|
||||
|
||||
@@ -479,30 +505,48 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
|
||||
request.depthOnly.surface = RenderSurface(96, 48);
|
||||
request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
|
||||
request.objectId.surface = RenderSurface(320, 180);
|
||||
request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.objectId.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
request.postProcess.sourceSurface = RenderSurface(256, 128);
|
||||
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.postProcess.destinationSurface = RenderSurface(512, 256);
|
||||
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
|
||||
ASSERT_EQ(kOrderedCameraFrameStages.size(), 7u);
|
||||
request.finalOutput.sourceSurface = RenderSurface(512, 256);
|
||||
request.finalOutput.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
|
||||
request.finalOutput.destinationSurface = RenderSurface(640, 360);
|
||||
request.finalOutput.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5));
|
||||
|
||||
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_EQ(kOrderedCameraFrameStages.size(), 9u);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[0].stage, CameraFrameStage::PreScenePasses);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[1].stage, CameraFrameStage::ShadowCaster);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[2].stage, CameraFrameStage::DepthOnly);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[3].stage, CameraFrameStage::MainScene);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[4].stage, CameraFrameStage::ObjectId);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[5].stage, CameraFrameStage::PostScenePasses);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[6].stage, CameraFrameStage::OverlayPasses);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[4].stage, CameraFrameStage::PostProcess);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[5].stage, CameraFrameStage::FinalOutput);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[6].stage, CameraFrameStage::ObjectId);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[7].stage, CameraFrameStage::PostScenePasses);
|
||||
EXPECT_EQ(kOrderedCameraFrameStages[8].stage, CameraFrameStage::OverlayPasses);
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::MainScene), "MainScene");
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::PostProcess), "PostProcess");
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::FinalOutput), "FinalOutput");
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::OverlayPasses), "OverlayPasses");
|
||||
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PreScenePasses));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::ShadowCaster));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::DepthOnly));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::MainScene));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PostProcess));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::FinalOutput));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::ObjectId));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PostScenePasses));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::OverlayPasses));
|
||||
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PreScenePasses), &prePasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PostProcess), &postProcessPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::FinalOutput), &finalOutputPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PostScenePasses), &postPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::OverlayPasses), &overlayPasses);
|
||||
EXPECT_EQ(request.GetScenePassRequest(CameraFrameStage::ShadowCaster), &request.shadowCaster);
|
||||
@@ -510,6 +554,17 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
|
||||
EXPECT_EQ(request.GetScenePassRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_EQ(request.GetObjectIdRequest(CameraFrameStage::ObjectId), &request.objectId);
|
||||
EXPECT_EQ(request.GetObjectIdRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_TRUE(request.RequiresIntermediateSceneColor());
|
||||
EXPECT_EQ(request.GetMainSceneSurface().GetRenderAreaWidth(), 256u);
|
||||
EXPECT_EQ(request.GetMainSceneSurface().GetRenderAreaHeight(), 128u);
|
||||
EXPECT_EQ(request.GetFinalCompositedSurface().GetRenderAreaWidth(), 640u);
|
||||
EXPECT_EQ(request.GetFinalCompositedSurface().GetRenderAreaHeight(), 360u);
|
||||
ASSERT_NE(request.GetOutputSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(request.GetOutputSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 512u);
|
||||
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 256u);
|
||||
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::FinalOutput), nullptr);
|
||||
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::FinalOutput)->GetRenderAreaWidth(), 512u);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
|
||||
@@ -694,6 +749,74 @@ TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRe
|
||||
"shutdown:pre" }));
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages) {
|
||||
Scene scene("CameraRendererPostProcessScene");
|
||||
|
||||
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));
|
||||
|
||||
auto postProcessPass = std::make_unique<MockScenePass>(state, "postProcess");
|
||||
MockScenePass* postProcessPassRaw = postProcessPass.get();
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::move(postProcessPass));
|
||||
|
||||
auto finalOutputPass = std::make_unique<MockScenePass>(state, "finalOutput");
|
||||
MockScenePass* finalOutputPassRaw = finalOutputPass.get();
|
||||
RenderPassSequence finalOutputPasses;
|
||||
finalOutputPasses.AddPass(std::move(finalOutputPass));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(800, 600);
|
||||
request.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
|
||||
request.postProcess.sourceSurface = RenderSurface(256, 128);
|
||||
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.postProcess.destinationSurface = RenderSurface(512, 256);
|
||||
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
request.postProcess.passes = &postProcessPasses;
|
||||
|
||||
request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
|
||||
request.finalOutput.destinationSurface = request.surface;
|
||||
request.finalOutput.passes = &finalOutputPasses;
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_EQ(state->lastSurfaceWidth, 256u);
|
||||
EXPECT_EQ(state->lastSurfaceHeight, 128u);
|
||||
EXPECT_EQ(state->lastCameraViewportWidth, 256u);
|
||||
EXPECT_EQ(state->lastCameraViewportHeight, 128u);
|
||||
ASSERT_NE(postProcessPassRaw, nullptr);
|
||||
EXPECT_TRUE(postProcessPassRaw->lastHasSourceSurface);
|
||||
EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceWidth, 256u);
|
||||
EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceHeight, 128u);
|
||||
EXPECT_EQ(postProcessPassRaw->lastSurfaceWidth, 512u);
|
||||
EXPECT_EQ(postProcessPassRaw->lastSurfaceHeight, 256u);
|
||||
ASSERT_NE(finalOutputPassRaw, nullptr);
|
||||
EXPECT_TRUE(finalOutputPassRaw->lastHasSourceSurface);
|
||||
EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceWidth, 512u);
|
||||
EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceHeight, 256u);
|
||||
EXPECT_EQ(finalOutputPassRaw->lastSurfaceWidth, 800u);
|
||||
EXPECT_EQ(finalOutputPassRaw->lastSurfaceHeight, 600u);
|
||||
EXPECT_EQ(
|
||||
state->eventLog,
|
||||
(std::vector<std::string>{
|
||||
"pipeline",
|
||||
"init:postProcess",
|
||||
"postProcess",
|
||||
"init:finalOutput",
|
||||
"finalOutput",
|
||||
"shutdown:finalOutput",
|
||||
"shutdown:postProcess" }));
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
Scene scene("CameraRendererFormalFrameStageScene");
|
||||
|
||||
@@ -716,6 +839,12 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
RenderPassSequence prePasses;
|
||||
prePasses.AddPass(std::make_unique<TrackingPass>(state, "pre"));
|
||||
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::make_unique<TrackingPass>(state, "postProcess"));
|
||||
|
||||
RenderPassSequence finalOutputPasses;
|
||||
finalOutputPasses.AddPass(std::make_unique<TrackingPass>(state, "finalOutput"));
|
||||
|
||||
RenderPassSequence postPasses;
|
||||
postPasses.AddPass(std::make_unique<TrackingPass>(state, "post"));
|
||||
|
||||
@@ -727,17 +856,26 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
request.preScenePasses = &prePasses;
|
||||
request.postProcess.passes = &postProcessPasses;
|
||||
request.finalOutput.passes = &finalOutputPasses;
|
||||
request.postScenePasses = &postPasses;
|
||||
request.overlayPasses = &overlayPasses;
|
||||
request.shadowCaster.surface = RenderSurface(128, 64);
|
||||
request.shadowCaster.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.depthOnly.surface = RenderSurface(96, 48);
|
||||
request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.postProcess.sourceSurface = RenderSurface(256, 128);
|
||||
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
|
||||
request.postProcess.destinationSurface = RenderSurface(320, 180);
|
||||
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5));
|
||||
request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
|
||||
request.finalOutput.destinationSurface = request.surface;
|
||||
request.objectId.surface = RenderSurface(320, 180);
|
||||
request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
|
||||
request.objectId.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
|
||||
request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(6));
|
||||
request.objectId.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(7));
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_EQ(
|
||||
@@ -750,6 +888,10 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
"init:depthOnly",
|
||||
"depthOnly",
|
||||
"pipeline",
|
||||
"init:postProcess",
|
||||
"postProcess",
|
||||
"init:finalOutput",
|
||||
"finalOutput",
|
||||
"objectId",
|
||||
"init:post",
|
||||
"post",
|
||||
@@ -757,6 +899,8 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
|
||||
"overlay",
|
||||
"shutdown:overlay",
|
||||
"shutdown:post",
|
||||
"shutdown:finalOutput",
|
||||
"shutdown:postProcess",
|
||||
"shutdown:pre" }));
|
||||
}
|
||||
|
||||
@@ -1056,6 +1200,33 @@ TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
|
||||
EXPECT_TRUE(state->eventLog.empty());
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, StopsRenderingWhenPostProcessRequestIsInvalid) {
|
||||
Scene scene("CameraRendererInvalidPostProcessScene");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
CameraRenderer renderer(std::make_unique<MockPipeline>(state));
|
||||
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::make_unique<TrackingPass>(state, "postProcess"));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.postProcess.passes = &postProcessPasses;
|
||||
request.postProcess.sourceSurface = RenderSurface(256, 128);
|
||||
request.postProcess.destinationSurface = RenderSurface(320, 180);
|
||||
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
|
||||
EXPECT_FALSE(renderer.Render(request));
|
||||
EXPECT_TRUE(state->eventLog.empty());
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ShutsDownInitializedPassesWhenPipelineRenderFails) {
|
||||
Scene scene("CameraRendererFailureScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user