diff --git a/docs/used/SRP_URP_MainSceneFeatureInjectionPlan_2026-04-22_完成归档.md b/docs/used/SRP_URP_MainSceneFeatureInjectionPlan_2026-04-22_完成归档.md new file mode 100644 index 00000000..57ba6150 --- /dev/null +++ b/docs/used/SRP_URP_MainSceneFeatureInjectionPlan_2026-04-22_完成归档.md @@ -0,0 +1,73 @@ +# SRP / URP Main Scene Feature Injection Plan + +时间:2026-04-22 + +## 背景 + +当前 renderer block 主线已经基本清晰: + +- `ShadowCaster` +- `DepthPrepass` +- `MainScene` +- `PostProcess` +- `FinalOutput` + +`UniversalRenderer` 也已经拆出了对应 block owner。 + +但 `main scene` 这一面的 renderer feature 注入还不够干净: + +1. `RenderObjectsRendererFeature` 自己手写 scene-stage 与 pass-stage 判断。 +2. `BuiltinGaussianSplatRendererFeature` / `BuiltinVolumetricRendererFeature` 各自重复一套 native scene feature pass 注入逻辑。 +3. 缺少统一的 main-scene feature 注入骨架,后续继续加 scene feature 时还会复制这些判断和样板。 + +## 本阶段目标 + +补齐 `main scene` feature 注入的统一 utility / controller,把当前散落在 feature 里的 main-scene 注入规则收口。 + +目标结果: + +1. `RenderObjectsRendererFeature` 走统一的 main-scene pass 注入 helper。 +2. Gaussian / Volumetric 共用同一套 native scene feature controller。 +3. 后续再加 main-scene renderer feature 时,有稳定的落点可复用。 + +## 范围 + +本阶段只处理 main-scene feature 注入骨架,不做: + +- public renderer feature API 改造 +- deferred rendering +- C++ host / RenderGraph 改造 + +## 实施步骤 + +### 1. 新增 main-scene feature 注入 utility + +职责: + +- 统一判断当前是否处于 main scene stage +- 统一判断 pass 是否属于当前 stage +- 统一执行 enqueue + +### 2. 新增 native scene feature controller + +职责: + +- 持有 `NativeSceneFeaturePass` +- 统一 create / configure / enqueue +- 供 Gaussian / Volumetric 复用 + +### 3. 接入现有 feature + +接入: + +- `RenderObjectsRendererFeature` +- `BuiltinGaussianSplatRendererFeature` +- `BuiltinVolumetricRendererFeature` + +## 完成标准 + +满足以下条件才算本阶段收口: + +1. main-scene feature 注入规则不再散落在多个 feature 里重复实现。 +2. Gaussian / Volumetric 共用统一 native scene feature controller。 +3. `XCEditor` 编译通过,old editor 冒烟通过。 diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 17357f6d..058928d8 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -256,6 +256,8 @@ set(XCENGINE_RENDER_PIPELINES_UNIVERSAL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneBlock.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalNativeSceneFeatureController.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalDepthPrepassBlock.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderPipeline.cs diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs index 6ede0104..370244bf 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs @@ -8,26 +8,23 @@ namespace XCEngine.Rendering.Universal { public RenderPassEvent passEvent = RenderPassEvent.BeforeRenderingTransparents; - - private NativeSceneFeaturePass m_pass; + private readonly UniversalNativeSceneFeatureController m_controller = + new UniversalNativeSceneFeatureController( + NativeSceneFeaturePassId + .BuiltinGaussianSplat); protected override int ComputeRuntimeStateHash() { int hash = base.ComputeRuntimeStateHash(); - hash = - RuntimeStateHashUtility.Combine( - hash, - (int)passEvent); - return hash; + return m_controller.AppendRuntimeStateHash( + hash, + passEvent); } public override void Create() { - m_pass = new NativeSceneFeaturePass( - NativeSceneFeaturePassId - .BuiltinGaussianSplat, - passEvent); + m_controller.Create(passEvent); } public override void AddRenderPasses( @@ -42,9 +39,10 @@ namespace XCEngine.Rendering.Universal } CreateInstance(); - - m_pass.Configure(passEvent); - renderer.EnqueuePass(m_pass); + m_controller.EnqueuePass( + renderer, + renderingData, + passEvent); } } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs index 439aff89..40c844f6 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs @@ -8,26 +8,23 @@ namespace XCEngine.Rendering.Universal { public RenderPassEvent passEvent = RenderPassEvent.BeforeRenderingTransparents; - - private NativeSceneFeaturePass m_pass; + private readonly UniversalNativeSceneFeatureController m_controller = + new UniversalNativeSceneFeatureController( + NativeSceneFeaturePassId + .BuiltinVolumetric); protected override int ComputeRuntimeStateHash() { int hash = base.ComputeRuntimeStateHash(); - hash = - RuntimeStateHashUtility.Combine( - hash, - (int)passEvent); - return hash; + return m_controller.AppendRuntimeStateHash( + hash, + passEvent); } public override void Create() { - m_pass = new NativeSceneFeaturePass( - NativeSceneFeaturePassId - .BuiltinVolumetric, - passEvent); + m_controller.Create(passEvent); } public override void AddRenderPasses( @@ -42,9 +39,10 @@ namespace XCEngine.Rendering.Universal } CreateInstance(); - - m_pass.Configure(passEvent); - renderer.EnqueuePass(m_pass); + m_controller.EnqueuePass( + renderer, + renderingData, + passEvent); } } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs index d7f2a705..364f8e85 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs @@ -136,13 +136,10 @@ namespace XCEngine.Rendering.Universal scenePhase, BuildRendererListDesc(), BuildDrawingSettings()); - if (!m_pass.SupportsStage( - renderingData.stage)) - { - return; - } - - renderer.EnqueuePass(m_pass); + UniversalMainSceneFeatureUtility.EnqueuePass( + renderer, + renderingData, + m_pass); } private RendererListDesc BuildRendererListDesc() diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs new file mode 100644 index 00000000..8bb629d9 --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs @@ -0,0 +1,42 @@ +using XCEngine; +using XCEngine.Rendering; + +namespace XCEngine.Rendering.Universal +{ + internal static class UniversalMainSceneFeatureUtility + { + public static bool IsActive( + RenderingData renderingData) + { + return renderingData != null && + renderingData.isMainSceneStage; + } + + public static bool SupportsPass( + RenderingData renderingData, + ScriptableRenderPass renderPass) + { + return IsActive(renderingData) && + renderPass != null && + renderPass.SupportsStage( + renderingData.stage); + } + + public static bool EnqueuePass( + ScriptableRenderer renderer, + RenderingData renderingData, + ScriptableRenderPass renderPass) + { + if (renderer == null || + !SupportsPass( + renderingData, + renderPass)) + { + return false; + } + + renderer.EnqueuePass(renderPass); + return true; + } + } +} diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalNativeSceneFeatureController.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalNativeSceneFeatureController.cs new file mode 100644 index 00000000..1cb79dee --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalNativeSceneFeatureController.cs @@ -0,0 +1,53 @@ +using XCEngine; +using XCEngine.Rendering; + +namespace XCEngine.Rendering.Universal +{ + internal sealed class UniversalNativeSceneFeatureController + { + private readonly NativeSceneFeaturePassId m_featurePassId; + private NativeSceneFeaturePass m_pass; + + public UniversalNativeSceneFeatureController( + NativeSceneFeaturePassId featurePassId) + { + m_featurePassId = + featurePassId; + } + + public int AppendRuntimeStateHash( + int hash, + RenderPassEvent passEvent) + { + return RuntimeStateHashUtility.Combine( + hash, + (int)passEvent); + } + + public void Create( + RenderPassEvent passEvent) + { + if (m_pass == null) + { + m_pass = new NativeSceneFeaturePass( + m_featurePassId, + passEvent); + return; + } + + m_pass.Configure(passEvent); + } + + public void EnqueuePass( + ScriptableRenderer renderer, + RenderingData renderingData, + RenderPassEvent passEvent) + { + Create(passEvent); + UniversalMainSceneFeatureUtility.EnqueuePass( + renderer, + renderingData, + m_pass); + } + } +}