diff --git a/docs/used/SRP_URP_ScriptableObjectRenderAssetPlan_2026-04-21_完成归档.md b/docs/used/SRP_URP_ScriptableObjectRenderAssetPlan_2026-04-21_完成归档.md new file mode 100644 index 00000000..0aec5aed --- /dev/null +++ b/docs/used/SRP_URP_ScriptableObjectRenderAssetPlan_2026-04-21_完成归档.md @@ -0,0 +1,95 @@ +# SRP URP ScriptableObject Render Asset Plan + +Date: 2026-04-21 + +## Background + +The recent SRP stages already moved the managed URP side closer to Unity: + +- native backend responsibilities were narrowed +- renderer feature lifecycle invalidation was stabilized +- renderer feature runtime-state synchronization was added +- renderer feature ownership was collapsed onto configured + `rendererFeatures` + +The next architectural mismatch is now more fundamental: + +- render pipeline assets, renderer data, and renderer features still sit + directly on the generic managed `Object` base +- there is no `ScriptableObject` substrate for non-scene managed authoring + objects + +That means the SRP/URP API shape still lacks the exact kind of managed asset +identity Unity uses for: + +- `RenderPipelineAsset` +- `ScriptableRendererData` +- `ScriptableRendererFeature` + +If the engine is meant to converge toward Unity-style SRP/URP authoring in C#, +this substrate needs to exist before the next layer of asset/editor work. + +## Goal + +Introduce a minimal managed `ScriptableObject` base and move the SRP/URP render +asset types onto it: + +- add `ScriptableObject : Object` +- make `RenderPipelineAsset` inherit from `ScriptableObject` +- make `ScriptableRendererData` inherit from `ScriptableObject` +- make `ScriptableRendererFeature` inherit from `ScriptableObject` + +The scope of this stage is intentionally small: + +- provide the inheritance substrate +- keep runtime behavior unchanged +- avoid pulling in editor serialization or native asset authoring work yet + +## Why Now + +Without this step, the managed SRP model keeps carrying a structural mismatch: + +- render assets look like plain objects instead of authored managed assets +- future editor integration would need to retrofit the base type later +- Unity-style mental model remains fuzzy for users extending URP in C# + +Doing this now keeps the next SRP stages building on the right object model +instead of stacking more rendering behavior on top of a temporary substrate. + +## Scope + +Included: + +- `managed/XCEngine.ScriptCore/ScriptableObject.cs` +- `managed/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs` +- `managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs` +- `managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs` +- managed build manifests / source lists if needed +- lightweight API probe updates if needed + +Not included: + +- editor inspector integration +- asset database / serialization work +- native object persistence changes +- deferred rendering or renderer feature behavior changes + +## Implementation Plan + +1. Add a minimal managed `ScriptableObject` base +2. Move render asset types to inherit from it +3. Add `CreateInstance()` style helper only if it is needed cleanly now +4. Update managed source registration if required +5. Rebuild `XCEditor` +6. Run old editor smoke for at least 10 seconds and verify a fresh + `SceneReady` +7. Archive the plan, commit, and push + +## Expected Result + +After this stage: + +- SRP/URP render assets sit on a Unity-like managed asset base type +- the managed rendering API shape is closer to Unity's authoring model +- future renderer-data / feature authoring work can continue on a cleaner + substrate diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 341f1b98..44a23b55 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -158,6 +158,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Matrix4x4.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Object.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableObject.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Physics.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/PhysicsBodyType.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Quaternion.cs diff --git a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs index a228c848..1cec1a85 100644 --- a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs +++ b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs @@ -44,6 +44,9 @@ namespace Gameplay public bool HasPublicPipelineAssetConfigureCameraFramePlan; public bool HasPipelineAssetSetDirty; public bool HasPipelineAssetGetRuntimeResourceVersion; + public bool HasScriptableObjectType; + public bool HasScriptableObjectCreateInstance; + public bool HasRenderPipelineAssetScriptableObjectBase; public bool HasPlanningContextType; public bool HasRendererFeatureConfigureCameraFramePlan; public bool HasRendererRecordingContextType; @@ -51,6 +54,8 @@ namespace Gameplay public bool HasRendererBackedRenderPipelineAssetType; public bool HasRendererBackedRenderPipelineType; public bool HasRendererDrivenRenderPipelineType; + public bool HasRendererDataScriptableObjectBase; + public bool HasRendererFeatureScriptableObjectBase; public bool HasRendererDataSetupRenderer; public bool HasRendererDataSetDirty; public bool HasRendererDataIsInvalidated; @@ -93,6 +98,9 @@ namespace Gameplay typeof(CameraRenderRequestContext); System.Type pipelineAssetType = typeof(ScriptableRenderPipelineAsset); + System.Type scriptableObjectType = + typeof(Object).Assembly.GetType( + "XCEngine.ScriptableObject"); System.Type rasterPassBuilderType = typeof(RenderGraphRasterPassBuilder); System.Type rendererFeatureType = @@ -288,6 +296,17 @@ namespace Gameplay "GetRuntimeResourceVersionInstance", BindingFlags.Instance | BindingFlags.NonPublic) != null; + HasScriptableObjectType = + scriptableObjectType != null; + HasScriptableObjectCreateInstance = + scriptableObjectType != null && + scriptableObjectType.GetMethod( + "CreateInstance", + BindingFlags.Static | + BindingFlags.Public) != null; + HasRenderPipelineAssetScriptableObjectBase = + scriptableObjectType != null && + pipelineAssetType.BaseType == scriptableObjectType; HasPlanningContextType = contextType.Assembly.GetType( "XCEngine.Rendering.ScriptableRenderPipelinePlanningContext") != null; @@ -312,6 +331,12 @@ namespace Gameplay HasRendererDrivenRenderPipelineType = System.Type.GetType( "XCEngine.Rendering.Universal.RendererDrivenRenderPipeline, XCEngine.RenderPipelines.Universal") != null; + HasRendererDataScriptableObjectBase = + scriptableObjectType != null && + rendererDataType.BaseType == scriptableObjectType; + HasRendererFeatureScriptableObjectBase = + scriptableObjectType != null && + rendererFeatureType.BaseType == scriptableObjectType; HasRendererDataSetupRenderer = rendererDataType.GetMethod( "SetupRenderer", diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs index 8136d298..72339ee7 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs @@ -5,7 +5,8 @@ using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { - public abstract class ScriptableRendererData : Object + public abstract class ScriptableRendererData + : ScriptableObject { public ScriptableRendererFeature[] rendererFeatures; private ScriptableRendererFeature[] m_rendererFeatures; diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs index cfa6120b..120a83f8 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs @@ -4,7 +4,7 @@ using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { public abstract class ScriptableRendererFeature - : Object + : ScriptableObject { private bool m_disposed; private bool m_runtimeCreated; diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs b/managed/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs index 72539ed9..22028689 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs @@ -2,7 +2,8 @@ using XCEngine; namespace XCEngine.Rendering { - public abstract class RenderPipelineAsset : Object + public abstract class RenderPipelineAsset + : ScriptableObject { protected RenderPipelineAsset() { diff --git a/managed/XCEngine.ScriptCore/ScriptableObject.cs b/managed/XCEngine.ScriptCore/ScriptableObject.cs new file mode 100644 index 00000000..e3c7b599 --- /dev/null +++ b/managed/XCEngine.ScriptCore/ScriptableObject.cs @@ -0,0 +1,45 @@ +using System; +using System.Reflection; + +namespace XCEngine +{ + public abstract class ScriptableObject : Object + { + protected ScriptableObject() + { + } + + public static T CreateInstance() + where T : ScriptableObject + { + return CreateInstance(typeof(T)) as T; + } + + public static ScriptableObject CreateInstance( + Type scriptableObjectType) + { + if (scriptableObjectType == null || + !typeof(ScriptableObject) + .IsAssignableFrom(scriptableObjectType)) + { + return null; + } + + try + { + return Activator.CreateInstance( + scriptableObjectType, + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic, + binder: null, + args: Array.Empty(), + culture: null) as ScriptableObject; + } + catch + { + return null; + } + } + } +}