diff --git a/docs/used/SRP_UniversalCameraFramePlanPolicyManagedOwnershipPlan_完成归档_2026-04-21.md b/docs/used/SRP_UniversalCameraFramePlanPolicyManagedOwnershipPlan_完成归档_2026-04-21.md new file mode 100644 index 00000000..7279c0d4 --- /dev/null +++ b/docs/used/SRP_UniversalCameraFramePlanPolicyManagedOwnershipPlan_完成归档_2026-04-21.md @@ -0,0 +1,66 @@ +# SRP Universal Camera Frame Plan Policy Managed Ownership Plan 2026-04-21 + +## Goal + +Move default camera-frame planning policy selection out of native `RenderPipelineAsset` hardcoded flow and into managed URP ownership. + +This stage still keeps the actual frame-plan mutation native. +It only makes managed URP explicitly choose which native camera-frame plan policy asset key should be used. + +## Why This Stage + +The current SRP split already moved: + +1. shadow planning policy into managed URP asset ownership; +2. shadow execution policy into managed URP renderer-data ownership; +3. render-scene setup policy into managed URP renderer-data ownership; +4. shadow-caster standalone stage selection into managed URP renderer-data ownership. + +But `RenderPipelineAsset::ConfigureCameraFramePlan()` still directly hardcodes: + +1. default final-color policy resolution; +2. default fullscreen stage planning for post-process and final-output. + +Managed URP can currently override pieces afterward, but the native layer still decides the default path first. +That is not the final SRP ownership shape. + +## Scope + +Included: + +1. add a native registry for camera-frame plan policies; +2. add managed asset / renderer-data API for camera-frame plan policy asset keys; +3. let Mono runtime resolve those keys from managed pipeline assets; +4. let `ManagedScriptableRenderPipelineAsset` use managed-selected frame-plan policy keys before invoking managed frame-plan configuration; +5. make `UniversalRendererData` explicitly own the builtin camera-frame plan policy key; +6. rebuild `XCEditor` and run old editor smoke. + +Not included: + +1. exposing raw `ResolvedFinalColorPolicy` editing directly to C#; +2. moving fullscreen-stage planning logic into managed code; +3. moving camera-frame validation into managed code; +4. post-process pass authoring UI. + +## Acceptance + +This stage is complete when: + +1. managed URP explicitly owns camera-frame default policy selection; +2. native default camera-frame plan policy is no longer blindly hardcoded for managed pipeline assets; +3. builtin URP still gets the same default final-color and fullscreen-stage behavior; +4. `XCEditor` build and old editor smoke both pass. + +## Result + +Completed. + +Managed URP renderer data now selects the native camera-frame plan policy key through the same `managed key -> native registry -> builtin policy` ownership path already used by the earlier SRP stages. + +`UniversalRendererData` explicitly selects builtin key `"BuiltinDefaultCameraFramePlan"`, while native still owns the concrete default frame-plan mutation logic for final-color resolution and fullscreen-stage defaults. + +## Validation + +1. `cmake --build . --config Debug --target XCEditor` +2. launched `editor/bin/Debug/XCEngine.exe` for about 12 seconds +3. verified `editor/bin/Debug/editor.log` contains new `SceneReady` at `2026-04-21 12:20:32` diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 3d30d73e..dcb9627b 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 GetCameraFramePlanPolicyAssetKey() const { + return {}; + } + virtual std::string GetCameraFramePlanPolicyAssetKey( + int32_t rendererIndex) const { + (void)rendererIndex; + return GetCameraFramePlanPolicyAssetKey(); + } virtual std::string GetRenderSceneSetupPolicyAssetKey() const { return {}; } diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp index 7a15b6b0..b906ce84 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp @@ -47,6 +47,8 @@ using CameraFrameStandalonePassRegistry = std::unordered_map; using DirectionalShadowPlanningPolicyRegistry = std::unordered_map; +using CameraFramePlanPolicyRegistry = + std::unordered_map; using RenderSceneSetupPolicyRegistry = std::unordered_map; using DirectionalShadowExecutionPolicyRegistry = @@ -68,6 +70,12 @@ GetDirectionalShadowPlanningPolicyRegistry() { return registry; } +CameraFramePlanPolicyRegistry& +GetCameraFramePlanPolicyRegistry() { + static CameraFramePlanPolicyRegistry registry = {}; + return registry; +} + RenderSceneSetupPolicyRegistry& GetRenderSceneSetupPolicyRegistry() { static RenderSceneSetupPolicyRegistry registry = {}; @@ -95,6 +103,11 @@ std::unordered_set& GetBuiltinDirectionalShadowPlanningPolicyKeys() return builtinKeys; } +std::unordered_set& GetBuiltinCameraFramePlanPolicyKeys() { + static std::unordered_set builtinKeys = {}; + return builtinKeys; +} + std::unordered_set& GetBuiltinRenderSceneSetupPolicyKeys() { static std::unordered_set builtinKeys = {}; return builtinKeys; @@ -121,6 +134,11 @@ std::mutex& GetDirectionalShadowPlanningPolicyRegistryMutex() { return mutex; } +std::mutex& GetCameraFramePlanPolicyRegistryMutex() { + static std::mutex mutex; + return mutex; +} + std::mutex& GetRenderSceneSetupPolicyRegistryMutex() { static std::mutex mutex; return mutex; @@ -190,6 +208,25 @@ void EnsureBuiltinDirectionalShadowPlanningPolicyRegistryInitialized() { (void)initialized; } +void EnsureBuiltinCameraFramePlanPolicyRegistryInitialized() { + static const bool initialized = []() { + CameraFramePlanPolicyRegistry& registry = + GetCameraFramePlanPolicyRegistry(); + registry.emplace( + "BuiltinDefaultCameraFramePlan", + [](CameraFramePlan& plan, + const FinalColorSettings& defaultFinalColorSettings) { + ApplyDefaultRenderPipelineAssetCameraFramePlanPolicy( + plan, + defaultFinalColorSettings); + }); + GetBuiltinCameraFramePlanPolicyKeys().insert( + "BuiltinDefaultCameraFramePlan"); + return true; + }(); + (void)initialized; +} + void EnsureBuiltinRenderSceneSetupPolicyRegistryInitialized() { static const bool initialized = []() { RenderSceneSetupPolicyRegistry& registry = @@ -454,6 +491,72 @@ bool ApplyDirectionalShadowPlanningPolicyByKey( return true; } +bool RegisterCameraFramePlanPolicy( + const std::string& key, + CameraFramePlanPolicy policy) { + if (key.empty() || !policy) { + return false; + } + + EnsureBuiltinCameraFramePlanPolicyRegistryInitialized(); + + std::lock_guard lock( + GetCameraFramePlanPolicyRegistryMutex()); + CameraFramePlanPolicyRegistry& registry = + GetCameraFramePlanPolicyRegistry(); + if (registry.find(key) != registry.end()) { + return false; + } + + registry.emplace(key, std::move(policy)); + return true; +} + +bool UnregisterCameraFramePlanPolicy( + const std::string& key) { + if (key.empty()) { + return false; + } + + EnsureBuiltinCameraFramePlanPolicyRegistryInitialized(); + + std::lock_guard lock( + GetCameraFramePlanPolicyRegistryMutex()); + if (GetBuiltinCameraFramePlanPolicyKeys().find(key) != + GetBuiltinCameraFramePlanPolicyKeys().end()) { + return false; + } + + CameraFramePlanPolicyRegistry& registry = + GetCameraFramePlanPolicyRegistry(); + return registry.erase(key) != 0u; +} + +bool ApplyCameraFramePlanPolicyByKey( + const std::string& key, + CameraFramePlan& plan, + const FinalColorSettings& defaultFinalColorSettings) { + if (key.empty()) { + return false; + } + + EnsureBuiltinCameraFramePlanPolicyRegistryInitialized(); + + std::lock_guard lock( + GetCameraFramePlanPolicyRegistryMutex()); + const CameraFramePlanPolicyRegistry& registry = + GetCameraFramePlanPolicyRegistry(); + const auto it = registry.find(key); + if (it == registry.end() || !it->second) { + return false; + } + + it->second( + plan, + defaultFinalColorSettings); + return true; +} + bool RegisterRenderSceneSetupPolicy( const std::string& key, RenderSceneSetupPolicy policy) { diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.h b/engine/src/Rendering/Internal/RenderPipelineFactory.h index fe6c744a..f453dabb 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 FinalColorSettings; struct RenderSceneData; struct DirectionalShadowPlanningSettings; struct DirectionalShadowExecutionState; @@ -30,6 +31,10 @@ using DirectionalShadowPlanningPolicy = size_t, size_t, const DirectionalShadowPlanningSettings&)>; +using CameraFramePlanPolicy = + std::function; using RenderSceneSetupPolicy = std::function runtime = ResolveManagedAssetRuntime(); runtime != nullptr) { + ApplyManagedCameraFramePlanPolicyOrDefault( + runtime->GetCameraFramePlanPolicyAssetKey( + plan.request.rendererIndex), + plan, + GetDefaultFinalColorSettings()); runtime->ConfigureCameraFramePlan(plan); + return; } + + RenderPipelineAsset::ConfigureCameraFramePlan(plan); } FinalColorSettings ManagedScriptableRenderPipelineAsset::GetDefaultFinalColorSettings() const { diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 41288100..f3bd9e7e 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 GetCameraFramePlanPolicyAssetKey() const + override; + std::string GetCameraFramePlanPolicyAssetKey( + int32_t rendererIndex) const override; std::string GetRenderSceneSetupPolicyAssetKey() const override; std::string GetRenderSceneSetupPolicyAssetKey( @@ -1368,6 +1372,10 @@ private: MonoObject* assetObject) const; MonoMethod* ResolveGetPipelineRendererAssetKeyContextualMethod( MonoObject* assetObject) const; + MonoMethod* ResolveGetCameraFramePlanPolicyAssetKeyMethod( + MonoObject* assetObject) const; + MonoMethod* ResolveGetCameraFramePlanPolicyAssetKeyContextualMethod( + MonoObject* assetObject) const; MonoMethod* ResolveGetRenderSceneSetupPolicyAssetKeyMethod( MonoObject* assetObject) const; MonoMethod* ResolveGetRenderSceneSetupPolicyAssetKeyContextualMethod( @@ -1400,6 +1408,11 @@ private: mutable MonoMethod* m_getPipelineRendererAssetKeyMethod = nullptr; mutable MonoMethod* m_getPipelineRendererAssetKeyContextualMethod = nullptr; + mutable MonoMethod* m_getCameraFramePlanPolicyAssetKeyMethod = + nullptr; + mutable MonoMethod* + m_getCameraFramePlanPolicyAssetKeyContextualMethod = + nullptr; mutable MonoMethod* m_getRenderSceneSetupPolicyAssetKeyMethod = nullptr; mutable MonoMethod* @@ -2063,6 +2076,61 @@ MonoManagedRenderPipelineAssetRuntime::GetPipelineRendererAsset( return m_pipelineRendererAsset; } +std::string MonoManagedRenderPipelineAssetRuntime:: + GetCameraFramePlanPolicyAssetKey() const { + return GetCameraFramePlanPolicyAssetKey(-1); +} + +std::string MonoManagedRenderPipelineAssetRuntime:: + GetCameraFramePlanPolicyAssetKey( + int32_t rendererIndex) const { + if (!SyncManagedAssetRuntimeState()) { + return {}; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + if (assetObject == nullptr) { + return {}; + } + + MonoMethod* const contextualMethod = + ResolveGetCameraFramePlanPolicyAssetKeyContextualMethod( + 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 = + ResolveGetCameraFramePlanPolicyAssetKeyMethod( + 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:: GetRenderSceneSetupPolicyAssetKey() const { return GetRenderSceneSetupPolicyAssetKey(-1); @@ -2477,6 +2545,8 @@ void MonoManagedRenderPipelineAssetRuntime::ReleaseManagedAsset() const { m_getRuntimeResourceVersionMethod = nullptr; m_getPipelineRendererAssetKeyMethod = nullptr; m_getPipelineRendererAssetKeyContextualMethod = nullptr; + m_getCameraFramePlanPolicyAssetKeyMethod = nullptr; + m_getCameraFramePlanPolicyAssetKeyContextualMethod = nullptr; m_getRenderSceneSetupPolicyAssetKeyMethod = nullptr; m_getRenderSceneSetupPolicyAssetKeyContextualMethod = nullptr; m_getDirectionalShadowPlanningPolicyAssetKeyMethod = nullptr; @@ -2633,6 +2703,37 @@ MonoManagedRenderPipelineAssetRuntime::ResolveGetPipelineRendererAssetKeyContext return m_getPipelineRendererAssetKeyContextualMethod; } +MonoMethod* +MonoManagedRenderPipelineAssetRuntime:: + ResolveGetCameraFramePlanPolicyAssetKeyMethod( + MonoObject* assetObject) const { + if (m_getCameraFramePlanPolicyAssetKeyMethod == nullptr) { + m_getCameraFramePlanPolicyAssetKeyMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "GetCameraFramePlanPolicyAssetKey", + 0); + } + + return m_getCameraFramePlanPolicyAssetKeyMethod; +} + +MonoMethod* +MonoManagedRenderPipelineAssetRuntime:: + ResolveGetCameraFramePlanPolicyAssetKeyContextualMethod( + MonoObject* assetObject) const { + if (m_getCameraFramePlanPolicyAssetKeyContextualMethod == + nullptr) { + m_getCameraFramePlanPolicyAssetKeyContextualMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "GetCameraFramePlanPolicyAssetKeyContextual", + 1); + } + + return m_getCameraFramePlanPolicyAssetKeyContextualMethod; +} + MonoMethod* MonoManagedRenderPipelineAssetRuntime:: ResolveGetRenderSceneSetupPolicyAssetKeyMethod( diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs index 2de709e7..262208d9 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs @@ -78,6 +78,26 @@ namespace XCEngine.Rendering.Universal : string.Empty; } + protected override string GetCameraFramePlanPolicyAssetKeyContextual( + int rendererIndex) + { + ScriptableRendererData resolvedRendererData = + GetRendererData(rendererIndex); + if (resolvedRendererData != null) + { + string assetKey = + resolvedRendererData + .GetCameraFramePlanPolicyAssetKeyInstance(); + if (!string.IsNullOrEmpty(assetKey)) + { + return assetKey; + } + } + + return base.GetCameraFramePlanPolicyAssetKeyContextual( + rendererIndex); + } + protected override string GetRenderSceneSetupPolicyAssetKeyContextual( int rendererIndex) diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs index 0ed5ea9e..1cb908b7 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 GetCameraFramePlanPolicyAssetKeyInstance() + { + return GetCameraFramePlanPolicyAssetKey(); + } + internal string GetRenderSceneSetupPolicyAssetKeyInstance() { return GetRenderSceneSetupPolicyAssetKey(); @@ -171,6 +176,11 @@ namespace XCEngine.Rendering.Universal return string.Empty; } + protected virtual string GetCameraFramePlanPolicyAssetKey() + { + return string.Empty; + } + protected virtual string GetRenderSceneSetupPolicyAssetKey() { return string.Empty; diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs index f12cbe06..c3e77d88 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 GetCameraFramePlanPolicyAssetKey() + { + return "BuiltinDefaultCameraFramePlan"; + } + protected override string GetRenderSceneSetupPolicyAssetKey() { return "BuiltinDefaultSceneSetup"; diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs index e9f84c8f..53148e43 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs @@ -58,6 +58,17 @@ namespace XCEngine.Rendering return GetPipelineRendererAssetKey(); } + protected virtual string GetCameraFramePlanPolicyAssetKey() + { + return string.Empty; + } + + protected virtual string GetCameraFramePlanPolicyAssetKeyContextual( + int rendererIndex) + { + return GetCameraFramePlanPolicyAssetKey(); + } + protected virtual string GetRenderSceneSetupPolicyAssetKey() { return string.Empty;