Add rendering pass sequence scaffolding
This commit is contained in:
@@ -334,6 +334,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderCameraData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/CameraRenderRequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/VisibleRenderObject.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderSceneExtractor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipeline.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipelineAsset.h
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderCameraData.h>
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
@@ -19,6 +20,8 @@ struct CameraRenderRequest {
|
||||
RenderSurface surface;
|
||||
float cameraDepth = 0.0f;
|
||||
RenderClearFlags clearFlags = RenderClearFlags::All;
|
||||
RenderPassSequence* preScenePasses = nullptr;
|
||||
RenderPassSequence* postScenePasses = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
return scene != nullptr &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderMaterialUtility.h>
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
||||
#include <XCEngine/Rendering/RenderResourceCache.h>
|
||||
|
||||
@@ -28,12 +29,17 @@ namespace Rendering {
|
||||
class RenderSurface;
|
||||
|
||||
namespace Pipelines {
|
||||
namespace Detail {
|
||||
class BuiltinForwardOpaquePass;
|
||||
} // namespace Detail
|
||||
|
||||
class BuiltinForwardPipeline : public RenderPipeline {
|
||||
public:
|
||||
BuiltinForwardPipeline() = default;
|
||||
BuiltinForwardPipeline();
|
||||
~BuiltinForwardPipeline() override;
|
||||
|
||||
static RHI::InputLayoutDesc BuildInputLayout();
|
||||
|
||||
bool Initialize(const RenderContext& context) override;
|
||||
void Shutdown() override;
|
||||
bool Render(
|
||||
@@ -42,6 +48,8 @@ public:
|
||||
const RenderSceneData& sceneData) override;
|
||||
|
||||
private:
|
||||
friend class Detail::BuiltinForwardOpaquePass;
|
||||
|
||||
struct OwnedDescriptorSet {
|
||||
RHI::RHIDescriptorPool* pool = nullptr;
|
||||
RHI::RHIDescriptorSet* set = nullptr;
|
||||
@@ -65,6 +73,7 @@ private:
|
||||
|
||||
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
||||
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
||||
bool ExecuteForwardOpaquePass(const RenderPassContext& context);
|
||||
bool DrawVisibleItem(
|
||||
const RenderContext& context,
|
||||
const RenderSceneData& sceneData,
|
||||
@@ -85,6 +94,7 @@ private:
|
||||
RHI::RHISampler* m_sampler = nullptr;
|
||||
RHI::RHITexture* m_fallbackTexture = nullptr;
|
||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||
RenderPassSequence m_passSequence;
|
||||
};
|
||||
|
||||
} // namespace Pipelines
|
||||
|
||||
90
engine/include/XCEngine/Rendering/RenderPass.h
Normal file
90
engine/include/XCEngine/Rendering/RenderPass.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
struct RenderSceneData;
|
||||
class RenderSurface;
|
||||
|
||||
struct RenderPassContext {
|
||||
const RenderContext& renderContext;
|
||||
const RenderSurface& surface;
|
||||
const RenderSceneData& sceneData;
|
||||
};
|
||||
|
||||
class RenderPass {
|
||||
public:
|
||||
virtual ~RenderPass() = default;
|
||||
|
||||
virtual const char* GetName() const = 0;
|
||||
|
||||
virtual bool Initialize(const RenderContext&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Shutdown() {
|
||||
}
|
||||
|
||||
virtual bool Execute(const RenderPassContext& context) = 0;
|
||||
};
|
||||
|
||||
class RenderPassSequence {
|
||||
public:
|
||||
void AddPass(std::unique_ptr<RenderPass> pass) {
|
||||
if (pass != nullptr) {
|
||||
m_passes.push_back(std::move(pass));
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetPassCount() const {
|
||||
return m_passes.size();
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext& context) {
|
||||
for (const std::unique_ptr<RenderPass>& pass : m_passes) {
|
||||
if (pass == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pass->Initialize(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
for (auto passIt = m_passes.rbegin(); passIt != m_passes.rend(); ++passIt) {
|
||||
if (*passIt != nullptr) {
|
||||
(*passIt)->Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext& context) {
|
||||
for (const std::unique_ptr<RenderPass>& pass : m_passes) {
|
||||
if (pass == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pass->Execute(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<RenderPass>> m_passes;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -51,7 +51,31 @@ bool CameraRenderer::Render(
|
||||
}
|
||||
|
||||
sceneData.cameraData.clearFlags = request.clearFlags;
|
||||
return m_pipeline->Render(request.context, request.surface, sceneData);
|
||||
const RenderPassContext passContext = {
|
||||
request.context,
|
||||
request.surface,
|
||||
sceneData
|
||||
};
|
||||
|
||||
if (request.preScenePasses != nullptr) {
|
||||
if (!request.preScenePasses->Initialize(request.context) ||
|
||||
!request.preScenePasses->Execute(passContext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_pipeline->Render(request.context, request.surface, sceneData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.postScenePasses != nullptr) {
|
||||
if (!request.postScenePasses->Initialize(request.context) ||
|
||||
!request.postScenePasses->Execute(passContext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -16,6 +16,35 @@
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
namespace Pipelines {
|
||||
namespace Detail {
|
||||
|
||||
class BuiltinForwardOpaquePass final : public RenderPass {
|
||||
public:
|
||||
explicit BuiltinForwardOpaquePass(BuiltinForwardPipeline& pipeline)
|
||||
: m_pipeline(pipeline) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return "BuiltinForwardOpaquePass";
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext& context) override {
|
||||
return m_pipeline.EnsureInitialized(context);
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
m_pipeline.DestroyPipelineResources();
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext& context) override {
|
||||
return m_pipeline.ExecuteForwardOpaquePass(context);
|
||||
}
|
||||
|
||||
private:
|
||||
BuiltinForwardPipeline& m_pipeline;
|
||||
};
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -101,21 +130,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
pipelineDesc.sampleCount = 1;
|
||||
ApplyMaterialRenderState(material, pipelineDesc);
|
||||
|
||||
RHI::InputElementDesc position = {};
|
||||
position.semanticName = "POSITION";
|
||||
position.semanticIndex = 0;
|
||||
position.format = static_cast<uint32_t>(RHI::Format::R32G32B32A32_Float);
|
||||
position.inputSlot = 0;
|
||||
position.alignedByteOffset = 0;
|
||||
pipelineDesc.inputLayout.elements.push_back(position);
|
||||
|
||||
RHI::InputElementDesc texcoord = {};
|
||||
texcoord.semanticName = "TEXCOORD";
|
||||
texcoord.semanticIndex = 0;
|
||||
texcoord.format = static_cast<uint32_t>(RHI::Format::R32G32_Float);
|
||||
texcoord.inputSlot = 0;
|
||||
texcoord.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, uv0));
|
||||
pipelineDesc.inputLayout.elements.push_back(texcoord);
|
||||
pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout();
|
||||
|
||||
if (backendType == RHI::RHIType::D3D12) {
|
||||
pipelineDesc.vertexShader.source.assign(
|
||||
@@ -170,26 +185,66 @@ const Resources::Texture* FindMaterialTexture(const Resources::Material& materia
|
||||
|
||||
} // namespace
|
||||
|
||||
BuiltinForwardPipeline::BuiltinForwardPipeline() {
|
||||
m_passSequence.AddPass(std::make_unique<Detail::BuiltinForwardOpaquePass>(*this));
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::~BuiltinForwardPipeline() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() {
|
||||
RHI::InputLayoutDesc inputLayout = {};
|
||||
|
||||
RHI::InputElementDesc position = {};
|
||||
position.semanticName = "POSITION";
|
||||
position.semanticIndex = 0;
|
||||
position.format = static_cast<uint32_t>(RHI::Format::R32G32B32_Float);
|
||||
position.inputSlot = 0;
|
||||
position.alignedByteOffset = 0;
|
||||
inputLayout.elements.push_back(position);
|
||||
|
||||
RHI::InputElementDesc texcoord = {};
|
||||
texcoord.semanticName = "TEXCOORD";
|
||||
texcoord.semanticIndex = 0;
|
||||
texcoord.format = static_cast<uint32_t>(RHI::Format::R32G32_Float);
|
||||
texcoord.inputSlot = 0;
|
||||
texcoord.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, uv0));
|
||||
inputLayout.elements.push_back(texcoord);
|
||||
|
||||
return inputLayout;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::Initialize(const RenderContext& context) {
|
||||
return EnsureInitialized(context);
|
||||
return m_passSequence.Initialize(context);
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::Shutdown() {
|
||||
DestroyPipelineResources();
|
||||
m_passSequence.Shutdown();
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::Render(
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface,
|
||||
const RenderSceneData& sceneData) {
|
||||
if (!EnsureInitialized(context)) {
|
||||
if (!Initialize(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RenderPassContext passContext = {
|
||||
context,
|
||||
surface,
|
||||
sceneData
|
||||
};
|
||||
|
||||
return m_passSequence.Execute(passContext);
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& passContext) {
|
||||
const RenderContext& context = passContext.renderContext;
|
||||
const RenderSurface& surface = passContext.surface;
|
||||
const RenderSceneData& sceneData = passContext.sceneData;
|
||||
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
if (colorAttachments.empty()) {
|
||||
return false;
|
||||
|
||||
@@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.15)
|
||||
project(XCEngine_RenderingUnitTests)
|
||||
|
||||
set(RENDERING_UNIT_TEST_SOURCES
|
||||
test_render_pass.cpp
|
||||
test_builtin_forward_pipeline.cpp
|
||||
test_camera_scene_renderer.cpp
|
||||
test_render_scene_extractor.cpp
|
||||
)
|
||||
|
||||
29
tests/Rendering/unit/test_builtin_forward_pipeline.cpp
Normal file
29
tests/Rendering/unit/test_builtin_forward_pipeline.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
|
||||
using namespace XCEngine::Rendering::Pipelines;
|
||||
using namespace XCEngine::Resources;
|
||||
using namespace XCEngine::RHI;
|
||||
|
||||
TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
||||
const InputLayoutDesc inputLayout = BuiltinForwardPipeline::BuildInputLayout();
|
||||
|
||||
ASSERT_EQ(inputLayout.elements.size(), 2u);
|
||||
|
||||
const InputElementDesc& position = inputLayout.elements[0];
|
||||
EXPECT_EQ(position.semanticName, "POSITION");
|
||||
EXPECT_EQ(position.semanticIndex, 0u);
|
||||
EXPECT_EQ(position.format, static_cast<uint32_t>(Format::R32G32B32_Float));
|
||||
EXPECT_EQ(position.inputSlot, 0u);
|
||||
EXPECT_EQ(position.alignedByteOffset, 0u);
|
||||
|
||||
const InputElementDesc& texcoord = inputLayout.elements[1];
|
||||
EXPECT_EQ(texcoord.semanticName, "TEXCOORD");
|
||||
EXPECT_EQ(texcoord.semanticIndex, 0u);
|
||||
EXPECT_EQ(texcoord.format, static_cast<uint32_t>(Format::R32G32_Float));
|
||||
EXPECT_EQ(texcoord.inputSlot, 0u);
|
||||
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Components;
|
||||
@@ -25,6 +26,7 @@ struct MockPipelineState {
|
||||
RenderClearFlags lastClearFlags = RenderClearFlags::All;
|
||||
std::vector<CameraComponent*> renderedCameras;
|
||||
std::vector<RenderClearFlags> renderedClearFlags;
|
||||
std::vector<std::string> eventLog;
|
||||
};
|
||||
|
||||
class MockPipeline final : public RenderPipeline {
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
const RenderContext&,
|
||||
const RenderSurface& surface,
|
||||
const RenderSceneData& sceneData) override {
|
||||
m_state->eventLog.push_back("pipeline");
|
||||
++m_state->renderCalls;
|
||||
m_state->lastSurfaceWidth = surface.GetWidth();
|
||||
m_state->lastSurfaceHeight = surface.GetHeight();
|
||||
@@ -61,6 +64,32 @@ private:
|
||||
std::shared_ptr<MockPipelineState> m_state;
|
||||
};
|
||||
|
||||
class TrackingPass final : public RenderPass {
|
||||
public:
|
||||
TrackingPass(std::shared_ptr<MockPipelineState> state, const char* label)
|
||||
: m_state(std::move(state))
|
||||
, m_label(label) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return m_label;
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext&) override {
|
||||
m_state->eventLog.push_back(std::string("init:") + m_label);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext&) override {
|
||||
m_state->eventLog.push_back(m_label);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockPipelineState> m_state;
|
||||
const char* m_label = "";
|
||||
};
|
||||
|
||||
RenderContext CreateValidContext() {
|
||||
RenderContext context;
|
||||
context.device = reinterpret_cast<XCEngine::RHI::RHIDevice*>(1);
|
||||
@@ -105,6 +134,38 @@ TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
|
||||
EXPECT_EQ(state->lastClearFlags, RenderClearFlags::None);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ExecutesInjectedPreAndPostPassSequencesAroundPipelineRender) {
|
||||
Scene scene("CameraRendererPassScene");
|
||||
|
||||
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));
|
||||
|
||||
RenderPassSequence prePasses;
|
||||
prePasses.AddPass(std::make_unique<TrackingPass>(state, "pre"));
|
||||
|
||||
RenderPassSequence postPasses;
|
||||
postPasses.AddPass(std::make_unique<TrackingPass>(state, "post"));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
request.preScenePasses = &prePasses;
|
||||
request.postScenePasses = &postPasses;
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_EQ(
|
||||
state->eventLog,
|
||||
(std::vector<std::string>{ "init:pre", "pre", "pipeline", "init:post", "post" }));
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, BuildsSingleExplicitRequestFromSelectedCamera) {
|
||||
Scene scene("SceneRendererRequestScene");
|
||||
|
||||
|
||||
125
tests/Rendering/unit/test_render_pass.cpp
Normal file
125
tests/Rendering/unit/test_render_pass.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderSceneExtractor.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Rendering;
|
||||
|
||||
namespace {
|
||||
|
||||
struct TrackingPassState {
|
||||
std::vector<std::string> initializeOrder;
|
||||
std::vector<std::string> executeOrder;
|
||||
std::vector<std::string> shutdownOrder;
|
||||
};
|
||||
|
||||
class TrackingPass final : public RenderPass {
|
||||
public:
|
||||
TrackingPass(
|
||||
std::string name,
|
||||
TrackingPassState& state,
|
||||
bool initializeResult = true,
|
||||
bool executeResult = true)
|
||||
: m_name(std::move(name))
|
||||
, m_state(state)
|
||||
, m_initializeResult(initializeResult)
|
||||
, m_executeResult(executeResult) {
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return m_name.c_str();
|
||||
}
|
||||
|
||||
bool Initialize(const RenderContext&) override {
|
||||
m_state.initializeOrder.push_back(m_name);
|
||||
return m_initializeResult;
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
m_state.shutdownOrder.push_back(m_name);
|
||||
}
|
||||
|
||||
bool Execute(const RenderPassContext&) override {
|
||||
m_state.executeOrder.push_back(m_name);
|
||||
return m_executeResult;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
TrackingPassState& m_state;
|
||||
bool m_initializeResult = true;
|
||||
bool m_executeResult = true;
|
||||
};
|
||||
|
||||
RenderContext CreateValidContext() {
|
||||
RenderContext context;
|
||||
context.device = reinterpret_cast<XCEngine::RHI::RHIDevice*>(1);
|
||||
context.commandList = reinterpret_cast<XCEngine::RHI::RHICommandList*>(1);
|
||||
context.commandQueue = reinterpret_cast<XCEngine::RHI::RHICommandQueue*>(1);
|
||||
return context;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(RenderPassSequence_Test, InitializesAndExecutesInInsertionOrderThenShutsDownInReverse) {
|
||||
TrackingPassState state;
|
||||
RenderPassSequence sequence;
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("SceneColor", state));
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("GridOverlay", state));
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("Outline", state));
|
||||
|
||||
ASSERT_EQ(sequence.GetPassCount(), 3u);
|
||||
|
||||
const RenderContext context = CreateValidContext();
|
||||
ASSERT_TRUE(sequence.Initialize(context));
|
||||
|
||||
RenderSceneData sceneData;
|
||||
const RenderSurface surface(1280, 720);
|
||||
const RenderPassContext passContext = {
|
||||
context,
|
||||
surface,
|
||||
sceneData
|
||||
};
|
||||
|
||||
ASSERT_TRUE(sequence.Execute(passContext));
|
||||
sequence.Shutdown();
|
||||
|
||||
EXPECT_EQ(
|
||||
state.initializeOrder,
|
||||
(std::vector<std::string>{ "SceneColor", "GridOverlay", "Outline" }));
|
||||
EXPECT_EQ(
|
||||
state.executeOrder,
|
||||
(std::vector<std::string>{ "SceneColor", "GridOverlay", "Outline" }));
|
||||
EXPECT_EQ(
|
||||
state.shutdownOrder,
|
||||
(std::vector<std::string>{ "Outline", "GridOverlay", "SceneColor" }));
|
||||
}
|
||||
|
||||
TEST(RenderPassSequence_Test, StopsExecutingWhenAPassFails) {
|
||||
TrackingPassState state;
|
||||
RenderPassSequence sequence;
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("SceneColor", state));
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("SelectionMask", state, true, false));
|
||||
sequence.AddPass(std::make_unique<TrackingPass>("Outline", state));
|
||||
|
||||
const RenderContext context = CreateValidContext();
|
||||
ASSERT_TRUE(sequence.Initialize(context));
|
||||
|
||||
RenderSceneData sceneData;
|
||||
const RenderSurface surface(640, 360);
|
||||
const RenderPassContext passContext = {
|
||||
context,
|
||||
surface,
|
||||
sceneData
|
||||
};
|
||||
|
||||
EXPECT_FALSE(sequence.Execute(passContext));
|
||||
EXPECT_EQ(
|
||||
state.executeOrder,
|
||||
(std::vector<std::string>{ "SceneColor", "SelectionMask" }));
|
||||
}
|
||||
Reference in New Issue
Block a user