diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 2afc62df..f160eadb 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -582,6 +582,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameGraph/State.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPassGraphContract.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPipelineStageGraphContract.cpp diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index c371eb78..74d28bd3 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -3,8 +3,7 @@ #include "Components/CameraComponent.h" #include "Rendering/Execution/DirectionalShadowExecutionState.h" #include "Rendering/Execution/Internal/CameraFrameGraph/Executor.h" -#include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" -#include "Rendering/Pipelines/ScriptableRenderPipelineHost.h" +#include "Rendering/Internal/RenderPipelineFactory.h" #include "Rendering/RenderPipelineAsset.h" #include "Rendering/RenderSurface.h" #include "Rendering/Shadow/DirectionalShadowRuntime.h" @@ -13,40 +12,8 @@ namespace XCEngine { namespace Rendering { -namespace { - -std::shared_ptr CreateDefaultPipelineAsset() { - return Pipelines::CreateManagedOrDefaultScriptableRenderPipelineAsset(); -} - -std::unique_ptr CreatePipelineFromAsset( - const std::shared_ptr& pipelineAsset) { - const std::shared_ptr resolvedAsset = - pipelineAsset != nullptr ? pipelineAsset : CreateDefaultPipelineAsset(); - if (resolvedAsset != nullptr) { - std::unique_ptr pipeline = resolvedAsset->CreatePipeline(); - if (pipeline != nullptr) { - return pipeline; - } - } - - const std::shared_ptr fallbackAsset = - CreateDefaultPipelineAsset(); - if (fallbackAsset != nullptr && - fallbackAsset != resolvedAsset) { - std::unique_ptr pipeline = fallbackAsset->CreatePipeline(); - if (pipeline != nullptr) { - return pipeline; - } - } - - return std::make_unique(); -} - -} // namespace - CameraRenderer::CameraRenderer() - : CameraRenderer(CreateDefaultPipelineAsset()) { + : CameraRenderer(Internal::CreateDefaultRenderPipelineAsset()) { } CameraRenderer::CameraRenderer(std::unique_ptr pipeline) @@ -56,9 +23,8 @@ CameraRenderer::CameraRenderer(std::unique_ptr pipeline) } CameraRenderer::CameraRenderer(std::shared_ptr pipelineAsset) - : m_pipelineAsset(std::move(pipelineAsset)) - , m_directionalShadowRuntime(std::make_unique()) { - SetPipelineAsset(m_pipelineAsset); + : m_directionalShadowRuntime(std::make_unique()) { + SetPipelineAsset(std::move(pipelineAsset)); } CameraRenderer::~CameraRenderer() { @@ -73,8 +39,10 @@ void CameraRenderer::SetPipeline(std::unique_ptr pipeline) { } void CameraRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { - m_pipelineAsset = pipelineAsset != nullptr ? std::move(pipelineAsset) : CreateDefaultPipelineAsset(); - ResetPipeline(CreatePipelineFromAsset(m_pipelineAsset)); + ResetPipeline( + Internal::CreateRenderPipelineOrDefault( + pipelineAsset, + &m_pipelineAsset)); } void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { @@ -84,8 +52,10 @@ void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { m_pipeline = std::move(pipeline); if (m_pipeline == nullptr) { - m_pipelineAsset = CreateDefaultPipelineAsset(); - m_pipeline = CreatePipelineFromAsset(m_pipelineAsset); + m_pipeline = + Internal::CreateRenderPipelineOrDefault( + nullptr, + &m_pipelineAsset); } } diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp new file mode 100644 index 00000000..9377a582 --- /dev/null +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp @@ -0,0 +1,60 @@ +#include "Rendering/Internal/RenderPipelineFactory.h" + +#include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" +#include "Rendering/Pipelines/ScriptableRenderPipelineHost.h" + +namespace XCEngine { +namespace Rendering { +namespace Internal { + +std::shared_ptr CreateDefaultRenderPipelineAsset() { + return Pipelines::CreateManagedOrDefaultScriptableRenderPipelineAsset(); +} + +std::shared_ptr ResolveRenderPipelineAssetOrDefault( + std::shared_ptr preferredAsset) { + return preferredAsset != nullptr + ? std::move(preferredAsset) + : CreateDefaultRenderPipelineAsset(); +} + +std::unique_ptr CreateRenderPipelineOrDefault( + const std::shared_ptr& preferredAsset, + std::shared_ptr* outResolvedAsset) { + const std::shared_ptr resolvedAsset = + ResolveRenderPipelineAssetOrDefault(preferredAsset); + if (resolvedAsset != nullptr) { + if (std::unique_ptr pipeline = + resolvedAsset->CreatePipeline()) { + if (outResolvedAsset != nullptr) { + *outResolvedAsset = resolvedAsset; + } + return pipeline; + } + } + + const std::shared_ptr fallbackAsset = + CreateDefaultRenderPipelineAsset(); + if (fallbackAsset != nullptr && + fallbackAsset != resolvedAsset) { + if (std::unique_ptr pipeline = + fallbackAsset->CreatePipeline()) { + if (outResolvedAsset != nullptr) { + *outResolvedAsset = fallbackAsset; + } + return pipeline; + } + } + + if (outResolvedAsset != nullptr) { + *outResolvedAsset = + fallbackAsset != nullptr + ? fallbackAsset + : resolvedAsset; + } + return std::make_unique(); +} + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.h b/engine/src/Rendering/Internal/RenderPipelineFactory.h new file mode 100644 index 00000000..b6b649f9 --- /dev/null +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Rendering { + +class RenderPipeline; +class RenderPipelineAsset; + +namespace Internal { + +std::shared_ptr CreateDefaultRenderPipelineAsset(); + +std::shared_ptr ResolveRenderPipelineAssetOrDefault( + std::shared_ptr preferredAsset); + +std::unique_ptr CreateRenderPipelineOrDefault( + const std::shared_ptr& preferredAsset, + std::shared_ptr* outResolvedAsset = nullptr); + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index d08bf133..dc3644d3 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -421,6 +421,7 @@ public: struct MockPipelineAssetState { int createCalls = 0; int configureCameraFramePlanCalls = 0; + bool createNullPipeline = false; std::shared_ptr lastCreatedPipelineState; FinalColorSettings defaultFinalColorSettings = {}; std::function configureCameraFramePlan = {}; @@ -691,6 +692,11 @@ public: std::unique_ptr CreatePipeline() const override { ++m_state->createCalls; + if (m_state->createNullPipeline) { + m_state->lastCreatedPipelineState.reset(); + return nullptr; + } + m_state->lastCreatedPipelineState = std::make_shared(); return std::make_unique(m_state->lastCreatedPipelineState); } @@ -4281,6 +4287,35 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) { Pipelines::ClearManagedRenderPipelineAssetDescriptor(); } +TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotCreatePipeline) { + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor); + + auto failingAssetState = std::make_shared(); + failingAssetState->createNullPipeline = true; + + CameraRenderer renderer(std::make_shared(failingAssetState)); + EXPECT_EQ(failingAssetState->createCalls, 1); + EXPECT_NE(renderer.GetPipeline(), nullptr); + EXPECT_NE( + dynamic_cast(renderer.GetPipeline()), + nullptr); + + auto* asset = + dynamic_cast( + renderer.GetPipelineAsset()); + ASSERT_NE(asset, nullptr); + EXPECT_EQ(asset->GetDescriptor().assemblyName, "GameScripts"); + EXPECT_EQ(asset->GetDescriptor().namespaceName, "Gameplay"); + EXPECT_EQ(asset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset"); + + Pipelines::ClearManagedRenderPipelineAssetDescriptor(); +} + TEST(SceneRenderer_Test, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) { Scene scene("SceneRendererAssetScene");