Files
XCEngine/docs/plan/SRP_Mainline_2026-04-16.md

11 KiB
Raw Blame History

SRP Mainline 2026-04-16

1. 结论

现在可以正式切到 SRP 主线。

但这里的“切到 SRP 主线”不是直接开做 URP 包层,也不是马上把所有渲染效果搬到 C#。

当前最正确的主线是:

Native RenderGraph / Planning / Execution -> Managed SRP Runtime -> Managed Forward Pipeline v1 -> URP-like 官方包层

也就是说,下一阶段的真正目标不是“做一个叫 SRP 的壳”,而是先把:

  1. managed pipeline asset 真正实例化出来
  2. managed pipeline 真正被 native 持有并回调
  3. C# 真正能组织主场景 render graph 录制

这三件事做成闭环。

在这之前,URP-like package 还不能开工。

2. 当前状态判断

2.1 已经具备的东西

当前 native 底座已经够你正式进入 SRP 主线:

  1. RenderGraph 已经不是空壳builder / compiler / executor 都在工作。
  2. ScriptableRenderPipelineHost 已经能把 “stage recorder + fallback renderer” 组合起来。
  3. BuiltinForwardPipeline 已经支持按 MainScene stage 录制 render graph。
  4. 默认 pipeline 工厂已经能从 “managed or default asset” 入口走。

这说明 native 侧已经有了 SRP runtime 可以依附的承接点。

2.2 还没具备的东西

当前 managed 侧还远远没有形成真正的 SRP runtime

  1. GraphicsSettings 现在只是在 native 里记录了一个 pipeline asset 类型描述,不是 asset 实例。
  2. ManagedScriptableRenderPipelineAsset 现在只是 “fallback host + optional stage recorder bridge”。
  3. 真正的 ManagedRenderPipelineBridge 实现根本还没接进运行时,当前只有测试里在 mock。
  4. ScriptableRenderPipelineAsset 只有 CreatePipeline() 占位,没有 frame/request planning 能力。
  5. ScriptableRenderPipeline 只有 SupportsStageRenderGraph/RecordStageRenderGraph 两个最小占位方法,而且没有上下文对象。
  6. C# 层没有 ScriptableRenderContext、没有 CommandBuffer、没有 RendererFeature、没有 Renderer 抽象。

所以当前状态不是 “SRP 已经差不多了”,而是:

native SRP host seam 已经有了

managed SRP runtime 还没开始真正落地

3. 当前最核心的架构根因

3.1 现在的 managed 入口只是“选择器”,不是“运行时”

当前 GraphicsSettings.renderPipelineAssetType 的语义本质上是:

把一个 C# 类型名写回 native

它没有解决下面这些真正关键的问题:

  1. asset 谁实例化
  2. pipeline 谁实例化
  3. 生命周期谁管理
  4. native 如何回调到具体 pipeline 对象
  5. pipeline 如何持有自己的配置

这意味着现在的 API 形态本身还不是未来可长期保留的最终形态。

3.2 当前 bridge 只允许“补一个 stage recorder”这还不是 SRP

现在 ManagedScriptableRenderPipelineAsset 的工作方式是:

  1. 先创建 native fallback host
  2. 再给 host 塞一个可选 stage recorder

这条路只能得到:

C# 向 builtin forward 注入一个 graph 录制钩子

而不是:

C# 自己拥有一条 render pipeline

如果这条边界不改,后面做出来的只会是“脚本包装版 builtin forward”不是 Unity 意义上的 SRP

3.3 当前 pipeline API 颗粒度太粗,无法支撑未来 SRP/URP

现在 managed pipeline 只有:

  1. SupportsStageRenderGraph(CameraFrameStage)
  2. RecordStageRenderGraph(CameraFrameStage)

问题是它没有:

  1. frame context
  2. camera context
  3. render graph builder/context wrapper
  4. scene draw / fullscreen / blit / renderer feature 注入接口
  5. request/frame planning hook

这意味着即使把 bridge 打通C# 也拿不到足够的组织能力。

3.4 当前 BuiltinForwardPipeline 仍然太像“完整管线”,还不像“可被 SRP 调度的 renderer”

现在 builtin forward 内部已经有:

  1. SceneRenderFeatureHost
  2. scene phase
  3. stage graph builder

但这些能力大部分仍然锁在 BuiltinForwardPipeline 这个整管线对象内部。

对未来 SRP 来说,更正确的方向应该是:

  1. BuiltinForward 退化为 native 默认 renderer 实现
  2. 把可复用的 scene rendering contract 抽成稳定 native renderer API
  3. 让 managed pipeline 调用这个 renderer API 组织主场景,而不是直接操纵 BuiltinForwardPipeline 私有实现

3.5 当前 asset API 太弱,无法承担 Unity 风格 pipeline asset 角色

Unity 里的 pipeline asset 至少要承担:

  1. 创建 pipeline 实例
  2. 保存 renderer 配置
  3. 保存阴影/后处理/renderer data 等默认策略
  4. 驱动 camera/frame 规划策略

而现在的 managed asset 还没有这些入口。

这会直接阻塞未来:

  1. URP Asset
  2. RendererData
  3. RendererFeature
  4. shadow/post-process 默认策略迁移

4. 正确目标架构

正确的长期结构应该是:

RHI -> Native Render Kernel -> Native RenderGraph -> Native Renderer Contract -> Managed SRP Runtime -> Managed Universal Pipeline Package -> 用户自定义 Pipeline / Feature / Pass

其中边界必须明确:

4.1 留在 C++ 的东西

  1. RHI
  2. RenderGraph
  3. planning / execution 主框架
  4. scene extraction / culling / resource lifetime
  5. native draw/fullscreen/feature primitive
  6. builtin renderer 的底层实现

4.2 去到 managed 的东西

  1. ScriptableRenderPipelineAsset
  2. ScriptableRenderPipeline
  3. ScriptableRenderContext
  4. UniversalRenderPipelineAsset
  5. UniversalRenderer / RendererFeature / RenderPass
  6. 用户自定义管线逻辑

4.3 不应该长期停留在 C++ builtin pipeline 里的东西

这些能力最终应该逐步上移到 URP-like package 的组织层:

  1. 阴影默认策略
  2. post-process 组合策略
  3. gaussian / volumetric / custom effect 的注入时机
  4. renderer feature 的启用/排序/注入逻辑

但注意:

“上移”指的是组织和调度上移,不是把底层 draw code 全部改成 C#。

5. 执行顺序

Step 1: 先做通用 managed object runtime

这是第一刀,必须先做。

目标:

  1. MonoScriptRuntime 里补通用 managed object 创建/持有/释放能力
  2. 不再只支持 ScriptComponent 实例和 GameObject/Component wrapper
  3. 能按类描述创建任意 managed 对象
  4. 能持有 GC handle
  5. 能反射调用实例方法
  6. 能稳定记录异常并向 native 返回失败

这一步是后面所有 SRP runtime 的硬前提。

没有这个asset/pipeline 实例生命周期根本没法成立。

Step 2: 把 managed pipeline asset 从“类型描述”提升成“真实 runtime 对象”

目标:

  1. native 不再只拿到 assembly/namespace/class 描述
  2. native 能真正创建一个 managed asset 实例
  3. managed asset 能产出 managed pipeline 实例
  4. runtime 能持有 asset/pipeline 的生命周期

这一阶段里,GraphicsSettings.renderPipelineAssetType 可以临时继续作为 bootstrap 入口, 但 native 内部不能再停留在 descriptor-only 语义。

后面如果你要彻底对齐 Unity再把 public API 从 Type 收敛到 asset instance。

Step 3: 实现真正的 managed render pipeline bridge

目标:

  1. MonoScriptRuntime 提供真实的 ManagedRenderPipelineBridge
  2. ManagedScriptableRenderPipelineAsset 不再只接测试 mock
  3. bridge 负责创建绑定到具体 managed pipeline 实例的 recorder/runtime object
  4. native ScriptableRenderPipelineHost 能稳定回调到对应 managed pipeline

注意:

这一步不能继续维持“只有一个全局空 bridge”的临时状态。

Step 4: 扩展 managed asset / pipeline API

当前 API 太弱,必须补接口。

至少要补到:

  1. asset 侧有 request/frame planning 钩子
  2. pipeline 侧有真正的 record context
  3. pipeline 侧能拿到 camera/stage/frame 语义
  4. pipeline 侧不再只有裸 CameraFrameStage

推荐的第一版方向:

  1. ScriptableRenderPipelineAsset
    • CreatePipeline()
    • ConfigureCameraRenderRequest(...)
    • ConfigureCameraFramePlan(...)
    • GetDefaultFinalColorSettings() 或等价设置入口
  2. ScriptableRenderPipeline
    • SupportsStageRenderGraph(...)
    • RecordStageRenderGraph(...)
    • 参数升级为真正 context而不是只有 stage enum

Step 5: 建立 ScriptableRenderContext v1

这是第二个根问题。

如果没有 ScriptableRenderContextC# 根本无法真正组织管线。

第一版不要直接暴露 RHI要暴露“受控的 native renderer contract”

  1. record graph pass
  2. 调用 builtin scene renderer 绘制 opaque / skybox / transparent
  3. 执行 fullscreen / blit
  4. 访问必要的 frame blackboard / source/target
  5. 注入 renderer feature/pass

原则:

  1. 不把 native 内部 planning 细节直接暴露给 C#
  2. 不把 RenderGraphTextureHandleRenderSurface 全量裸暴露成最终 API
  3. 先做受控 wrapper再决定哪些能力继续上放

Step 6: 把 builtin forward 拆成可复用的 native renderer contract

这一刀非常关键。

目标:

  1. 不让 managed pipeline 直接依赖 BuiltinForwardPipeline 私有实现
  2. 把主场景绘制能力抽成稳定 native renderer API
  3. 让 builtin forward 退化为默认实现,而不是未来所有 SRP 的硬编码中心

正确方向类似:

BuiltinForwardSceneRenderer

或等价的

UniversalRendererNativeBackend

它至少要能被 managed 调度:

  1. 主场景 phase 绘制
  2. feature injection point
  3. shadow sampling 相关约束
  4. scene setup / pass begin/end

Step 7: 做一条最小可用 managed forward pipeline

到这一步,才算真正开始“跑 SRP 主线”。

验收标准不是有类名,而是:

  1. C# managed pipeline 可以被创建
  2. managed pipeline 可以稳定回调
  3. managed pipeline 可以录 MainScene graph
  4. 这条 managed pipeline 可以替代当前 builtin forward 跑出主场景

这个阶段不要急着搬阴影/体积/高斯的全部组织权。

先把主链跑通。

Step 8: 再开 URP-like package

只有当 Step 7 完成,才适合正式开:

UniversalRenderPipelineAsset UniversalRendererData UniversalRenderer RendererFeature RenderPassEvent

到这时才开始逐步把:

  1. 阴影策略
  2. post-process 组合
  3. 体积/高斯等特性注入

从当前 native builtin 组织层往 managed package 迁。

6. 本阶段明确不做的事

现在不应该做:

  1. 直接开 URP
  2. 直接做 deferred pipeline
  3. 直接把所有阴影/体积/高斯逻辑搬上 C#
  4. 直接暴露一堆 raw native internal type 给脚本

这些都会把架构做歪。

7. 第一阶段收口标准

下面这些条件成立,才算 SRP runtime v1 收口:

  1. 运行时存在真实的 managed bridge而不是测试 mock
  2. native 能创建并持有 managed asset 与 managed pipeline 实例
  3. managed pipeline 能稳定参与 camera frame graph 录制
  4. managed forward pipeline 能替代 builtin forward 跑通主场景
  5. builtin forward 仍保留 native fallback 路径
  6. editor / runtime 渲染不会因为切到 managed pipeline 而失稳

8. 下一步从哪里下刀

下一步不要先碰 URP,也不要先改渲染效果组织。

第一刀就应该是:

MonoScriptRuntime -> 通用 managed object runtime -> 真实 ManagedRenderPipelineBridge

原因很简单:

当前最根的缺口不是某个 pass不是某个 renderer feature也不是 shader。

而是:

managed pipeline 在运行时根本还不存在

这个根因不先解决后面所有“SRP API”“URP 包”“阴影搬迁”都会变成空中楼阁。