diff --git a/docs/used/SRP_UniversalRenderSceneSetupPolicyManagedOwnershipPlan_完成归档_2026-04-21.md b/docs/used/SRP_UniversalRenderSceneSetupPolicyManagedOwnershipPlan_完成归档_2026-04-21.md new file mode 100644 index 00000000..1e42277d --- /dev/null +++ b/docs/used/SRP_UniversalRenderSceneSetupPolicyManagedOwnershipPlan_完成归档_2026-04-21.md @@ -0,0 +1,62 @@ +# SRP Universal Render Scene Setup Policy Managed Ownership Plan 2026-04-21 + +## Goal + +Move render-scene setup policy selection out of native `RenderPipeline` hardcoded defaults and into managed URP renderer-data ownership. + +This stage still keeps the actual `RenderSceneData` population native. +It only makes managed URP explicitly choose which native scene-setup policy asset key should be used. + +## Why This Stage + +The current SRP split already moved: + +1. renderer selection into managed URP; +2. builtin scene feature ownership into managed renderer features; +3. shadow planning policy into managed URP asset ownership; +4. shadow execution policy into managed URP renderer-data ownership; +5. shadow-caster standalone pass selection into managed URP renderer-data ownership. + +But `RenderPipeline::ConfigureRenderSceneData()` still directly hardcodes: + +1. global shader keyword setup; +2. environment / skybox setup. + +That means managed URP still cannot explicitly own how extracted scene data is finalized before the renderer consumes it. + +## Scope + +Included: + +1. add a native registry for render-scene setup policies; +2. add managed asset / renderer-data API for render-scene setup policy asset keys; +3. let Mono runtime resolve those keys from managed pipeline assets; +4. let `ScriptableRenderPipelineHost` use managed-selected render-scene setup policy keys; +5. make `UniversalRendererData` explicitly own the builtin render-scene setup policy key; +6. rebuild `XCEditor` and run old editor smoke. + +Not included: + +1. exposing mutable `RenderSceneData` editing directly to C#; +2. custom managed skybox authoring APIs; +3. custom global shader keyword authoring APIs; +4. changing render-scene extraction itself. + +## Acceptance + +This stage is complete when: + +1. managed URP explicitly owns render-scene setup policy selection; +2. native host no longer blindly applies the default render-scene setup for managed pipelines; +3. builtin scene setup still works for the default URP renderer; +4. `XCEditor` build and old editor smoke both pass. + +## Result + +Completed on 2026-04-21. + +Validation: + +1. `cmake --build . --config Debug --target XCEditor` passed; +2. old editor smoke passed with `editor/bin/Debug/XCEngine.exe`; +3. `editor/bin/Debug/editor.log` recorded `SceneReady` at `2026-04-21 03:07:29`. diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 8d7a722c..3d30d73e 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -96,6 +96,14 @@ public: (void)rendererIndex; return GetPipelineRendererAsset(); } + virtual std::string GetRenderSceneSetupPolicyAssetKey() const { + return {}; + } + virtual std::string GetRenderSceneSetupPolicyAssetKey( + int32_t rendererIndex) const { + (void)rendererIndex; + return GetRenderSceneSetupPolicyAssetKey(); + } virtual std::string GetCameraFrameStandalonePassAssetKey( CameraFrameStage stage) const { (void)stage; diff --git a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h index 347c98b8..760dcc7a 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h @@ -58,6 +58,9 @@ public: const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData) override; + void ConfigureRenderSceneData( + const CameraFramePlan& plan, + RenderSceneData& sceneData) const override; bool ConfigureDirectionalShadowExecutionState( const CameraFramePlan& plan, const DirectionalShadowSurfaceAllocation& shadowAllocation, diff --git a/engine/include/XCEngine/Rendering/RenderPipeline.h b/engine/include/XCEngine/Rendering/RenderPipeline.h index 618e5324..0879133b 100644 --- a/engine/include/XCEngine/Rendering/RenderPipeline.h +++ b/engine/include/XCEngine/Rendering/RenderPipeline.h @@ -29,6 +29,9 @@ void ApplyDefaultRenderPipelineDirectionalShadowExecutionPolicy( const CameraFramePlan& plan, const DirectionalShadowSurfaceAllocation& shadowAllocation, DirectionalShadowExecutionState& shadowState); +void ApplyDefaultRenderPipelineSceneSetupPolicy( + const CameraFramePlan& plan, + RenderSceneData& sceneData); struct RenderPipelineStageRenderGraphContext { RenderGraphBuilder& graphBuilder; diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp index c93e2b5e..7a15b6b0 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp @@ -1,6 +1,7 @@ #include "Rendering/Internal/RenderPipelineFactory.h" #include +#include #include "Rendering/Execution/DirectionalShadowExecutionState.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" @@ -46,6 +47,8 @@ using CameraFrameStandalonePassRegistry = std::unordered_map; using DirectionalShadowPlanningPolicyRegistry = std::unordered_map; +using RenderSceneSetupPolicyRegistry = + std::unordered_map; using DirectionalShadowExecutionPolicyRegistry = std::unordered_map; @@ -65,6 +68,12 @@ GetDirectionalShadowPlanningPolicyRegistry() { return registry; } +RenderSceneSetupPolicyRegistry& +GetRenderSceneSetupPolicyRegistry() { + static RenderSceneSetupPolicyRegistry registry = {}; + return registry; +} + DirectionalShadowExecutionPolicyRegistry& GetDirectionalShadowExecutionPolicyRegistry() { static DirectionalShadowExecutionPolicyRegistry registry = {}; @@ -86,6 +95,11 @@ std::unordered_set& GetBuiltinDirectionalShadowPlanningPolicyKeys() return builtinKeys; } +std::unordered_set& GetBuiltinRenderSceneSetupPolicyKeys() { + static std::unordered_set builtinKeys = {}; + return builtinKeys; +} + std::unordered_set& GetBuiltinDirectionalShadowExecutionPolicyKeys() { static std::unordered_set builtinKeys = {}; @@ -107,6 +121,11 @@ std::mutex& GetDirectionalShadowPlanningPolicyRegistryMutex() { return mutex; } +std::mutex& GetRenderSceneSetupPolicyRegistryMutex() { + static std::mutex mutex; + return mutex; +} + std::mutex& GetDirectionalShadowExecutionPolicyRegistryMutex() { static std::mutex mutex; return mutex; @@ -171,6 +190,25 @@ void EnsureBuiltinDirectionalShadowPlanningPolicyRegistryInitialized() { (void)initialized; } +void EnsureBuiltinRenderSceneSetupPolicyRegistryInitialized() { + static const bool initialized = []() { + RenderSceneSetupPolicyRegistry& registry = + GetRenderSceneSetupPolicyRegistry(); + registry.emplace( + "BuiltinDefaultSceneSetup", + [](const CameraFramePlan& plan, + RenderSceneData& sceneData) { + ApplyDefaultRenderPipelineSceneSetupPolicy( + plan, + sceneData); + }); + GetBuiltinRenderSceneSetupPolicyKeys().insert( + "BuiltinDefaultSceneSetup"); + return true; + }(); + (void)initialized; +} + void EnsureBuiltinDirectionalShadowExecutionPolicyRegistryInitialized() { static const bool initialized = []() { DirectionalShadowExecutionPolicyRegistry& registry = @@ -416,6 +454,72 @@ bool ApplyDirectionalShadowPlanningPolicyByKey( return true; } +bool RegisterRenderSceneSetupPolicy( + const std::string& key, + RenderSceneSetupPolicy policy) { + if (key.empty() || !policy) { + return false; + } + + EnsureBuiltinRenderSceneSetupPolicyRegistryInitialized(); + + std::lock_guard lock( + GetRenderSceneSetupPolicyRegistryMutex()); + RenderSceneSetupPolicyRegistry& registry = + GetRenderSceneSetupPolicyRegistry(); + if (registry.find(key) != registry.end()) { + return false; + } + + registry.emplace(key, std::move(policy)); + return true; +} + +bool UnregisterRenderSceneSetupPolicy( + const std::string& key) { + if (key.empty()) { + return false; + } + + EnsureBuiltinRenderSceneSetupPolicyRegistryInitialized(); + + std::lock_guard lock( + GetRenderSceneSetupPolicyRegistryMutex()); + if (GetBuiltinRenderSceneSetupPolicyKeys().find(key) != + GetBuiltinRenderSceneSetupPolicyKeys().end()) { + return false; + } + + RenderSceneSetupPolicyRegistry& registry = + GetRenderSceneSetupPolicyRegistry(); + return registry.erase(key) != 0u; +} + +bool ApplyRenderSceneSetupPolicyByKey( + const std::string& key, + const CameraFramePlan& plan, + RenderSceneData& sceneData) { + if (key.empty()) { + return false; + } + + EnsureBuiltinRenderSceneSetupPolicyRegistryInitialized(); + + std::lock_guard lock( + GetRenderSceneSetupPolicyRegistryMutex()); + const RenderSceneSetupPolicyRegistry& registry = + GetRenderSceneSetupPolicyRegistry(); + const auto it = registry.find(key); + if (it == registry.end() || !it->second) { + return false; + } + + it->second( + plan, + sceneData); + return true; +} + bool RegisterDirectionalShadowExecutionPolicy( const std::string& key, DirectionalShadowExecutionPolicy policy) { diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.h b/engine/src/Rendering/Internal/RenderPipelineFactory.h index 1e273173..fe6c744a 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.h +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.h @@ -13,6 +13,7 @@ class RenderPipelineAsset; class RenderPass; struct CameraRenderRequest; struct CameraFramePlan; +struct RenderSceneData; struct DirectionalShadowPlanningSettings; struct DirectionalShadowExecutionState; struct DirectionalShadowSurfaceAllocation; @@ -29,6 +30,10 @@ using DirectionalShadowPlanningPolicy = size_t, size_t, const DirectionalShadowPlanningSettings&)>; +using RenderSceneSetupPolicy = + std::function; using DirectionalShadowExecutionPolicy = std::functionRender(context, surface, sceneData); } +void ScriptableRenderPipelineHost::ConfigureRenderSceneData( + const CameraFramePlan& plan, + RenderSceneData& sceneData) const { + if (m_managedAssetRuntime == nullptr) { + RenderPipeline::ConfigureRenderSceneData( + plan, + sceneData); + return; + } + + const std::string assetKey = + m_managedAssetRuntime + ->GetRenderSceneSetupPolicyAssetKey( + plan.request.rendererIndex); + if (assetKey.empty()) { + RenderPipeline::ConfigureRenderSceneData( + plan, + sceneData); + return; + } + + if (!Rendering::Internal::ApplyRenderSceneSetupPolicyByKey( + assetKey, + plan, + sceneData)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + Containers::String( + "ScriptableRenderPipelineHost failed to resolve render scene setup policy asset key: ") + + assetKey.c_str()); + RenderPipeline::ConfigureRenderSceneData( + plan, + sceneData); + } +} + bool ScriptableRenderPipelineHost::ConfigureDirectionalShadowExecutionState( const CameraFramePlan& plan, const DirectionalShadowSurfaceAllocation& shadowAllocation, diff --git a/engine/src/Rendering/RenderPipeline.cpp b/engine/src/Rendering/RenderPipeline.cpp index f3b1e326..ef384fab 100644 --- a/engine/src/Rendering/RenderPipeline.cpp +++ b/engine/src/Rendering/RenderPipeline.cpp @@ -52,6 +52,14 @@ RenderEnvironmentData BuildDefaultEnvironmentData(const CameraFramePlan& plan) { void RenderPipeline::ConfigureRenderSceneData( const CameraFramePlan& plan, RenderSceneData& sceneData) const { + ApplyDefaultRenderPipelineSceneSetupPolicy( + plan, + sceneData); +} + +void ApplyDefaultRenderPipelineSceneSetupPolicy( + const CameraFramePlan& plan, + RenderSceneData& sceneData) { sceneData.globalShaderKeywords = BuildDefaultSceneGlobalShaderKeywords(sceneData); sceneData.environment = diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 16709ec7..41288100 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -1319,6 +1319,10 @@ public: GetPipelineRendererAsset() const override; std::shared_ptr GetPipelineRendererAsset(int32_t rendererIndex) const override; + std::string GetRenderSceneSetupPolicyAssetKey() const + override; + std::string GetRenderSceneSetupPolicyAssetKey( + int32_t rendererIndex) const override; std::string GetDirectionalShadowPlanningPolicyAssetKey() const override; std::string GetDirectionalShadowExecutionPolicyAssetKey() const @@ -1364,6 +1368,10 @@ private: MonoObject* assetObject) const; MonoMethod* ResolveGetPipelineRendererAssetKeyContextualMethod( MonoObject* assetObject) const; + MonoMethod* ResolveGetRenderSceneSetupPolicyAssetKeyMethod( + MonoObject* assetObject) const; + MonoMethod* ResolveGetRenderSceneSetupPolicyAssetKeyContextualMethod( + MonoObject* assetObject) const; MonoMethod* ResolveGetDirectionalShadowPlanningPolicyAssetKeyMethod( MonoObject* assetObject) const; MonoMethod* ResolveGetDirectionalShadowExecutionPolicyAssetKeyMethod( @@ -1392,6 +1400,11 @@ private: mutable MonoMethod* m_getPipelineRendererAssetKeyMethod = nullptr; mutable MonoMethod* m_getPipelineRendererAssetKeyContextualMethod = nullptr; + mutable MonoMethod* m_getRenderSceneSetupPolicyAssetKeyMethod = + nullptr; + mutable MonoMethod* + m_getRenderSceneSetupPolicyAssetKeyContextualMethod = + nullptr; mutable MonoMethod* m_getDirectionalShadowPlanningPolicyAssetKeyMethod = nullptr; mutable MonoMethod* @@ -2050,6 +2063,61 @@ MonoManagedRenderPipelineAssetRuntime::GetPipelineRendererAsset( return m_pipelineRendererAsset; } +std::string MonoManagedRenderPipelineAssetRuntime:: + GetRenderSceneSetupPolicyAssetKey() const { + return GetRenderSceneSetupPolicyAssetKey(-1); +} + +std::string MonoManagedRenderPipelineAssetRuntime:: + GetRenderSceneSetupPolicyAssetKey( + int32_t rendererIndex) const { + if (!SyncManagedAssetRuntimeState()) { + return {}; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + if (assetObject == nullptr) { + return {}; + } + + MonoMethod* const contextualMethod = + ResolveGetRenderSceneSetupPolicyAssetKeyContextualMethod( + assetObject); + if (contextualMethod != nullptr) { + void* args[1] = { &rendererIndex }; + MonoObject* managedKeyObject = nullptr; + if (!m_runtime->InvokeManagedMethod( + assetObject, + contextualMethod, + args, + &managedKeyObject)) { + return {}; + } + + return MonoStringToUtf8( + reinterpret_cast(managedKeyObject)); + } + + MonoMethod* const method = + ResolveGetRenderSceneSetupPolicyAssetKeyMethod( + assetObject); + if (method == nullptr) { + return {}; + } + + MonoObject* managedKeyObject = nullptr; + if (!m_runtime->InvokeManagedMethod( + assetObject, + method, + nullptr, + &managedKeyObject)) { + return {}; + } + + return MonoStringToUtf8( + reinterpret_cast(managedKeyObject)); +} + std::string MonoManagedRenderPipelineAssetRuntime:: GetDirectionalShadowPlanningPolicyAssetKey() const { if (!SyncManagedAssetRuntimeState()) { @@ -2409,6 +2477,8 @@ void MonoManagedRenderPipelineAssetRuntime::ReleaseManagedAsset() const { m_getRuntimeResourceVersionMethod = nullptr; m_getPipelineRendererAssetKeyMethod = nullptr; m_getPipelineRendererAssetKeyContextualMethod = nullptr; + m_getRenderSceneSetupPolicyAssetKeyMethod = nullptr; + m_getRenderSceneSetupPolicyAssetKeyContextualMethod = nullptr; m_getDirectionalShadowPlanningPolicyAssetKeyMethod = nullptr; m_getDirectionalShadowExecutionPolicyAssetKeyMethod = nullptr; m_getDirectionalShadowExecutionPolicyAssetKeyContextualMethod = @@ -2563,6 +2633,37 @@ MonoManagedRenderPipelineAssetRuntime::ResolveGetPipelineRendererAssetKeyContext return m_getPipelineRendererAssetKeyContextualMethod; } +MonoMethod* +MonoManagedRenderPipelineAssetRuntime:: + ResolveGetRenderSceneSetupPolicyAssetKeyMethod( + MonoObject* assetObject) const { + if (m_getRenderSceneSetupPolicyAssetKeyMethod == nullptr) { + m_getRenderSceneSetupPolicyAssetKeyMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "GetRenderSceneSetupPolicyAssetKey", + 0); + } + + return m_getRenderSceneSetupPolicyAssetKeyMethod; +} + +MonoMethod* +MonoManagedRenderPipelineAssetRuntime:: + ResolveGetRenderSceneSetupPolicyAssetKeyContextualMethod( + MonoObject* assetObject) const { + if (m_getRenderSceneSetupPolicyAssetKeyContextualMethod == + nullptr) { + m_getRenderSceneSetupPolicyAssetKeyContextualMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "GetRenderSceneSetupPolicyAssetKeyContextual", + 1); + } + + return m_getRenderSceneSetupPolicyAssetKeyContextualMethod; +} + MonoMethod* MonoManagedRenderPipelineAssetRuntime:: ResolveGetDirectionalShadowPlanningPolicyAssetKeyMethod( diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs index 9dff0ca0..2de709e7 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs @@ -78,6 +78,28 @@ namespace XCEngine.Rendering.Universal : string.Empty; } + protected override string + GetRenderSceneSetupPolicyAssetKeyContextual( + int rendererIndex) + { + ScriptableRendererData resolvedRendererData = + GetRendererData(rendererIndex); + if (resolvedRendererData != null) + { + string assetKey = + resolvedRendererData + .GetRenderSceneSetupPolicyAssetKeyInstance(); + if (!string.IsNullOrEmpty(assetKey)) + { + return assetKey; + } + } + + return base + .GetRenderSceneSetupPolicyAssetKeyContextual( + rendererIndex); + } + protected override string GetCameraFrameStandalonePassAssetKeyContextual( CameraFrameStage stage, diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs index adfe8e4c..0ed5ea9e 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs @@ -61,6 +61,11 @@ namespace XCEngine.Rendering.Universal return GetPipelineRendererAssetKey(); } + internal string GetRenderSceneSetupPolicyAssetKeyInstance() + { + return GetRenderSceneSetupPolicyAssetKey(); + } + internal string GetCameraFrameStandalonePassAssetKeyInstance( CameraFrameStage stage) { @@ -166,6 +171,11 @@ namespace XCEngine.Rendering.Universal return string.Empty; } + protected virtual string GetRenderSceneSetupPolicyAssetKey() + { + return string.Empty; + } + protected virtual string GetCameraFrameStandalonePassAssetKey( CameraFrameStage stage) { diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs index a5f7ffc0..f12cbe06 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs @@ -32,6 +32,11 @@ namespace XCEngine.Rendering.Universal return "BuiltinForward"; } + protected override string GetRenderSceneSetupPolicyAssetKey() + { + return "BuiltinDefaultSceneSetup"; + } + protected override string GetCameraFrameStandalonePassAssetKey( CameraFrameStage stage) { diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs index f1945d27..e9f84c8f 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs @@ -58,6 +58,18 @@ namespace XCEngine.Rendering return GetPipelineRendererAssetKey(); } + protected virtual string GetRenderSceneSetupPolicyAssetKey() + { + return string.Empty; + } + + protected virtual string + GetRenderSceneSetupPolicyAssetKeyContextual( + int rendererIndex) + { + return GetRenderSceneSetupPolicyAssetKey(); + } + protected virtual string GetCameraFrameStandalonePassAssetKey( CameraFrameStage stage) {