diff --git a/engine/include/XCEngine/Rendering/CameraRenderer.h b/engine/include/XCEngine/Rendering/CameraRenderer.h index 373c5d0c..e5b96ed3 100644 --- a/engine/include/XCEngine/Rendering/CameraRenderer.h +++ b/engine/include/XCEngine/Rendering/CameraRenderer.h @@ -14,20 +14,27 @@ class Scene; namespace Rendering { class RenderSurface; +class RenderPipelineAsset; class CameraRenderer { public: CameraRenderer(); explicit CameraRenderer(std::unique_ptr pipeline); + explicit CameraRenderer(std::shared_ptr pipelineAsset); ~CameraRenderer(); void SetPipeline(std::unique_ptr pipeline); + void SetPipelineAsset(std::shared_ptr pipelineAsset); RenderPipeline* GetPipeline() const { return m_pipeline.get(); } + const RenderPipelineAsset* GetPipelineAsset() const { return m_pipelineAsset.get(); } bool Render(const CameraRenderRequest& request); private: + void ResetPipeline(std::unique_ptr pipeline); + RenderSceneExtractor m_sceneExtractor; + std::shared_ptr m_pipelineAsset; std::unique_ptr m_pipeline; }; diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index 336b198f..29bdba2d 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -100,6 +101,11 @@ private: RenderPassSequence m_passSequence; }; +class BuiltinForwardPipelineAsset final : public RenderPipelineAsset { +public: + std::unique_ptr CreatePipeline() const override; +}; + } // namespace Pipelines } // namespace Rendering } // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/SceneRenderer.h b/engine/include/XCEngine/Rendering/SceneRenderer.h index a1ce1a54..ef816d6b 100644 --- a/engine/include/XCEngine/Rendering/SceneRenderer.h +++ b/engine/include/XCEngine/Rendering/SceneRenderer.h @@ -13,14 +13,19 @@ class Scene; namespace Rendering { +class RenderPipelineAsset; + class SceneRenderer { public: SceneRenderer(); explicit SceneRenderer(std::unique_ptr pipeline); + explicit SceneRenderer(std::shared_ptr pipelineAsset); ~SceneRenderer() = default; void SetPipeline(std::unique_ptr pipeline); + void SetPipelineAsset(std::shared_ptr pipelineAsset); RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); } + const RenderPipelineAsset* GetPipelineAsset() const { return m_cameraRenderer.GetPipelineAsset(); } std::vector BuildRenderRequests( const Components::Scene& scene, diff --git a/engine/src/Rendering/CameraRenderer.cpp b/engine/src/Rendering/CameraRenderer.cpp index 7b37bf6c..0822ee73 100644 --- a/engine/src/Rendering/CameraRenderer.cpp +++ b/engine/src/Rendering/CameraRenderer.cpp @@ -1,6 +1,7 @@ #include "Rendering/CameraRenderer.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" +#include "Rendering/RenderPipelineAsset.h" #include "Rendering/RenderSurface.h" #include "Scene/Scene.h" @@ -32,17 +33,38 @@ void ShutdownPassSequence(RenderPassSequence* sequence, bool initialized) { } } +std::shared_ptr CreateDefaultPipelineAsset() { + static const std::shared_ptr s_defaultPipelineAsset = + std::make_shared(); + return s_defaultPipelineAsset; +} + +std::unique_ptr CreatePipelineFromAsset( + const std::shared_ptr& pipelineAsset) { + if (pipelineAsset != nullptr) { + std::unique_ptr pipeline = pipelineAsset->CreatePipeline(); + if (pipeline != nullptr) { + return pipeline; + } + } + + return std::make_unique(); +} + } // namespace CameraRenderer::CameraRenderer() - : m_pipeline(std::make_unique()) { + : CameraRenderer(CreateDefaultPipelineAsset()) { } CameraRenderer::CameraRenderer(std::unique_ptr pipeline) - : m_pipeline(std::move(pipeline)) { - if (!m_pipeline) { - m_pipeline = std::make_unique(); - } + : m_pipelineAsset(nullptr) { + ResetPipeline(std::move(pipeline)); +} + +CameraRenderer::CameraRenderer(std::shared_ptr pipelineAsset) + : m_pipelineAsset(std::move(pipelineAsset)) { + SetPipelineAsset(m_pipelineAsset); } CameraRenderer::~CameraRenderer() { @@ -52,13 +74,24 @@ CameraRenderer::~CameraRenderer() { } void CameraRenderer::SetPipeline(std::unique_ptr pipeline) { - if (m_pipeline) { + m_pipelineAsset.reset(); + ResetPipeline(std::move(pipeline)); +} + +void CameraRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { + m_pipelineAsset = pipelineAsset != nullptr ? std::move(pipelineAsset) : CreateDefaultPipelineAsset(); + ResetPipeline(CreatePipelineFromAsset(m_pipelineAsset)); +} + +void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { + if (m_pipeline != nullptr) { m_pipeline->Shutdown(); } m_pipeline = std::move(pipeline); - if (!m_pipeline) { - m_pipeline = std::make_unique(); + if (m_pipeline == nullptr) { + m_pipelineAsset = CreateDefaultPipelineAsset(); + m_pipeline = CreatePipelineFromAsset(m_pipelineAsset); } } diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index 97e53e44..b56318d0 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -236,6 +236,10 @@ BuiltinForwardPipeline::~BuiltinForwardPipeline() { Shutdown(); } +std::unique_ptr BuiltinForwardPipelineAsset::CreatePipeline() const { + return std::make_unique(); +} + RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() { RHI::InputLayoutDesc inputLayout = {}; diff --git a/engine/src/Rendering/SceneRenderer.cpp b/engine/src/Rendering/SceneRenderer.cpp index 032b34f9..3d7cd0b8 100644 --- a/engine/src/Rendering/SceneRenderer.cpp +++ b/engine/src/Rendering/SceneRenderer.cpp @@ -25,10 +25,18 @@ SceneRenderer::SceneRenderer(std::unique_ptr pipeline) : m_cameraRenderer(std::move(pipeline)) { } +SceneRenderer::SceneRenderer(std::shared_ptr pipelineAsset) + : m_cameraRenderer(std::move(pipelineAsset)) { +} + void SceneRenderer::SetPipeline(std::unique_ptr pipeline) { m_cameraRenderer.SetPipeline(std::move(pipeline)); } +void SceneRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { + m_cameraRenderer.SetPipelineAsset(std::move(pipelineAsset)); +} + std::vector SceneRenderer::BuildRenderRequests( const Components::Scene& scene, Components::CameraComponent* overrideCamera, diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 142246ab..52f84571 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,11 @@ struct MockPipelineState { std::vector eventLog; }; +struct MockPipelineAssetState { + int createCalls = 0; + std::shared_ptr lastCreatedPipelineState; +}; + class MockPipeline final : public RenderPipeline { public: explicit MockPipeline(std::shared_ptr state) @@ -65,6 +71,22 @@ private: std::shared_ptr m_state; }; +class MockPipelineAsset final : public RenderPipelineAsset { +public: + explicit MockPipelineAsset(std::shared_ptr state) + : m_state(std::move(state)) { + } + + std::unique_ptr CreatePipeline() const override { + ++m_state->createCalls; + m_state->lastCreatedPipelineState = std::make_shared(); + return std::make_unique(m_state->lastCreatedPipelineState); + } + +private: + std::shared_ptr m_state; +}; + class TrackingPass final : public RenderPass { public: TrackingPass( @@ -321,6 +343,44 @@ TEST(SceneRenderer_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer) EXPECT_EQ(replacementState->shutdownCalls, 1); } +TEST(SceneRenderer_Test, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) { + Scene scene("SceneRendererAssetScene"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(2.0f); + + auto initialAssetState = std::make_shared(); + auto replacementAssetState = std::make_shared(); + + { + SceneRenderer renderer(std::make_shared(initialAssetState)); + ASSERT_NE(renderer.GetPipeline(), nullptr); + ASSERT_NE(renderer.GetPipelineAsset(), nullptr); + EXPECT_EQ(initialAssetState->createCalls, 1); + + const RenderSurface surface(800, 600); + ASSERT_TRUE(renderer.Render(scene, nullptr, CreateValidContext(), surface)); + ASSERT_NE(initialAssetState->lastCreatedPipelineState, nullptr); + EXPECT_EQ(initialAssetState->lastCreatedPipelineState->renderCalls, 1); + EXPECT_EQ(initialAssetState->lastCreatedPipelineState->lastCamera, camera); + + renderer.SetPipelineAsset(std::make_shared(replacementAssetState)); + ASSERT_NE(initialAssetState->lastCreatedPipelineState, nullptr); + EXPECT_EQ(initialAssetState->lastCreatedPipelineState->shutdownCalls, 1); + EXPECT_EQ(replacementAssetState->createCalls, 1); + + ASSERT_TRUE(renderer.Render(scene, nullptr, CreateValidContext(), surface)); + ASSERT_NE(replacementAssetState->lastCreatedPipelineState, nullptr); + EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->renderCalls, 1); + EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->lastCamera, camera); + } + + ASSERT_NE(replacementAssetState->lastCreatedPipelineState, nullptr); + EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->shutdownCalls, 1); +} + TEST(SceneRenderer_Test, SortsManualCameraRequestsByDepthBeforeRendering) { Scene scene("SceneRendererManualRequests");