375 lines
11 KiB
Markdown
375 lines
11 KiB
Markdown
# 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`
|
||
|
||
这是第二个根问题。
|
||
|
||
如果没有 `ScriptableRenderContext`,C# 根本无法真正组织管线。
|
||
|
||
第一版不要直接暴露 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. 不把 `RenderGraphTextureHandle`、`RenderSurface` 全量裸暴露成最终 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 包”“阴影搬迁”都会变成空中楼阁。
|