diff --git a/docs/used/SRP_URP_RendererDataCollectionPlan_2026-04-22_完成归档.md b/docs/used/SRP_URP_RendererDataCollectionPlan_2026-04-22_完成归档.md new file mode 100644 index 00000000..adbeecb6 --- /dev/null +++ b/docs/used/SRP_URP_RendererDataCollectionPlan_2026-04-22_完成归档.md @@ -0,0 +1,64 @@ +# SRP / URP Renderer Data Collection Plan + +时间:2026-04-22 + +## 背景 + +上一阶段已经把 `ScriptableRendererData` 里的 `rendererFeatures` 生命周期收成了 +`ScriptableRendererFeatureCollection`,但 pipeline asset 这一层还有同类问题: + +1. `RendererBackedRenderPipelineAsset` 直接持有并管理 `rendererDataList/defaultRendererIndex` +2. renderer data 的默认创建、fallback 解析、重复引用释放、runtime hash 都散落在 asset 类里 +3. 这会让 asset 同时承担 authoring 表达和 runtime collection 管理两类职责 + +这和上一阶段收 `rendererFeatures` 之前的状态很像,也不利于后续继续往 Unity 风格的 +多个 renderer variant 演进。 + +## 目标 + +把 asset 层的 renderer data 管理正式收成一个 collection 边界。 + +阶段完成后要达到: + +1. `RendererBackedRenderPipelineAsset` 不再散写 renderer data 列表生命周期细节 +2. 默认 renderer data 创建、index fallback、runtime hash、release 都有统一落点 +3. 不改变现有外部 authoring 入口,仍然保留 `rendererDataList/defaultRendererIndex` +4. 为后续继续做 Unity 风格的 renderer variant / renderer profile 留出干净接缝 + +## 范围 + +本阶段只处理 renderer data collection 收口,不做: + +- deferred renderer +- editor inspector / 面板 +- public API 大改 +- 阴影或 post-process 功能扩展 + +## 实施步骤 + +### 1. 新增 ScriptableRendererDataCollection + +职责: + +- 统一解析 `rendererDataList` +- 统一保证默认 renderer data 存在 +- 统一处理 `defaultRendererIndex` fallback +- 统一计算 collection runtime hash +- 统一释放 renderer data runtime 资源 + +### 2. 接入 RendererBackedRenderPipelineAsset + +迁移内容: + +- renderer data 解析 +- renderer 选择 +- runtime version 同步 +- release 流程 + +## 完成标准 + +满足以下条件才算本阶段收口: + +1. `RendererBackedRenderPipelineAsset` 不再自己散写 renderer data collection 细节 +2. `XCEditor` 编译通过 +3. old editor 冒烟至少 10 秒通过 diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index cf436ea2..eb9e7d83 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -251,6 +251,7 @@ set(XCENGINE_RENDER_PIPELINES_UNIVERSAL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderPass.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererDataCollection.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeatureCollection.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/StageColorData.cs diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs index ea3e4255..4843b0ff 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using XCEngine; using XCEngine.Rendering; @@ -11,11 +10,15 @@ namespace XCEngine.Rendering.Universal public ScriptableRendererData[] rendererDataList = Array.Empty(); public int defaultRendererIndex = 0; + private readonly ScriptableRendererDataCollection + m_rendererDataCollection; private int m_rendererDataRuntimeStateHash; private bool m_rendererDataRuntimeStateHashResolved; protected RendererBackedRenderPipelineAsset() { + m_rendererDataCollection = + new ScriptableRendererDataCollection(); } protected override ScriptableRenderPipeline CreatePipeline() @@ -96,30 +99,21 @@ namespace XCEngine.Rendering.Universal protected override void ReleaseRuntimeResources() { - ReleaseRendererDataRuntimeResources(); + m_rendererDataCollection.ReleaseRuntimeResources( + ref rendererDataList, + ref defaultRendererIndex, + CreateDefaultRendererData); } protected override void SynchronizeRuntimeResourceVersion() { - int runtimeStateHash = - ComputeRendererDataRuntimeStateHash(); - if (!m_rendererDataRuntimeStateHashResolved) - { - m_rendererDataRuntimeStateHash = - runtimeStateHash; - m_rendererDataRuntimeStateHashResolved = true; - return; - } - - if (runtimeStateHash == - m_rendererDataRuntimeStateHash) - { - return; - } - - m_rendererDataRuntimeStateHash = - runtimeStateHash; - SetDirty(); + m_rendererDataCollection.Synchronize( + ref rendererDataList, + ref defaultRendererIndex, + ref m_rendererDataRuntimeStateHash, + ref m_rendererDataRuntimeStateHashResolved, + CreateDefaultRendererData, + SetDirty); } protected virtual ScriptableRenderPipeline @@ -152,7 +146,10 @@ namespace XCEngine.Rendering.Universal internal ScriptableRendererData GetDefaultRendererData() { - return ResolveSelectedRendererData(); + return m_rendererDataCollection.GetDefaultRendererData( + ref rendererDataList, + ref defaultRendererIndex, + CreateDefaultRendererData); } internal ScriptableRenderer GetDefaultRenderer() @@ -162,8 +159,7 @@ namespace XCEngine.Rendering.Universal internal ScriptableRendererData ResolveSelectedRendererData() { - return ResolveRendererDataByResolvedIndex( - ResolveSelectedRendererIndex()); + return GetDefaultRendererData(); } internal ScriptableRenderer ResolveSelectedRenderer() @@ -178,9 +174,11 @@ namespace XCEngine.Rendering.Universal internal ScriptableRendererData GetRendererData( int rendererIndex) { - return ResolveRendererDataByResolvedIndex( - ResolveRendererIndexWithFallback( - rendererIndex)); + return m_rendererDataCollection.GetRendererData( + ref rendererDataList, + ref defaultRendererIndex, + rendererIndex, + CreateDefaultRendererData); } internal ScriptableRenderer ResolveRenderer( @@ -193,113 +191,13 @@ namespace XCEngine.Rendering.Universal : null; } - protected internal void ReleaseRendererDataRuntimeResources() - { - EnsureRendererDataList(); - - for (int i = 0; i < rendererDataList.Length; ++i) - { - ScriptableRendererData rendererData = - rendererDataList[i]; - if (rendererData == null || - WasRendererDataReleasedEarlier(i)) - { - continue; - } - - rendererData.ReleaseRuntimeResourcesInstance(); - } - } - - private void EnsureRendererDataList() - { - if (rendererDataList != null && - rendererDataList.Length > 0) - { - return; - } - - ScriptableRendererData defaultRendererData = - CreateDefaultRendererData(); - rendererDataList = - defaultRendererData != null - ? new ScriptableRendererData[] - { - defaultRendererData - } - : Array.Empty(); - defaultRendererIndex = 0; - } - - private bool WasRendererDataReleasedEarlier( - int rendererDataIndex) - { - ScriptableRendererData rendererData = - rendererDataList[rendererDataIndex]; - for (int i = 0; i < rendererDataIndex; ++i) - { - if (object.ReferenceEquals( - rendererDataList[i], - rendererData)) - { - return true; - } - } - - return false; - } - - private int ComputeRendererDataRuntimeStateHash() - { - unchecked - { - int hash = 17; - hash = - (hash * 31) + - ResolveSelectedRendererIndex(); - if (rendererDataList == null) - { - return hash; - } - - hash = (hash * 31) + rendererDataList.Length; - for (int i = 0; i < rendererDataList.Length; ++i) - { - ScriptableRendererData rendererData = - rendererDataList[i]; - if (rendererData != null) - { - hash = - (hash * 31) + - RuntimeHelpers.GetHashCode( - rendererData); - hash = - (hash * 31) + - rendererData - .GetRuntimeStateVersionInstance(); - } - } - - return hash; - } - } - - private ScriptableRendererData ResolveRendererDataByResolvedIndex( - int resolvedRendererIndex) - { - EnsureRendererDataList(); - if (resolvedRendererIndex < 0) - { - return null; - } - - return rendererDataList[resolvedRendererIndex]; - } - private int ResolveSelectedRendererIndex() { - return ResolveRendererIndexWithFallback( - defaultRendererIndex); + return m_rendererDataCollection + .ResolveDefaultRendererIndex( + ref rendererDataList, + ref defaultRendererIndex, + CreateDefaultRendererData); } private ScriptableRendererData ResolveRendererData( @@ -348,76 +246,12 @@ namespace XCEngine.Rendering.Universal } context.rendererIndex = - ResolveRendererIndexWithFallback( - rendererIndex); - } - - private int ResolveRendererIndexWithFallback( - int rendererIndex) - { - EnsureRendererDataList(); - if (rendererDataList.Length == 0) - { - return -1; - } - - if (rendererIndex < 0 || - rendererIndex >= rendererDataList.Length) - { - return ResolveDefaultRendererIndex(); - } - - if (rendererDataList[rendererIndex] == null) - { - return ResolveDefaultRendererIndex(); - } - - return rendererIndex; - } - - private int ResolveDefaultRendererIndex() - { - EnsureRendererDataList(); - if (rendererDataList.Length == 0) - { - return -1; - } - - if (defaultRendererIndex < 0 || - defaultRendererIndex >= rendererDataList.Length) - { - defaultRendererIndex = 0; - } - - if (rendererDataList[defaultRendererIndex] != null) - { - return defaultRendererIndex; - } - - int firstValidRendererIndex = - FindFirstValidRendererIndex(); - if (firstValidRendererIndex >= 0) - { - defaultRendererIndex = firstValidRendererIndex; - return defaultRendererIndex; - } - - rendererDataList[defaultRendererIndex] = - CreateDefaultRendererData(); - return defaultRendererIndex; - } - - private int FindFirstValidRendererIndex() - { - for (int i = 0; i < rendererDataList.Length; ++i) - { - if (rendererDataList[i] != null) - { - return i; - } - } - - return -1; + m_rendererDataCollection + .ResolveRendererIndexWithFallback( + ref rendererDataList, + ref defaultRendererIndex, + rendererIndex, + CreateDefaultRendererData); } } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererDataCollection.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererDataCollection.cs new file mode 100644 index 00000000..ce6087f1 --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererDataCollection.cs @@ -0,0 +1,283 @@ +using System; +using System.Runtime.CompilerServices; + +namespace XCEngine.Rendering.Universal +{ + internal sealed class ScriptableRendererDataCollection + { + private ScriptableRendererData[] m_rendererDataList; + + public ScriptableRendererDataCollection() + { + m_rendererDataList = + Array.Empty(); + } + + public ScriptableRendererData GetDefaultRendererData( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + Func createDefaultRendererData) + { + return GetRendererData( + ref configuredRendererDataList, + ref defaultRendererIndex, + ResolveDefaultRendererIndex( + ref configuredRendererDataList, + ref defaultRendererIndex, + createDefaultRendererData), + createDefaultRendererData); + } + + public ScriptableRendererData GetRendererData( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + int rendererIndex, + Func createDefaultRendererData) + { + int resolvedRendererIndex = + ResolveRendererIndexWithFallback( + ref configuredRendererDataList, + ref defaultRendererIndex, + rendererIndex, + createDefaultRendererData); + if (resolvedRendererIndex < 0) + { + return null; + } + + return m_rendererDataList[resolvedRendererIndex]; + } + + public int ResolveDefaultRendererIndex( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + Func createDefaultRendererData) + { + ScriptableRendererData[] rendererDataList = + ResolveConfiguredRendererDataList( + ref configuredRendererDataList, + createDefaultRendererData); + if (rendererDataList.Length == 0) + { + return -1; + } + + if (defaultRendererIndex < 0 || + defaultRendererIndex >= rendererDataList.Length) + { + defaultRendererIndex = 0; + } + + if (rendererDataList[defaultRendererIndex] != null) + { + return defaultRendererIndex; + } + + int firstValidRendererIndex = + FindFirstValidRendererIndex(rendererDataList); + if (firstValidRendererIndex >= 0) + { + defaultRendererIndex = firstValidRendererIndex; + return defaultRendererIndex; + } + + rendererDataList[defaultRendererIndex] = + createDefaultRendererData != null + ? createDefaultRendererData() + : null; + return rendererDataList[defaultRendererIndex] != null + ? defaultRendererIndex + : -1; + } + + public int ResolveRendererIndexWithFallback( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + int rendererIndex, + Func createDefaultRendererData) + { + ScriptableRendererData[] rendererDataList = + ResolveConfiguredRendererDataList( + ref configuredRendererDataList, + createDefaultRendererData); + if (rendererDataList.Length == 0) + { + return -1; + } + + if (rendererIndex < 0 || + rendererIndex >= rendererDataList.Length) + { + return ResolveDefaultRendererIndex( + ref configuredRendererDataList, + ref defaultRendererIndex, + createDefaultRendererData); + } + + if (rendererDataList[rendererIndex] == null) + { + return ResolveDefaultRendererIndex( + ref configuredRendererDataList, + ref defaultRendererIndex, + createDefaultRendererData); + } + + return rendererIndex; + } + + public void Synchronize( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + ref int collectionHash, + ref bool collectionHashResolved, + Func createDefaultRendererData, + Action onChanged) + { + ScriptableRendererData[] rendererDataList = + ResolveConfiguredRendererDataList( + ref configuredRendererDataList, + createDefaultRendererData); + int resolvedDefaultRendererIndex = + ResolveDefaultRendererIndex( + ref configuredRendererDataList, + ref defaultRendererIndex, + createDefaultRendererData); + int resolvedHash = + ComputeCollectionHash( + rendererDataList, + resolvedDefaultRendererIndex); + if (!collectionHashResolved) + { + collectionHash = resolvedHash; + collectionHashResolved = true; + return; + } + + if (resolvedHash == collectionHash) + { + return; + } + + collectionHash = resolvedHash; + onChanged?.Invoke(); + } + + public void ReleaseRuntimeResources( + ref ScriptableRendererData[] configuredRendererDataList, + ref int defaultRendererIndex, + Func createDefaultRendererData) + { + ScriptableRendererData[] rendererDataList = + ResolveConfiguredRendererDataList( + ref configuredRendererDataList, + createDefaultRendererData); + for (int i = 0; i < rendererDataList.Length; ++i) + { + ScriptableRendererData rendererData = + rendererDataList[i]; + if (rendererData == null || + WasRendererDataReleasedEarlier( + rendererDataList, + i)) + { + continue; + } + + rendererData.ReleaseRuntimeResourcesInstance(); + } + } + + private ScriptableRendererData[] ResolveConfiguredRendererDataList( + ref ScriptableRendererData[] configuredRendererDataList, + Func createDefaultRendererData) + { + if (configuredRendererDataList == null || + configuredRendererDataList.Length == 0) + { + ScriptableRendererData defaultRendererData = + createDefaultRendererData != null + ? createDefaultRendererData() + : null; + configuredRendererDataList = + defaultRendererData != null + ? new ScriptableRendererData[] + { + defaultRendererData + } + : Array.Empty(); + } + + m_rendererDataList = configuredRendererDataList; + return m_rendererDataList; + } + + private static int ComputeCollectionHash( + ScriptableRendererData[] rendererDataList, + int defaultRendererIndex) + { + unchecked + { + int hash = 17; + hash = (hash * 31) + defaultRendererIndex; + if (rendererDataList == null) + { + return hash; + } + + hash = (hash * 31) + rendererDataList.Length; + for (int i = 0; i < rendererDataList.Length; ++i) + { + ScriptableRendererData rendererData = + rendererDataList[i]; + if (rendererData == null) + { + hash = (hash * 31) + 1; + continue; + } + + hash = + (hash * 31) + + RuntimeHelpers.GetHashCode(rendererData); + hash = + (hash * 31) + + rendererData.GetRuntimeStateVersionInstance(); + } + + return hash; + } + } + + private static int FindFirstValidRendererIndex( + ScriptableRendererData[] rendererDataList) + { + for (int i = 0; i < rendererDataList.Length; ++i) + { + if (rendererDataList[i] != null) + { + return i; + } + } + + return -1; + } + + private static bool WasRendererDataReleasedEarlier( + ScriptableRendererData[] rendererDataList, + int rendererDataIndex) + { + ScriptableRendererData rendererData = + rendererDataList[rendererDataIndex]; + for (int i = 0; i < rendererDataIndex; ++i) + { + if (object.ReferenceEquals( + rendererDataList[i], + rendererData)) + { + return true; + } + } + + return false; + } + } +}