From 2fde2f16c21d1fa34b54446ae40e06c5d8c0a4f2 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 29 Apr 2026 03:29:17 +0800 Subject: [PATCH] Move scene request planning behind pipeline asset --- engine/include/XCEngine/Rendering/AGENTS.md | 14 +- .../Rendering/Execution/RenderPipelineHost.h | 27 +++ .../Rendering/Execution/SceneRenderer.h | 6 +- .../ManagedScriptableRenderPipelineAsset.h | 22 ++ .../XCEngine/Rendering/RenderPipelineAsset.h | 12 +- .../Execution/RenderPipelineHost.cpp | 61 ++++++ .../src/Rendering/Execution/SceneRenderer.cpp | 37 ++-- .../ManagedScriptableRenderPipelineAsset.cpp | 30 +++ engine/src/Rendering/RenderPipelineAsset.cpp | 17 ++ .../unit/test_camera_scene_renderer.cpp | 192 ++++++++++++++++++ 10 files changed, 393 insertions(+), 25 deletions(-) diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index 816d4b5c..667d5d65 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -52,8 +52,9 @@ Editor viewport 边界补充:SceneViewport 的 render request 是 editor 每 ```text SceneRenderer - -> SceneRenderRequestPlanner::BuildRequests -> RenderPipelineHost + -> RenderPipelineAsset::BuildSceneRenderRequests + -> (default) SceneRenderRequestPlanner::BuildRequests -> CameraFramePlanBuilder -> CameraRenderer -> DirectionalShadowRuntime @@ -66,9 +67,10 @@ SceneRenderer 职责边界: -- `SceneRenderer`:场景级入口,生成/排序 camera stack frame plans,并委托执行;不做 scene extraction,不发 draw call。 -- `SceneRenderRequestPlanner`:收集可用相机、推导 viewport / clear / camera stack / directional shadow request;不创建执行期临时资源。 -- `RenderPipelineHost`:把 requests 变成 frame plans / stack plans,稳定排序并驱动 `CameraRenderer`。 +- `SceneRenderer`:场景级 façade,只把 scene/override camera/context/surface 交给当前 pipeline host;不再直接拥有 top-level request planner。 +- `RenderPipelineAsset`:顶层 scene/camera request authority。`BuildSceneRenderRequests(...)` 是对齐 Unity `RenderPipeline.Render(...)` 入口责任的首个 native 合约面;未来所有 SRP/URP 级 camera policy、camera list 裁剪、renderer routing、tooling request 安装,都必须先经过这里。 +- `SceneRenderRequestPlanner`:默认 native planning utility。它负责收集可用相机、推导 viewport / clear / camera stack / directional shadow request;不再是顶层 authority,而是 `RenderPipelineAsset::BuildSceneRenderRequests(...)` 的默认 fallback 实现。 +- `RenderPipelineHost`:向当前 asset 请求 scene render requests,把 requests 变成 frame plans / stack plans,稳定排序并驱动 `CameraRenderer`。 - `CameraFramePlanBuilder`:从 `CameraRenderRequest` 建 `CameraFramePlan`,调用 `RenderPipelineAsset::ConfigureCameraFramePlan`。 - `CameraRenderer`:单相机执行器,校验 plan,解析 pipeline binding,生成 shadow execution state,抽取 `RenderSceneData`,交给 camera frame graph。 - `RenderPipeline` / `RenderPipelineBackend`:提供 scene setup、shadow execution setup、stage graph recording、fallback render。 @@ -98,7 +100,7 @@ SceneRenderer - `UsesGraphManagedSceneColor` 和 `UsesGraphManagedOutputColor(stage)` 决定是否创建 graph-managed transient color。 - fullscreen stage 的 source 必须通过 `CameraFrameColorSource` 表达,不要用隐式全局变量猜输入。 -不要绕过 `RenderPipelineAsset::ConfigureCameraRenderRequest` 和 `ConfigureCameraFramePlan`。它们是 SRP/URP asset policy 对齐 Unity 的核心 hook。 +不要绕过 `RenderPipelineAsset::BuildSceneRenderRequests`、`ConfigureCameraRenderRequest` 和 `ConfigureCameraFramePlan`。顶层 request ingress、per-request policy、per-plan policy 现在都要先经过 asset contract;这是 SRP/URP asset policy 对齐 Unity 的核心 hook 面。 ## 4. Stage 模型 @@ -412,7 +414,7 @@ cmake --build build --config Debug --target rendering_phase_regression 1. 稳住 native RenderGraph / RHI / scene backend。 2. 让 managed SRP runtime 持有真实 asset / pipeline / renderer runtime。 3. 扩大 `ScriptableRenderContext` 和 managed RenderGraph 的受控能力面。 -4. 把 URP-like 的 asset policy、renderer data、feature queue、render pass event、post/final/shadow policy 逐步上移到 managed。 +4. 把 URP-like 的 top-level scene request planning、asset policy、renderer data、feature queue、render pass event、post/final/shadow policy 逐步上移到 managed。 5. 保持 `BuiltinForwardPipeline` 作为可被 SRP 调度的 native backend,而不是重新变回唯一上层管线。 一句话:XCEngine Rendering 的对齐目标是 Unity SRP/URP 的分层模型,不是复制 Unity 某个版本的源码细节。C++ 做执行内核,C# 做可编排管线,RenderGraph 做中间契约。 diff --git a/engine/include/XCEngine/Rendering/Execution/RenderPipelineHost.h b/engine/include/XCEngine/Rendering/Execution/RenderPipelineHost.h index e539279e..2ddafdf3 100644 --- a/engine/include/XCEngine/Rendering/Execution/RenderPipelineHost.h +++ b/engine/include/XCEngine/Rendering/Execution/RenderPipelineHost.h @@ -2,11 +2,17 @@ #include #include +#include #include #include namespace XCEngine { +namespace Components { +class CameraComponent; +class Scene; +} // namespace Components + namespace Rendering { class CameraFramePlanBuilder; @@ -20,9 +26,23 @@ public: void SetPipelineAsset(std::shared_ptr pipelineAsset); RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); } const RenderPipelineAsset* GetPipelineAsset() const { return m_cameraRenderer.GetPipelineAsset(); } + void SetDirectionalShadowPlanningSettings( + const DirectionalShadowPlanningSettings& settings); + const DirectionalShadowPlanningSettings& + GetDirectionalShadowPlanningSettings() const; + std::vector BuildFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface); std::vector BuildFramePlans( const std::vector& requests); + std::vector BuildStackFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface); std::vector BuildStackFramePlans( const std::vector& requests); @@ -32,8 +52,15 @@ public: bool Render(const std::vector& stackPlans); private: + std::vector BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) const; + CameraRenderer m_cameraRenderer; std::unique_ptr m_framePlanBuilder; + SceneRenderRequestPlanner m_requestPlanner; }; } // namespace Rendering diff --git a/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h b/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h index 54104e93..76cc4a7d 100644 --- a/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h +++ b/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h @@ -2,7 +2,6 @@ #include #include -#include #include @@ -23,6 +22,10 @@ public: void SetPipelineAsset(std::shared_ptr pipelineAsset); RenderPipeline* GetPipeline() const { return m_pipelineHost.GetPipeline(); } const RenderPipelineAsset* GetPipelineAsset() const { return m_pipelineHost.GetPipelineAsset(); } + void SetDirectionalShadowPlanningSettings( + const DirectionalShadowPlanningSettings& settings); + const DirectionalShadowPlanningSettings& + GetDirectionalShadowPlanningSettings() const; std::vector BuildFramePlans( const Components::Scene& scene, @@ -45,7 +48,6 @@ public: const RenderContext& context, const RenderSurface& surface); - SceneRenderRequestPlanner m_requestPlanner; RenderPipelineHost m_pipelineHost; }; diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 3031e76c..692fd2cc 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -7,8 +7,14 @@ #include #include #include +#include namespace XCEngine { +namespace Components { +class CameraComponent; +class Scene; +} // namespace Components + namespace Rendering { struct CameraFramePlan; @@ -68,6 +74,13 @@ public: } std::unique_ptr CreatePipeline() const override; + std::vector BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings) + const override; void ConfigureCameraRenderRequest( CameraRenderRequest& request, size_t renderedBaseCameraCount, @@ -99,6 +112,15 @@ public: return nullptr; } + virtual bool BuildSceneRenderRequests( + const Components::Scene&, + Components::CameraComponent*, + const RenderContext&, + const RenderSurface&, + const DirectionalShadowPlanningSettings&, + std::vector&) const { + return false; + } virtual void ConfigureCameraRenderRequest( CameraRenderRequest&, size_t, diff --git a/engine/include/XCEngine/Rendering/RenderPipelineAsset.h b/engine/include/XCEngine/Rendering/RenderPipelineAsset.h index 3e2b432d..ec31f700 100644 --- a/engine/include/XCEngine/Rendering/RenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/RenderPipelineAsset.h @@ -1,20 +1,24 @@ #pragma once +#include #include #include +#include namespace XCEngine { namespace Components { +class CameraComponent; class Scene; } // namespace Components namespace Rendering { -struct CameraRenderRequest; struct CameraFramePlan; struct DirectionalShadowPlanningSettings; class RenderPipeline; +class RenderSurface; +struct RenderContext; void ApplyDefaultRenderPipelineAssetCameraRenderRequestPolicy( CameraRenderRequest& request, @@ -35,6 +39,12 @@ public: virtual std::unique_ptr CreatePipeline() const = 0; virtual void ConfigurePipeline(RenderPipeline&) const {} + virtual std::vector BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings) const; virtual void ConfigureCameraRenderRequest( CameraRenderRequest& request, size_t renderedBaseCameraCount, diff --git a/engine/src/Rendering/Execution/RenderPipelineHost.cpp b/engine/src/Rendering/Execution/RenderPipelineHost.cpp index 23e61049..c6b0b39b 100644 --- a/engine/src/Rendering/Execution/RenderPipelineHost.cpp +++ b/engine/src/Rendering/Execution/RenderPipelineHost.cpp @@ -2,6 +2,7 @@ #include "Rendering/Planning/CameraFramePlanBuilder.h" #include "Rendering/Planning/SceneRenderRequestUtils.h" +#include "Rendering/RenderPipelineAsset.h" #include #include @@ -53,6 +54,53 @@ void RenderPipelineHost::SetPipelineAsset(std::shared_ptr RenderPipelineHost::BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) const { + if (const RenderPipelineAsset* const pipelineAsset = + GetPipelineAsset(); + pipelineAsset != nullptr) { + return pipelineAsset->BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + m_requestPlanner.GetDirectionalShadowPlanningSettings()); + } + + return m_requestPlanner.BuildRequests( + scene, + overrideCamera, + context, + surface); +} + +std::vector RenderPipelineHost::BuildFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) { + return BuildFramePlans( + BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface)); +} + std::vector RenderPipelineHost::BuildFramePlans( const std::vector& requests) { return m_framePlanBuilder != nullptr @@ -62,6 +110,19 @@ std::vector RenderPipelineHost::BuildFramePlans( : std::vector(); } +std::vector RenderPipelineHost::BuildStackFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) { + return BuildStackFramePlans( + BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface)); +} + std::vector RenderPipelineHost::BuildStackFramePlans( const std::vector& requests) { return m_framePlanBuilder != nullptr diff --git a/engine/src/Rendering/Execution/SceneRenderer.cpp b/engine/src/Rendering/Execution/SceneRenderer.cpp index 0fb0e3d5..977b56cc 100644 --- a/engine/src/Rendering/Execution/SceneRenderer.cpp +++ b/engine/src/Rendering/Execution/SceneRenderer.cpp @@ -19,19 +19,27 @@ void SceneRenderer::SetPipelineAsset(std::shared_ptr m_pipelineHost.SetPipelineAsset(std::move(pipelineAsset)); } +void SceneRenderer::SetDirectionalShadowPlanningSettings( + const DirectionalShadowPlanningSettings& settings) { + m_pipelineHost.SetDirectionalShadowPlanningSettings( + settings); +} + +const DirectionalShadowPlanningSettings& +SceneRenderer::GetDirectionalShadowPlanningSettings() const { + return m_pipelineHost.GetDirectionalShadowPlanningSettings(); +} + std::vector SceneRenderer::BuildFramePlans( const Components::Scene& scene, Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { - const std::vector requests = - m_requestPlanner.BuildRequests( - scene, - overrideCamera, - context, - surface, - m_pipelineHost.GetPipelineAsset()); - return m_pipelineHost.BuildFramePlans(requests); + return m_pipelineHost.BuildFramePlans( + scene, + overrideCamera, + context, + surface); } std::vector SceneRenderer::BuildStackFramePlans( @@ -39,14 +47,11 @@ std::vector SceneRenderer::BuildStackFramePlans( Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { - const std::vector requests = - m_requestPlanner.BuildRequests( - scene, - overrideCamera, - context, - surface, - m_pipelineHost.GetPipelineAsset()); - return m_pipelineHost.BuildStackFramePlans(requests); + return m_pipelineHost.BuildStackFramePlans( + scene, + overrideCamera, + context, + surface); } bool SceneRenderer::Render(const CameraFramePlan& plan) { diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 53485c61..37dfe6df 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -118,6 +118,36 @@ std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipe return pipeline; } +std::vector +ManagedScriptableRenderPipelineAsset::BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings) const { + if (const std::shared_ptr runtime = + ResolveManagedAssetRuntime(); + runtime != nullptr) { + std::vector requests; + if (runtime->BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + directionalShadowSettings, + requests)) { + return requests; + } + } + + return RenderPipelineAsset::BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + directionalShadowSettings); +} + void ManagedScriptableRenderPipelineAsset::ConfigureCameraRenderRequest( CameraRenderRequest& request, size_t renderedBaseCameraCount, diff --git a/engine/src/Rendering/RenderPipelineAsset.cpp b/engine/src/Rendering/RenderPipelineAsset.cpp index 44c2a177..3a09b10c 100644 --- a/engine/src/Rendering/RenderPipelineAsset.cpp +++ b/engine/src/Rendering/RenderPipelineAsset.cpp @@ -95,6 +95,23 @@ void RenderPipelineAsset::ConfigureCameraRenderRequest( directionalShadowSettings); } +std::vector RenderPipelineAsset::BuildSceneRenderRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings) const { + SceneRenderRequestPlanner requestPlanner = {}; + requestPlanner.SetDirectionalShadowPlanningSettings( + directionalShadowSettings); + return requestPlanner.BuildRequests( + scene, + overrideCamera, + context, + surface, + this); +} + void RenderPipelineAsset::ConfigureCameraFramePlan(CameraFramePlan& plan) const { ApplyDefaultRenderPipelineAssetCameraFramePlanPolicy( plan, diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 6321932c..6c46bf00 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -517,6 +517,7 @@ public: struct MockPipelineAssetState { int createCalls = 0; + int buildSceneRenderRequestsCalls = 0; int configureCameraRenderRequestCalls = 0; int configureCameraFramePlanCalls = 0; bool createNullPipeline = false; @@ -525,6 +526,12 @@ struct MockPipelineAssetState { RenderPipeline* lastCreatedPipeline = nullptr; FinalColorSettings defaultFinalColorSettings = {}; std::function configurePipeline = {}; + std::function( + const Scene&, + CameraComponent*, + const RenderContext&, + const RenderSurface&, + const DirectionalShadowPlanningSettings&)> buildSceneRenderRequests = {}; std::functiondefaultFinalColorSettings; } + std::vector BuildSceneRenderRequests( + const Scene& scene, + CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings) const override { + ++m_state->buildSceneRenderRequestsCalls; + if (m_state->buildSceneRenderRequests) { + return m_state->buildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + directionalShadowSettings); + } + + return RenderPipelineAsset::BuildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + directionalShadowSettings); + } + void ConfigureCameraRenderRequest( CameraRenderRequest& request, size_t renderedBaseCameraCount, @@ -896,6 +927,7 @@ private: struct MockManagedRenderPipelineAssetRuntimeState { int createStageRecorderCalls = 0; + int buildSceneRenderRequestsCalls = 0; int configureCameraRenderRequestCalls = 0; int getPipelineBackendAssetCalls = 0; int getDefaultFinalColorSettingsCalls = 0; @@ -909,6 +941,13 @@ struct MockManagedRenderPipelineAssetRuntimeState { pipelineBackendAssetPolicy = Pipelines::ManagedPipelineBackendAssetPolicy::Unspecified; std::shared_ptr lastCreatedStageRecorderState; + std::function&)> buildSceneRenderRequests = {}; std::functionlastCreatedStageRecorderState); } + bool BuildSceneRenderRequests( + const Scene& scene, + CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings& directionalShadowSettings, + std::vector& requests) const override { + ++m_state->buildSceneRenderRequestsCalls; + if (!m_state->buildSceneRenderRequests) { + return false; + } + + return m_state->buildSceneRenderRequests( + scene, + overrideCamera, + context, + surface, + directionalShadowSettings, + requests); + } + void ConfigureCameraRenderRequest( CameraRenderRequest& request, size_t renderedBaseCameraCount, @@ -993,6 +1053,13 @@ struct MockManagedRenderPipelineBridgeState { Pipelines::ManagedPipelineBackendAssetPolicy pipelineBackendAssetPolicy = Pipelines::ManagedPipelineBackendAssetPolicy::Unspecified; + std::function&)> buildSceneRenderRequests = {}; std::functionpipelineBackendAsset; m_state->lastCreatedRuntimeState->pipelineBackendAssetPolicy = m_state->pipelineBackendAssetPolicy; + m_state->lastCreatedRuntimeState->buildSceneRenderRequests = + m_state->buildSceneRenderRequests; m_state->lastCreatedRuntimeState->configureCameraRenderRequest = m_state->configureCameraRenderRequest; return std::make_shared( @@ -3834,6 +3903,56 @@ TEST(SceneRenderer_Test, UsesPipelineAssetCameraRenderRequestConfigurationHook) EXPECT_FALSE(plans[0].directionalShadow.IsValid()); } +TEST(SceneRenderer_Test, DelegatesSceneRequestPlanningToPipelineAssetAuthority) { + Scene scene("SceneRendererAssetOwnedPlanningScene"); + + GameObject* firstCameraObject = scene.CreateGameObject("FirstCamera"); + auto* firstCamera = firstCameraObject->AddComponent(); + firstCamera->SetPrimary(true); + firstCamera->SetDepth(1.0f); + + GameObject* secondCameraObject = scene.CreateGameObject("SecondCamera"); + auto* secondCamera = secondCameraObject->AddComponent(); + secondCamera->SetPrimary(true); + secondCamera->SetDepth(5.0f); + + auto assetState = std::make_shared(); + assetState->buildSceneRenderRequests = + [secondCamera]( + const Scene& scene, + CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings&) { + EXPECT_EQ(overrideCamera, nullptr); + + CameraRenderRequest request = {}; + request.scene = &scene; + request.camera = secondCamera; + request.context = context; + request.surface = surface; + request.cameraStackId = 7u; + request.cameraDepth = secondCamera->GetDepth(); + request.cameraStackOrder = 0u; + request.clearFlags = RenderClearFlags::All; + return std::vector{ request }; + }; + + SceneRenderer renderer(std::make_shared(assetState)); + const std::vector plans = + renderer.BuildFramePlans( + scene, + nullptr, + CreateValidContext(), + RenderSurface(640, 360)); + + ASSERT_EQ(plans.size(), 1u); + EXPECT_EQ(assetState->buildSceneRenderRequestsCalls, 1); + EXPECT_EQ(assetState->configureCameraRenderRequestCalls, 0); + EXPECT_EQ(plans[0].request.camera, secondCamera); + EXPECT_EQ(plans[0].request.cameraStackId, 7u); +} + TEST(SceneRenderer_Test, BuildsFinalOutputRequestFromResolvedFinalColorPolicy) { Scene scene("SceneRendererFinalOutputScene"); @@ -5472,6 +5591,79 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossP Pipelines::ClearManagedRenderPipelineBridge(); } +TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeBuildSceneRenderRequests) { + Pipelines::ClearManagedRenderPipelineBridge(); + + Scene scene("ManagedPipelineAssetBuildSceneRequests"); + + GameObject* firstCameraObject = scene.CreateGameObject("FirstCamera"); + auto* firstCamera = firstCameraObject->AddComponent(); + firstCamera->SetPrimary(true); + firstCamera->SetDepth(1.0f); + + GameObject* secondCameraObject = scene.CreateGameObject("SecondCamera"); + auto* secondCamera = secondCameraObject->AddComponent(); + secondCamera->SetPrimary(true); + secondCamera->SetDepth(3.0f); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + + auto bridgeState = std::make_shared(); + bridgeState->pipelineBackendAsset = std::make_shared( + std::make_shared()); + bridgeState->buildSceneRenderRequests = + [secondCamera]( + const Scene& scene, + CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface, + const DirectionalShadowPlanningSettings&, + std::vector& requests) { + EXPECT_EQ(overrideCamera, nullptr); + + CameraRenderRequest request = {}; + request.scene = &scene; + request.camera = secondCamera; + request.context = context; + request.surface = surface; + request.cameraStackId = 9u; + request.cameraDepth = secondCamera->GetDepth(); + request.cameraStackOrder = 0u; + request.clearFlags = RenderClearFlags::All; + requests = { request }; + return true; + }; + + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(bridgeState)); + + Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); + const std::vector requests = + asset.BuildSceneRenderRequests( + scene, + nullptr, + CreateValidContext(), + RenderSurface(320, 180), + DirectionalShadowPlanningSettings{}); + + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); + ASSERT_EQ(requests.size(), 1u); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->buildSceneRenderRequestsCalls, + 1); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->configureCameraRenderRequestCalls, + 0); + EXPECT_EQ(requests[0].camera, secondCamera); + EXPECT_EQ(requests[0].cameraStackId, 9u); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST(ManagedScriptableRenderPipelineAsset_Test, RebindsManagedAssetRuntimeWhenBridgeChanges) { Pipelines::ClearManagedRenderPipelineBridge();