From df02a4e741670d1fed910faa0b554e7c36e98b1f Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 18 Apr 2026 01:10:45 +0800 Subject: [PATCH] fix(rendering): refresh managed pipelines on srp environment changes --- .../Rendering/Execution/CameraRenderer.h | 9 ++- .../ManagedScriptableRenderPipelineAsset.h | 1 + .../Rendering/Execution/CameraRenderer.cpp | 55 +++++++++++++++- .../ManagedScriptableRenderPipelineAsset.cpp | 13 ++++ .../unit/test_camera_scene_renderer.cpp | 65 +++++++++++++++++++ 5 files changed, 140 insertions(+), 3 deletions(-) diff --git a/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h b/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h index 255fa019..2e37a3a3 100644 --- a/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h +++ b/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace XCEngine { @@ -33,12 +34,14 @@ public: 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(); } + RenderPipeline* GetPipeline() const; + const RenderPipelineAsset* GetPipelineAsset() const; bool Render(const CameraFramePlan& plan); private: + bool UsesManagedPipelineBinding() const; + void RefreshManagedPipelineBindingIfNeeded(); void ResetPipeline(std::unique_ptr pipeline); bool BuildSceneDataForPlan( const CameraFramePlan& plan, @@ -53,6 +56,8 @@ private: std::shared_ptr m_pipelineAsset; std::unique_ptr m_pipeline; std::unique_ptr m_directionalShadowRuntime; + bool m_usesDefaultPipelineAssetSelection = false; + size_t m_managedPipelineEnvironmentGeneration = 0u; }; } // namespace Rendering diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 689d741b..0d0baab5 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -96,6 +96,7 @@ void SetManagedRenderPipelineBridge( void ClearManagedRenderPipelineBridge(); std::shared_ptr GetManagedRenderPipelineBridge(); +size_t GetManagedRenderPipelineEnvironmentGeneration(); void SetManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor); diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index d7d79d06..b71618b4 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -4,6 +4,7 @@ #include "Rendering/Execution/DirectionalShadowExecutionState.h" #include "Rendering/Execution/Internal/CameraFrameGraph/Executor.h" #include "Rendering/Internal/RenderPipelineFactory.h" +#include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Rendering/RenderPipelineAsset.h" #include "Rendering/RenderSurface.h" #include "Rendering/Shadow/DirectionalShadowRuntime.h" @@ -21,10 +22,17 @@ bool RequiresPipelineStageRecording( plan.GetPassSequence(stage) == nullptr; } +bool IsManagedPipelineAsset( + const std::shared_ptr& pipelineAsset) { + return dynamic_cast( + pipelineAsset.get()) != nullptr; +} + } // namespace CameraRenderer::CameraRenderer() - : CameraRenderer(Internal::CreateDefaultRenderPipelineAsset()) { + : m_directionalShadowRuntime(std::make_unique()) { + SetPipelineAsset(nullptr); } CameraRenderer::CameraRenderer(std::unique_ptr pipeline) @@ -46,16 +54,53 @@ CameraRenderer::~CameraRenderer() { void CameraRenderer::SetPipeline(std::unique_ptr pipeline) { m_pipelineAsset.reset(); + m_usesDefaultPipelineAssetSelection = false; + m_managedPipelineEnvironmentGeneration = 0u; ResetPipeline(std::move(pipeline)); } void CameraRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { + m_usesDefaultPipelineAssetSelection = pipelineAsset == nullptr; ResetPipeline( Internal::CreateRenderPipelineOrDefault( pipelineAsset, &m_pipelineAsset)); } +RenderPipeline* CameraRenderer::GetPipeline() const { + const_cast(this)->RefreshManagedPipelineBindingIfNeeded(); + return m_pipeline.get(); +} + +const RenderPipelineAsset* CameraRenderer::GetPipelineAsset() const { + const_cast(this)->RefreshManagedPipelineBindingIfNeeded(); + return m_pipelineAsset.get(); +} + +bool CameraRenderer::UsesManagedPipelineBinding() const { + return m_usesDefaultPipelineAssetSelection || + IsManagedPipelineAsset(m_pipelineAsset); +} + +void CameraRenderer::RefreshManagedPipelineBindingIfNeeded() { + if (!UsesManagedPipelineBinding()) { + return; + } + + const size_t currentGeneration = + Pipelines::GetManagedRenderPipelineEnvironmentGeneration(); + if (currentGeneration == m_managedPipelineEnvironmentGeneration) { + return; + } + + if (m_usesDefaultPipelineAssetSelection) { + SetPipelineAsset(nullptr); + return; + } + + SetPipelineAsset(m_pipelineAsset); +} + void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { if (m_pipeline != nullptr) { m_pipeline->Shutdown(); @@ -63,11 +108,17 @@ void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { m_pipeline = std::move(pipeline); if (m_pipeline == nullptr) { + m_usesDefaultPipelineAssetSelection = true; m_pipeline = Internal::CreateRenderPipelineOrDefault( nullptr, &m_pipelineAsset); } + + m_managedPipelineEnvironmentGeneration = + UsesManagedPipelineBinding() + ? Pipelines::GetManagedRenderPipelineEnvironmentGeneration() + : 0u; } bool CameraRenderer::BuildSceneDataForPlan( @@ -109,6 +160,8 @@ bool CameraRenderer::ExecuteRenderPlan( bool CameraRenderer::Render( const CameraFramePlan& plan) { + RefreshManagedPipelineBindingIfNeeded(); + if (!plan.IsValid() || m_pipeline == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 9f6a4e8a..79d674c8 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -23,6 +23,11 @@ size_t& GetManagedRenderPipelineBridgeGenerationStorage() { return s_generation; } +size_t& GetManagedRenderPipelineEnvironmentGenerationStorage() { + static size_t s_generation = 1u; + return s_generation; +} + } // namespace ManagedScriptableRenderPipelineAsset::ManagedScriptableRenderPipelineAsset( @@ -109,11 +114,13 @@ void SetManagedRenderPipelineBridge( std::shared_ptr bridge) { GetManagedRenderPipelineBridgeStorage() = std::move(bridge); ++GetManagedRenderPipelineBridgeGenerationStorage(); + ++GetManagedRenderPipelineEnvironmentGenerationStorage(); } void ClearManagedRenderPipelineBridge() { GetManagedRenderPipelineBridgeStorage().reset(); ++GetManagedRenderPipelineBridgeGenerationStorage(); + ++GetManagedRenderPipelineEnvironmentGenerationStorage(); } std::shared_ptr @@ -121,13 +128,19 @@ GetManagedRenderPipelineBridge() { return GetManagedRenderPipelineBridgeStorage(); } +size_t GetManagedRenderPipelineEnvironmentGeneration() { + return GetManagedRenderPipelineEnvironmentGenerationStorage(); +} + void SetManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor) { GetManagedRenderPipelineAssetDescriptorStorage() = descriptor; + ++GetManagedRenderPipelineEnvironmentGenerationStorage(); } void ClearManagedRenderPipelineAssetDescriptor() { GetManagedRenderPipelineAssetDescriptorStorage() = {}; + ++GetManagedRenderPipelineEnvironmentGenerationStorage(); } ManagedRenderPipelineAssetDescriptor GetManagedRenderPipelineAssetDescriptor() { diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index c03b1629..e3e6a281 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -4912,6 +4912,37 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) { Pipelines::ClearManagedRenderPipelineAssetDescriptor(); } +TEST(CameraRenderer_Test, DefaultPipelineAssetRefreshesWhenManagedSelectionChanges) { + Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + + CameraRenderer renderer; + EXPECT_EQ( + dynamic_cast( + renderer.GetPipelineAsset()), + nullptr); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor); + + auto* managedAsset = + dynamic_cast( + renderer.GetPipelineAsset()); + ASSERT_NE(managedAsset, nullptr); + EXPECT_EQ(managedAsset->GetDescriptor().assemblyName, "GameScripts"); + EXPECT_EQ(managedAsset->GetDescriptor().namespaceName, "Gameplay"); + EXPECT_EQ(managedAsset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset"); + + Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + EXPECT_EQ( + dynamic_cast( + renderer.GetPipelineAsset()), + nullptr); +} + TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotCreatePipeline) { const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { "GameScripts", @@ -4941,6 +4972,40 @@ TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotC Pipelines::ClearManagedRenderPipelineAssetDescriptor(); } +TEST(CameraRenderer_Test, RecreatesManagedPipelineWhenBridgeChanges) { + Pipelines::ClearManagedRenderPipelineBridge(); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + + auto firstBridgeState = std::make_shared(); + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(firstBridgeState)); + + CameraRenderer renderer( + std::make_shared( + descriptor)); + ASSERT_NE( + dynamic_cast(renderer.GetPipeline()), + nullptr); + EXPECT_EQ(firstBridgeState->createAssetRuntimeCalls, 1); + + auto secondBridgeState = std::make_shared(); + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(secondBridgeState)); + + auto* host = + dynamic_cast(renderer.GetPipeline()); + ASSERT_NE(host, nullptr); + EXPECT_NE(host->GetStageRecorder(), nullptr); + EXPECT_EQ(secondBridgeState->createAssetRuntimeCalls, 1); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST(SceneRenderer_Test, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) { Scene scene("SceneRendererAssetScene");