From cafe3c807697ae66980ebf0167df3becf0cfc936 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 15 Apr 2026 20:07:52 +0800 Subject: [PATCH] refactor(rendering): add srp host stage recorder bridge --- .../ManagedScriptableRenderPipelineAsset.h | 16 ++ .../Pipelines/ScriptableRenderPipelineHost.h | 6 + .../XCEngine/Rendering/RenderPipeline.h | 17 ++ .../ManagedScriptableRenderPipelineAsset.cpp | 34 +++- .../ScriptableRenderPipelineHost.cpp | 43 ++++- .../unit/test_camera_scene_renderer.cpp | 172 ++++++++++++++++++ 6 files changed, 283 insertions(+), 5 deletions(-) diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 4dd9f7e7..882c3aef 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -42,6 +42,22 @@ private: ScriptableRenderPipelineHostAsset m_fallbackAsset; }; +class ManagedRenderPipelineBridge { +public: + virtual ~ManagedRenderPipelineBridge() = default; + + virtual std::unique_ptr CreateStageRecorder( + const ManagedRenderPipelineAssetDescriptor&) const { + return nullptr; + } +}; + +void SetManagedRenderPipelineBridge( + std::shared_ptr bridge); +void ClearManagedRenderPipelineBridge(); +std::shared_ptr +GetManagedRenderPipelineBridge(); + void SetManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor); void ClearManagedRenderPipelineAssetDescriptor(); diff --git a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h index 5cbbf3b7..cb5713dc 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h @@ -20,9 +20,13 @@ public: using RenderPipeline::Render; + void SetStageRecorder(std::unique_ptr stageRecorder); void SetPipelineRenderer(std::unique_ptr pipelineRenderer); void SetPipelineRendererAsset( std::shared_ptr pipelineRendererAsset); + RenderPipelineStageRecorder* GetStageRecorder() const { + return m_stageRecorder.get(); + } RenderPipelineRenderer* GetPipelineRenderer() const { return m_pipelineRenderer.get(); } @@ -42,8 +46,10 @@ public: const RenderSceneData& sceneData) override; private: + void ResetStageRecorder(std::unique_ptr stageRecorder); void ResetPipelineRenderer(std::unique_ptr pipelineRenderer); + std::unique_ptr m_stageRecorder; std::shared_ptr m_pipelineRendererAsset; std::unique_ptr m_pipelineRenderer; }; diff --git a/engine/include/XCEngine/Rendering/RenderPipeline.h b/engine/include/XCEngine/Rendering/RenderPipeline.h index 44e93748..202ebd0c 100644 --- a/engine/include/XCEngine/Rendering/RenderPipeline.h +++ b/engine/include/XCEngine/Rendering/RenderPipeline.h @@ -36,6 +36,23 @@ struct RenderPipelineStageRenderGraphContext { RenderGraphBlackboard* blackboard = nullptr; }; +class RenderPipelineStageRecorder { +public: + virtual ~RenderPipelineStageRecorder() = default; + + virtual bool Initialize(const RenderContext&) { + return true; + } + virtual void Shutdown() {} + virtual bool SupportsStageRenderGraph(CameraFrameStage) const { + return false; + } + virtual bool RecordStageRenderGraph( + const RenderPipelineStageRenderGraphContext&) { + return false; + } +}; + class RenderPipelineRenderer { public: virtual ~RenderPipelineRenderer() = default; diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 35a1e330..b690e284 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -13,6 +13,11 @@ ManagedRenderPipelineAssetDescriptor& GetManagedRenderPipelineAssetDescriptorSto return s_descriptor; } +std::shared_ptr& GetManagedRenderPipelineBridgeStorage() { + static std::shared_ptr s_bridge = nullptr; + return s_bridge; +} + } // namespace ManagedScriptableRenderPipelineAsset::ManagedScriptableRenderPipelineAsset( @@ -21,13 +26,40 @@ ManagedScriptableRenderPipelineAsset::ManagedScriptableRenderPipelineAsset( } std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipeline() const { - return m_fallbackAsset.CreatePipeline(); + std::unique_ptr pipeline = m_fallbackAsset.CreatePipeline(); + auto* host = dynamic_cast(pipeline.get()); + if (host == nullptr) { + return pipeline; + } + + const std::shared_ptr bridge = + GetManagedRenderPipelineBridgeStorage(); + if (bridge != nullptr) { + host->SetStageRecorder( + bridge->CreateStageRecorder(m_descriptor)); + } + + return pipeline; } FinalColorSettings ManagedScriptableRenderPipelineAsset::GetDefaultFinalColorSettings() const { return m_fallbackAsset.GetDefaultFinalColorSettings(); } +void SetManagedRenderPipelineBridge( + std::shared_ptr bridge) { + GetManagedRenderPipelineBridgeStorage() = std::move(bridge); +} + +void ClearManagedRenderPipelineBridge() { + GetManagedRenderPipelineBridgeStorage().reset(); +} + +std::shared_ptr +GetManagedRenderPipelineBridge() { + return GetManagedRenderPipelineBridgeStorage(); +} + void SetManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor) { GetManagedRenderPipelineAssetDescriptorStorage() = descriptor; diff --git a/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp b/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp index 52767ac3..987fa506 100644 --- a/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp +++ b/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp @@ -71,6 +71,11 @@ ScriptableRenderPipelineHost::~ScriptableRenderPipelineHost() { Shutdown(); } +void ScriptableRenderPipelineHost::SetStageRecorder( + std::unique_ptr stageRecorder) { + ResetStageRecorder(std::move(stageRecorder)); +} + void ScriptableRenderPipelineHost::SetPipelineRenderer( std::unique_ptr pipelineRenderer) { m_pipelineRendererAsset.reset(); @@ -88,11 +93,25 @@ void ScriptableRenderPipelineHost::SetPipelineRendererAsset( } bool ScriptableRenderPipelineHost::Initialize(const RenderContext& context) { - return m_pipelineRenderer != nullptr && - m_pipelineRenderer->Initialize(context); + if (m_pipelineRenderer == nullptr || + !m_pipelineRenderer->Initialize(context)) { + return false; + } + + if (m_stageRecorder != nullptr && + !m_stageRecorder->Initialize(context)) { + m_stageRecorder->Shutdown(); + m_pipelineRenderer->Shutdown(); + return false; + } + + return true; } void ScriptableRenderPipelineHost::Shutdown() { + if (m_stageRecorder != nullptr) { + m_stageRecorder->Shutdown(); + } if (m_pipelineRenderer != nullptr) { m_pipelineRenderer->Shutdown(); } @@ -101,12 +120,19 @@ void ScriptableRenderPipelineHost::Shutdown() { bool ScriptableRenderPipelineHost::SupportsStageRenderGraph( CameraFrameStage stage) const { - return m_pipelineRenderer != nullptr && - m_pipelineRenderer->SupportsStageRenderGraph(stage); + return (m_stageRecorder != nullptr && + m_stageRecorder->SupportsStageRenderGraph(stage)) || + (m_pipelineRenderer != nullptr && + m_pipelineRenderer->SupportsStageRenderGraph(stage)); } bool ScriptableRenderPipelineHost::RecordStageRenderGraph( const RenderPipelineStageRenderGraphContext& context) { + if (m_stageRecorder != nullptr && + m_stageRecorder->SupportsStageRenderGraph(context.stage)) { + return m_stageRecorder->RecordStageRenderGraph(context); + } + return m_pipelineRenderer != nullptr && m_pipelineRenderer->RecordStageRenderGraph(context); } @@ -125,6 +151,15 @@ bool ScriptableRenderPipelineHost::Render( m_pipelineRenderer->Render(context, surface, sceneData); } +void ScriptableRenderPipelineHost::ResetStageRecorder( + std::unique_ptr stageRecorder) { + if (m_stageRecorder != nullptr) { + m_stageRecorder->Shutdown(); + } + + m_stageRecorder = std::move(stageRecorder); +} + void ScriptableRenderPipelineHost::ResetPipelineRenderer( std::unique_ptr pipelineRenderer) { if (m_pipelineRenderer != nullptr) { diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 084aaf60..9ad14d44 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -416,6 +416,15 @@ struct MockPipelineAssetState { FinalColorSettings defaultFinalColorSettings = {}; }; +struct MockStageRecorderState { + int initializeCalls = 0; + int shutdownCalls = 0; + int recordMainSceneCalls = 0; + bool supportsMainSceneRenderGraph = false; + bool recordMainSceneResult = true; + bool lastReceivedRenderGraphBlackboard = false; +}; + class MockPipeline final : public RenderPipeline { public: explicit MockPipeline(std::shared_ptr state) @@ -592,6 +601,37 @@ private: std::shared_ptr m_state; }; +class MockStageRecorder final : public RenderPipelineStageRecorder { +public: + explicit MockStageRecorder(std::shared_ptr state) + : m_state(std::move(state)) { + } + + bool Initialize(const RenderContext&) override { + ++m_state->initializeCalls; + return true; + } + + void Shutdown() override { + ++m_state->shutdownCalls; + } + + bool SupportsStageRenderGraph(CameraFrameStage stage) const override { + return SupportsCameraFramePipelineGraphRecording(stage) && + m_state->supportsMainSceneRenderGraph; + } + + bool RecordStageRenderGraph( + const RenderPipelineStageRenderGraphContext& context) override { + ++m_state->recordMainSceneCalls; + m_state->lastReceivedRenderGraphBlackboard = context.blackboard != nullptr; + return m_state->recordMainSceneResult; + } + +private: + std::shared_ptr m_state; +}; + template PassT* InstallStandaloneStagePass( RenderPipeline& pipeline, @@ -623,6 +663,34 @@ private: std::shared_ptr m_state; }; +struct MockManagedRenderPipelineBridgeState { + int createStageRecorderCalls = 0; + Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {}; + std::shared_ptr lastCreatedStageRecorderState; +}; + +class MockManagedRenderPipelineBridge final + : public Pipelines::ManagedRenderPipelineBridge { +public: + explicit MockManagedRenderPipelineBridge( + std::shared_ptr state) + : m_state(std::move(state)) { + } + + std::unique_ptr CreateStageRecorder( + const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) const override { + ++m_state->createStageRecorderCalls; + m_state->lastDescriptor = descriptor; + m_state->lastCreatedStageRecorderState = + std::make_shared(); + return std::make_unique( + m_state->lastCreatedStageRecorderState); + } + +private: + std::shared_ptr m_state; +}; + class MockObjectIdPass final : public RenderPass { public: MockObjectIdPass( @@ -3755,6 +3823,75 @@ TEST(ScriptableRenderPipelineHost_Test, ForwardsRendererLifetimeAndFrameRenderin EXPECT_EQ(replacementState->shutdownCalls, 1); } +TEST(ScriptableRenderPipelineHost_Test, PrefersStageRecorderBeforeFallbackRenderer) { + auto rendererState = std::make_shared(); + auto initialRecorderState = std::make_shared(); + auto replacementRecorderState = std::make_shared(); + + rendererState->supportsMainSceneRenderGraph = true; + initialRecorderState->supportsMainSceneRenderGraph = true; + replacementRecorderState->supportsMainSceneRenderGraph = true; + + { + Pipelines::ScriptableRenderPipelineHost host( + std::make_unique(rendererState)); + + auto initialRecorder = + std::make_unique(initialRecorderState); + MockStageRecorder* initialRecorderRaw = initialRecorder.get(); + host.SetStageRecorder(std::move(initialRecorder)); + EXPECT_EQ(host.GetStageRecorder(), initialRecorderRaw); + + auto replacementRecorder = + std::make_unique(replacementRecorderState); + MockStageRecorder* replacementRecorderRaw = replacementRecorder.get(); + host.SetStageRecorder(std::move(replacementRecorder)); + + EXPECT_EQ(initialRecorderState->shutdownCalls, 1); + EXPECT_EQ(host.GetStageRecorder(), replacementRecorderRaw); + + Scene scene("ScriptableRenderPipelineRecorderScene"); + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(1.0f); + + const RenderContext context = CreateValidContext(); + const RenderSurface surface(800, 600); + RenderSceneData sceneData = + CreateSceneDataForCamera(scene, *camera, surface); + RenderGraph graph = {}; + RenderGraphBuilder graphBuilder(graph); + RenderGraphBlackboard blackboard = {}; + bool executionSucceeded = true; + const RenderPipelineStageRenderGraphContext graphContext = { + graphBuilder, + "MainScene", + CameraFrameStage::MainScene, + context, + sceneData, + surface, + nullptr, + nullptr, + XCEngine::RHI::ResourceStates::Common, + {}, + {}, + {}, + &executionSucceeded, + &blackboard + }; + + EXPECT_TRUE(host.SupportsStageRenderGraph(CameraFrameStage::MainScene)); + EXPECT_TRUE(host.RecordStageRenderGraph(graphContext)); + EXPECT_EQ(replacementRecorderState->recordMainSceneCalls, 1); + EXPECT_TRUE(replacementRecorderState->lastReceivedRenderGraphBlackboard); + EXPECT_EQ(rendererState->recordMainSceneCalls, 0); + } + + EXPECT_EQ(rendererState->shutdownCalls, 1); + EXPECT_EQ(replacementRecorderState->shutdownCalls, 1); +} + TEST(ScriptableRenderPipelineHostAsset_Test, CreatesHostFromRendererAssetAndForwardsDefaults) { auto assetState = std::make_shared(); assetState->defaultFinalColorSettings.outputTransferMode = @@ -3784,6 +3921,41 @@ TEST(ScriptableRenderPipelineHostAsset_Test, CreatesHostFromRendererAssetAndForw EXPECT_EQ(assetState->createCalls, 1); } +TEST(ManagedScriptableRenderPipelineAsset_Test, CreatesHostWithStageRecorderFromManagedBridge) { + Pipelines::ClearManagedRenderPipelineBridge(); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + auto bridgeState = std::make_shared(); + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(bridgeState)); + + { + Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); + std::unique_ptr pipeline = asset.CreatePipeline(); + ASSERT_NE(pipeline, nullptr); + + auto* host = + dynamic_cast(pipeline.get()); + ASSERT_NE(host, nullptr); + EXPECT_NE(host->GetPipelineRenderer(), nullptr); + EXPECT_NE(host->GetStageRecorder(), nullptr); + EXPECT_EQ(bridgeState->createStageRecorderCalls, 1); + EXPECT_EQ(bridgeState->lastDescriptor.assemblyName, "GameScripts"); + EXPECT_EQ(bridgeState->lastDescriptor.namespaceName, "Gameplay"); + EXPECT_EQ(bridgeState->lastDescriptor.className, "ManagedRenderPipelineProbeAsset"); + + ASSERT_NE(bridgeState->lastCreatedStageRecorderState, nullptr); + bridgeState->lastCreatedStageRecorderState->supportsMainSceneRenderGraph = true; + EXPECT_TRUE(host->SupportsStageRenderGraph(CameraFrameStage::MainScene)); + } + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) { const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { "GameScripts",