From fa9a5ffb007186d15ce6745d9992a466364ddaab Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 17 Apr 2026 23:54:04 +0800 Subject: [PATCH] refactor(rendering): retain managed pipeline asset runtime per native asset --- .../ManagedScriptableRenderPipelineAsset.h | 25 +- .../Scripting/Mono/MonoScriptRuntime.h | 2 + .../ManagedScriptableRenderPipelineAsset.cpp | 33 +- .../src/Scripting/Mono/MonoScriptRuntime.cpp | 400 ++++++++++-------- .../unit/test_camera_scene_renderer.cpp | 134 ++++-- tests/scripting/test_mono_script_runtime.cpp | 15 +- 6 files changed, 395 insertions(+), 214 deletions(-) diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index a7d210ac..9dd177da 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -42,23 +42,36 @@ public: void ConfigureCameraFramePlan(CameraFramePlan& plan) const override; private: + std::shared_ptr + ResolveManagedAssetRuntime() const; + ManagedRenderPipelineAssetDescriptor m_descriptor; ScriptableRenderPipelineHostAsset m_fallbackAsset; + mutable std::shared_ptr + m_managedAssetRuntime = nullptr; +}; + +class ManagedRenderPipelineAssetRuntime { +public: + virtual ~ManagedRenderPipelineAssetRuntime() = default; + + virtual std::unique_ptr CreateStageRecorder() const { + return nullptr; + } + + virtual void ConfigureCameraFramePlan(CameraFramePlan&) const { + } }; class ManagedRenderPipelineBridge { public: virtual ~ManagedRenderPipelineBridge() = default; - virtual std::unique_ptr CreateStageRecorder( + virtual std::shared_ptr + CreateAssetRuntime( const ManagedRenderPipelineAssetDescriptor&) const { return nullptr; } - - virtual void ConfigureCameraFramePlan( - const ManagedRenderPipelineAssetDescriptor&, - CameraFramePlan&) const { - } }; void SetManagedRenderPipelineBridge( diff --git a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h index 07738d7f..0e725e45 100644 --- a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h +++ b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h @@ -22,6 +22,7 @@ namespace XCEngine { namespace Scripting { class ScriptComponent; +class MonoManagedRenderPipelineAssetRuntime; class MonoManagedRenderPipelineBridge; class MonoManagedRenderPipelineStageRecorder; @@ -241,6 +242,7 @@ private: void SetError(const std::string& error); friend class MonoManagedRenderPipelineBridge; + friend class MonoManagedRenderPipelineAssetRuntime; friend class MonoManagedRenderPipelineStageRecorder; Settings m_settings; diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 1dd81d8e..a2e2f468 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -25,6 +25,22 @@ ManagedScriptableRenderPipelineAsset::ManagedScriptableRenderPipelineAsset( : m_descriptor(std::move(descriptor)) { } +std::shared_ptr +ManagedScriptableRenderPipelineAsset::ResolveManagedAssetRuntime() const { + if (m_managedAssetRuntime != nullptr) { + return m_managedAssetRuntime; + } + + const std::shared_ptr bridge = + GetManagedRenderPipelineBridgeStorage(); + if (bridge == nullptr) { + return nullptr; + } + + m_managedAssetRuntime = bridge->CreateAssetRuntime(m_descriptor); + return m_managedAssetRuntime; +} + std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipeline() const { std::unique_ptr pipeline = m_fallbackAsset.CreatePipeline(); auto* host = dynamic_cast(pipeline.get()); @@ -32,11 +48,10 @@ std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipe return pipeline; } - const std::shared_ptr bridge = - GetManagedRenderPipelineBridgeStorage(); - if (bridge != nullptr) { - host->SetStageRecorder( - bridge->CreateStageRecorder(m_descriptor)); + if (const std::shared_ptr runtime = + ResolveManagedAssetRuntime(); + runtime != nullptr) { + host->SetStageRecorder(runtime->CreateStageRecorder()); } return pipeline; @@ -50,10 +65,10 @@ void ManagedScriptableRenderPipelineAsset::ConfigureCameraFramePlan( CameraFramePlan& plan) const { RenderPipelineAsset::ConfigureCameraFramePlan(plan); - const std::shared_ptr bridge = - GetManagedRenderPipelineBridgeStorage(); - if (bridge != nullptr) { - bridge->ConfigureCameraFramePlan(m_descriptor, plan); + if (const std::shared_ptr runtime = + ResolveManagedAssetRuntime(); + runtime != nullptr) { + runtime->ConfigureCameraFramePlan(plan); } } diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index c49f6d7d..75820136 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -272,10 +272,11 @@ bool SupportsManagedRenderPipelineStageGraphRecording( } // namespace -class MonoManagedRenderPipelineStageRecorder final - : public Rendering::RenderPipelineStageRecorder { +class MonoManagedRenderPipelineAssetRuntime final + : public Rendering::Pipelines::ManagedRenderPipelineAssetRuntime + , public std::enable_shared_from_this { public: - MonoManagedRenderPipelineStageRecorder( + MonoManagedRenderPipelineAssetRuntime( MonoScriptRuntime* runtime, std::weak_ptr runtimeLifetime, Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor) @@ -284,6 +285,56 @@ public: , m_descriptor(std::move(descriptor)) { } + ~MonoManagedRenderPipelineAssetRuntime() override { + ReleaseManagedAsset(); + } + + std::unique_ptr CreateStageRecorder() const override; + + void ConfigureCameraFramePlan( + Rendering::CameraFramePlan& plan) const override; + + MonoScriptRuntime* GetRuntime() const { + return m_runtime; + } + + bool IsRuntimeAlive() const { + return m_runtime != nullptr && + !m_runtimeLifetime.expired() && + m_runtime->m_initialized; + } + + bool CreateManagedPipeline(uint32_t& outPipelineHandle) const; + +private: + bool EnsureManagedAsset() const; + void ReleaseManagedAsset() const; + MonoObject* GetManagedAssetObject() const; + MonoMethod* ResolveCreatePipelineMethod(MonoObject* assetObject) const; + MonoMethod* ResolveConfigureCameraFramePlanMethod( + MonoObject* assetObject) const; + + MonoScriptRuntime* m_runtime = nullptr; + std::weak_ptr m_runtimeLifetime; + Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor m_descriptor; + mutable uint32_t m_assetHandle = 0; + mutable MonoMethod* m_createPipelineMethod = nullptr; + mutable MonoMethod* m_configureCameraFramePlanMethod = nullptr; + mutable bool m_assetCreationAttempted = false; +}; + +class MonoManagedRenderPipelineStageRecorder final + : public Rendering::RenderPipelineStageRecorder { +public: + explicit MonoManagedRenderPipelineStageRecorder( + std::shared_ptr assetRuntime) + : m_assetRuntime(std::move(assetRuntime)) + , m_runtime( + m_assetRuntime != nullptr + ? m_assetRuntime->GetRuntime() + : nullptr) { + } + ~MonoManagedRenderPipelineStageRecorder() override { Shutdown(); } @@ -389,91 +440,27 @@ public: private: bool IsRuntimeAlive() const { - return m_runtime != nullptr && - !m_runtimeLifetime.expired() && - m_runtime->m_initialized; + return m_assetRuntime != nullptr && + m_assetRuntime->IsRuntimeAlive() && + m_runtime != nullptr; } bool EnsureManagedPipeline() const { if (m_pipelineHandle != 0) { return true; } - if (m_pipelineCreationAttempted || !IsRuntimeAlive() || - !m_descriptor.IsValid()) { + if (m_pipelineCreationAttempted || !IsRuntimeAlive()) { return false; } m_pipelineCreationAttempted = true; - MonoClass* assetClass = nullptr; - if (!m_runtime->ResolveManagedClass( - m_descriptor.assemblyName, - m_descriptor.namespaceName, - m_descriptor.className, - assetClass) || - assetClass == nullptr) { - return false; - } - - if (!IsMonoClassOrSubclass( - assetClass, - m_runtime->m_scriptableRenderPipelineAssetClass)) { - m_runtime->SetError( - "Managed render pipeline asset must derive from ScriptableRenderPipelineAsset: " + - m_descriptor.GetFullName() + "."); - return false; - } - - uint32_t assetHandle = 0; - if (!m_runtime->CreateExternalManagedObject(assetClass, assetHandle) || - assetHandle == 0) { - return false; - } - - MonoObject* const assetObject = m_runtime->GetManagedObject(assetHandle); - MonoMethod* const createPipelineMethod = - m_runtime->ResolveManagedMethod(assetObject, "CreatePipeline", 0); - if (!assetObject || !createPipelineMethod) { - m_runtime->DestroyExternalManagedObject(assetHandle); - return false; - } - - MonoObject* pipelineObject = nullptr; - if (!m_runtime->InvokeManagedMethod( - assetObject, - createPipelineMethod, - nullptr, - &pipelineObject) || - pipelineObject == nullptr) { - m_runtime->DestroyExternalManagedObject(assetHandle); - return false; - } - - if (!IsMonoClassOrSubclass( - mono_object_get_class(pipelineObject), - m_runtime->m_scriptableRenderPipelineClass)) { - m_runtime->SetError( - "Managed render pipeline asset returned a non-ScriptableRenderPipeline instance: " + - m_descriptor.GetFullName() + "."); - m_runtime->DestroyExternalManagedObject(assetHandle); - return false; - } - - const uint32_t pipelineHandle = - m_runtime->RetainExternalManagedObject(pipelineObject); - if (pipelineHandle == 0) { - m_runtime->DestroyExternalManagedObject(assetHandle); - return false; - } - - m_assetHandle = assetHandle; - m_pipelineHandle = pipelineHandle; - return true; + return m_assetRuntime->CreateManagedPipeline(m_pipelineHandle) && + m_pipelineHandle != 0; } void ReleaseManagedObjects() { if (!IsRuntimeAlive()) { - m_assetHandle = 0; m_pipelineHandle = 0; return; } @@ -482,10 +469,6 @@ private: m_runtime->DestroyExternalManagedObject(m_pipelineHandle); m_pipelineHandle = 0; } - if (m_assetHandle != 0) { - m_runtime->DestroyExternalManagedObject(m_assetHandle); - m_assetHandle = 0; - } } MonoMethod* ResolveSupportsStageMethod(MonoObject* pipelineObject) const { @@ -546,10 +529,8 @@ private: passes); } + std::shared_ptr m_assetRuntime; MonoScriptRuntime* m_runtime = nullptr; - std::weak_ptr m_runtimeLifetime; - Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor m_descriptor; - mutable uint32_t m_assetHandle = 0; mutable uint32_t m_pipelineHandle = 0; mutable MonoMethod* m_supportsStageMethod = nullptr; mutable MonoMethod* m_recordStageMethod = nullptr; @@ -559,6 +540,172 @@ private: Rendering::Pipelines::BuiltinForwardPipeline m_builtinForwardPipeline; }; +std::unique_ptr +MonoManagedRenderPipelineAssetRuntime::CreateStageRecorder() const { + if (!IsRuntimeAlive() || !m_descriptor.IsValid()) { + return nullptr; + } + + return std::make_unique( + shared_from_this()); +} + +void MonoManagedRenderPipelineAssetRuntime::ConfigureCameraFramePlan( + Rendering::CameraFramePlan& plan) const { + if (!EnsureManagedAsset()) { + return; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + MonoMethod* const method = + ResolveConfigureCameraFramePlanMethod(assetObject); + if (assetObject == nullptr || method == nullptr) { + return; + } + + ManagedScriptableRenderPipelinePlanningContextState planningContextState = {}; + planningContextState.plan = &plan; + const uint64_t planningContextHandle = + RegisterManagedScriptableRenderPipelinePlanningContextState( + planningContextState); + MonoObject* const planningContextObject = + m_runtime->CreateManagedScriptableRenderPipelinePlanningContext( + planningContextHandle); + if (planningContextObject == nullptr) { + UnregisterManagedScriptableRenderPipelinePlanningContextState( + planningContextHandle); + return; + } + + void* args[1] = { planningContextObject }; + m_runtime->InvokeManagedMethod( + assetObject, + method, + args, + nullptr); + UnregisterManagedScriptableRenderPipelinePlanningContextState( + planningContextHandle); +} + +bool MonoManagedRenderPipelineAssetRuntime::CreateManagedPipeline( + uint32_t& outPipelineHandle) const { + outPipelineHandle = 0; + if (!EnsureManagedAsset()) { + return false; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + MonoMethod* const createPipelineMethod = + ResolveCreatePipelineMethod(assetObject); + if (assetObject == nullptr || createPipelineMethod == nullptr) { + return false; + } + + MonoObject* pipelineObject = nullptr; + if (!m_runtime->InvokeManagedMethod( + assetObject, + createPipelineMethod, + nullptr, + &pipelineObject) || + pipelineObject == nullptr) { + return false; + } + + if (!IsMonoClassOrSubclass( + mono_object_get_class(pipelineObject), + m_runtime->m_scriptableRenderPipelineClass)) { + m_runtime->SetError( + "Managed render pipeline asset returned a non-ScriptableRenderPipeline instance: " + + m_descriptor.GetFullName() + "."); + return false; + } + + outPipelineHandle = m_runtime->RetainExternalManagedObject(pipelineObject); + return outPipelineHandle != 0; +} + +bool MonoManagedRenderPipelineAssetRuntime::EnsureManagedAsset() const { + if (m_assetHandle != 0) { + return true; + } + if (m_assetCreationAttempted || !IsRuntimeAlive() || !m_descriptor.IsValid()) { + return false; + } + + m_assetCreationAttempted = true; + + MonoClass* assetClass = nullptr; + if (!m_runtime->ResolveManagedClass( + m_descriptor.assemblyName, + m_descriptor.namespaceName, + m_descriptor.className, + assetClass) || + assetClass == nullptr) { + return false; + } + + if (!IsMonoClassOrSubclass( + assetClass, + m_runtime->m_scriptableRenderPipelineAssetClass)) { + m_runtime->SetError( + "Managed render pipeline asset must derive from ScriptableRenderPipelineAsset: " + + m_descriptor.GetFullName() + "."); + return false; + } + + return m_runtime->CreateExternalManagedObject(assetClass, m_assetHandle) && + m_assetHandle != 0; +} + +void MonoManagedRenderPipelineAssetRuntime::ReleaseManagedAsset() const { + m_createPipelineMethod = nullptr; + m_configureCameraFramePlanMethod = nullptr; + m_assetCreationAttempted = false; + + if (!IsRuntimeAlive()) { + m_assetHandle = 0; + return; + } + + if (m_assetHandle != 0) { + m_runtime->DestroyExternalManagedObject(m_assetHandle); + m_assetHandle = 0; + } +} + +MonoObject* MonoManagedRenderPipelineAssetRuntime::GetManagedAssetObject() const { + return m_assetHandle != 0 + ? m_runtime->GetManagedObject(m_assetHandle) + : nullptr; +} + +MonoMethod* MonoManagedRenderPipelineAssetRuntime::ResolveCreatePipelineMethod( + MonoObject* assetObject) const { + if (m_createPipelineMethod == nullptr) { + m_createPipelineMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "CreatePipeline", + 0); + } + + return m_createPipelineMethod; +} + +MonoMethod* +MonoManagedRenderPipelineAssetRuntime::ResolveConfigureCameraFramePlanMethod( + MonoObject* assetObject) const { + if (m_configureCameraFramePlanMethod == nullptr) { + m_configureCameraFramePlanMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "ConfigureCameraFramePlan", + 1); + } + + return m_configureCameraFramePlanMethod; +} + class MonoManagedRenderPipelineBridge final : public Rendering::Pipelines::ManagedRenderPipelineBridge { public: @@ -569,71 +716,20 @@ public: , m_runtimeLifetime(std::move(runtimeLifetime)) { } - std::unique_ptr CreateStageRecorder( - const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) const override { + std::shared_ptr + CreateAssetRuntime( + const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) + const override { if (!IsRuntimeAlive() || !descriptor.IsValid()) { return nullptr; } - return std::make_unique( + return std::make_shared( m_runtime, m_runtimeLifetime, descriptor); } - void ConfigureCameraFramePlan( - const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor, - Rendering::CameraFramePlan& plan) const override { - if (!IsRuntimeAlive() || !descriptor.IsValid()) { - return; - } - - uint32_t assetHandle = 0; - MonoObject* const assetObject = - CreateManagedAssetInstance(descriptor, assetHandle); - if (assetObject == nullptr || assetHandle == 0) { - if (assetHandle != 0) { - m_runtime->DestroyExternalManagedObject(assetHandle); - } - return; - } - - MonoMethod* const method = - m_runtime->ResolveManagedMethod( - assetObject, - "ConfigureCameraFramePlan", - 1); - if (method == nullptr) { - m_runtime->DestroyExternalManagedObject(assetHandle); - return; - } - - ManagedScriptableRenderPipelinePlanningContextState planningContextState = {}; - planningContextState.plan = &plan; - const uint64_t planningContextHandle = - RegisterManagedScriptableRenderPipelinePlanningContextState( - planningContextState); - MonoObject* const planningContextObject = - m_runtime->CreateManagedScriptableRenderPipelinePlanningContext( - planningContextHandle); - if (planningContextObject == nullptr) { - UnregisterManagedScriptableRenderPipelinePlanningContextState( - planningContextHandle); - m_runtime->DestroyExternalManagedObject(assetHandle); - return; - } - - void* args[1] = { planningContextObject }; - m_runtime->InvokeManagedMethod( - assetObject, - method, - args, - nullptr); - UnregisterManagedScriptableRenderPipelinePlanningContextState( - planningContextHandle); - m_runtime->DestroyExternalManagedObject(assetHandle); - } - private: bool IsRuntimeAlive() const { return m_runtime != nullptr && @@ -641,38 +737,6 @@ private: m_runtime->m_initialized; } - MonoObject* CreateManagedAssetInstance( - const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor, - uint32_t& outAssetHandle) const { - outAssetHandle = 0; - - MonoClass* assetClass = nullptr; - if (!m_runtime->ResolveManagedClass( - descriptor.assemblyName, - descriptor.namespaceName, - descriptor.className, - assetClass) || - assetClass == nullptr) { - return nullptr; - } - - if (!IsMonoClassOrSubclass( - assetClass, - m_runtime->m_scriptableRenderPipelineAssetClass)) { - m_runtime->SetError( - "Managed render pipeline asset must derive from ScriptableRenderPipelineAsset: " + - descriptor.GetFullName() + "."); - return nullptr; - } - - if (!m_runtime->CreateExternalManagedObject(assetClass, outAssetHandle) || - outAssetHandle == 0) { - return nullptr; - } - - return m_runtime->GetManagedObject(outAssetHandle); - } - MonoScriptRuntime* m_runtime = nullptr; std::weak_ptr m_runtimeLifetime; }; diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 0d429e37..7501c326 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -772,12 +772,44 @@ private: std::shared_ptr m_state; }; -struct MockManagedRenderPipelineBridgeState { +struct MockManagedRenderPipelineAssetRuntimeState { int createStageRecorderCalls = 0; - Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {}; - std::shared_ptr lastCreatedStageRecorderState; int configureCameraFramePlanCalls = 0; - Pipelines::ManagedRenderPipelineAssetDescriptor lastPlanningDescriptor = {}; + std::shared_ptr lastCreatedStageRecorderState; + std::function configureCameraFramePlan = {}; +}; + +class MockManagedRenderPipelineAssetRuntime final + : public Pipelines::ManagedRenderPipelineAssetRuntime { +public: + explicit MockManagedRenderPipelineAssetRuntime( + std::shared_ptr state) + : m_state(std::move(state)) { + } + + std::unique_ptr CreateStageRecorder() const override { + ++m_state->createStageRecorderCalls; + m_state->lastCreatedStageRecorderState = + std::make_shared(); + return std::make_unique( + m_state->lastCreatedStageRecorderState); + } + + void ConfigureCameraFramePlan(CameraFramePlan& plan) const override { + ++m_state->configureCameraFramePlanCalls; + if (m_state->configureCameraFramePlan) { + m_state->configureCameraFramePlan(plan); + } + } + +private: + std::shared_ptr m_state; +}; + +struct MockManagedRenderPipelineBridgeState { + int createAssetRuntimeCalls = 0; + Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {}; + std::shared_ptr lastCreatedRuntimeState; std::function configureCameraFramePlan = {}; }; @@ -789,24 +821,17 @@ public: : m_state(std::move(state)) { } - std::unique_ptr CreateStageRecorder( + std::shared_ptr + CreateAssetRuntime( const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) const override { - ++m_state->createStageRecorderCalls; + ++m_state->createAssetRuntimeCalls; m_state->lastDescriptor = descriptor; - m_state->lastCreatedStageRecorderState = - std::make_shared(); - return std::make_unique( - m_state->lastCreatedStageRecorderState); - } - - void ConfigureCameraFramePlan( - const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor, - CameraFramePlan& plan) const override { - ++m_state->configureCameraFramePlanCalls; - m_state->lastPlanningDescriptor = descriptor; - if (m_state->configureCameraFramePlan) { - m_state->configureCameraFramePlan(plan); - } + m_state->lastCreatedRuntimeState = + std::make_shared(); + m_state->lastCreatedRuntimeState->configureCameraFramePlan = + m_state->configureCameraFramePlan; + return std::make_shared( + m_state->lastCreatedRuntimeState); } private: @@ -4471,13 +4496,17 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, CreatesHostWithStageRecorderFrom ASSERT_NE(host, nullptr); EXPECT_NE(host->GetPipelineRenderer(), nullptr); EXPECT_NE(host->GetStageRecorder(), nullptr); - EXPECT_EQ(bridgeState->createStageRecorderCalls, 1); + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 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; + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); + ASSERT_NE( + bridgeState->lastCreatedRuntimeState->lastCreatedStageRecorderState, + nullptr); + bridgeState->lastCreatedRuntimeState->lastCreatedStageRecorderState + ->supportsMainSceneRenderGraph = true; EXPECT_TRUE(host->SupportsStageRenderGraph(CameraFrameStage::MainScene)); } @@ -4532,12 +4561,14 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeRequestFullscre Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); asset.ConfigureCameraFramePlan(plan); - EXPECT_EQ(bridgeState->configureCameraFramePlanCalls, 1); - EXPECT_EQ(bridgeState->lastPlanningDescriptor.assemblyName, "GameScripts"); - EXPECT_EQ(bridgeState->lastPlanningDescriptor.namespaceName, "Gameplay"); + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); EXPECT_EQ( - bridgeState->lastPlanningDescriptor.className, - "ManagedPlannedFullscreenRenderPipelineProbeAsset"); + bridgeState->lastCreatedRuntimeState->configureCameraFramePlanCalls, + 1); + EXPECT_EQ(bridgeState->lastDescriptor.assemblyName, "GameScripts"); + EXPECT_EQ(bridgeState->lastDescriptor.namespaceName, "Gameplay"); + EXPECT_EQ(bridgeState->lastDescriptor.className, "ManagedPlannedFullscreenRenderPipelineProbeAsset"); EXPECT_TRUE(plan.IsFullscreenStageRequested(CameraFrameStage::PostProcess)); EXPECT_TRUE(plan.IsFullscreenStageRequested(CameraFrameStage::FinalOutput)); EXPECT_EQ(plan.postProcess.passes, nullptr); @@ -4556,6 +4587,53 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeRequestFullscre Pipelines::ClearManagedRenderPipelineBridge(); } +TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossPipelineAndPlanRequests) { + 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 firstPipeline = asset.CreatePipeline(); + std::unique_ptr secondPipeline = asset.CreatePipeline(); + ASSERT_NE(firstPipeline, nullptr); + ASSERT_NE(secondPipeline, nullptr); + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); + + auto runtimeState = bridgeState->lastCreatedRuntimeState; + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + EXPECT_EQ(runtimeState->createStageRecorderCalls, 2); + ASSERT_NE(runtimeState->lastCreatedStageRecorderState, nullptr); + + auto* firstHost = + dynamic_cast(firstPipeline.get()); + auto* secondHost = + dynamic_cast(secondPipeline.get()); + ASSERT_NE(firstHost, nullptr); + ASSERT_NE(secondHost, nullptr); + EXPECT_NE(firstHost->GetStageRecorder(), nullptr); + EXPECT_NE(secondHost->GetStageRecorder(), nullptr); + + CameraRenderRequest request = {}; + request.surface = RenderSurface(128, 72); + CameraFramePlan firstPlan = CameraFramePlan::FromRequest(request); + CameraFramePlan secondPlan = CameraFramePlan::FromRequest(request); + asset.ConfigureCameraFramePlan(firstPlan); + asset.ConfigureCameraFramePlan(secondPlan); + + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + EXPECT_EQ(runtimeState->configureCameraFramePlanCalls, 2); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST(CameraRenderer_Test, RendersManagedRequestedPostProcessWithoutLegacySequence) { Scene scene("CameraRendererManagedRequestedPostProcessScene"); diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index f7124406..643a2a7c 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -335,8 +335,11 @@ TEST_F( "ManagedRenderPipelineProbeAsset" }; + std::shared_ptr + assetRuntime = bridge->CreateAssetRuntime(descriptor); + ASSERT_NE(assetRuntime, nullptr); std::unique_ptr recorder = - bridge->CreateStageRecorder(descriptor); + assetRuntime->CreateStageRecorder(); ASSERT_NE(recorder, nullptr); const XCEngine::Rendering::RenderContext context = {}; @@ -364,8 +367,11 @@ TEST_F( "ManagedRenderPipelineProbeAsset" }; + std::shared_ptr + assetRuntime = bridge->CreateAssetRuntime(descriptor); + ASSERT_NE(assetRuntime, nullptr); std::unique_ptr recorder = - bridge->CreateStageRecorder(descriptor); + assetRuntime->CreateStageRecorder(); ASSERT_NE(recorder, nullptr); const XCEngine::Rendering::RenderContext renderContext = {}; @@ -449,8 +455,11 @@ TEST_F( "ManagedPostProcessRenderPipelineProbeAsset" }; + std::shared_ptr + assetRuntime = bridge->CreateAssetRuntime(descriptor); + ASSERT_NE(assetRuntime, nullptr); std::unique_ptr recorder = - bridge->CreateStageRecorder(descriptor); + assetRuntime->CreateStageRecorder(); ASSERT_NE(recorder, nullptr); const XCEngine::Rendering::RenderContext renderContext = {};