349 lines
11 KiB
Markdown
349 lines
11 KiB
Markdown
# SRP Runtime v2 计划 2026-04-18
|
||
|
||
## 1. 结论
|
||
|
||
当前阶段的下一步,不是继续做 editor 对接,也不是直接开做 deferred,更不是马上把阴影、体积、高斯这些效果全部搬到 C#。
|
||
|
||
下一阶段的正确主线是:
|
||
|
||
`Native RenderGraph / Native Scene Renderer`
|
||
`-> Managed SRP Runtime v2`
|
||
`-> Managed Renderer / Feature / Pass 组织层`
|
||
`-> 之后再开正式的 URP-like package`
|
||
|
||
换句话说,`SRP runtime v1` 已经把“managed pipeline 能创建、能被 native 持有、能参与 stage record”这条链路打通了;
|
||
`SRP runtime v2` 要解决的是另一件更关键的事:
|
||
|
||
**让 managed 侧不再只是“在 ScriptableRenderPipeline 里手写 stage”,而是正式进入 Unity/URP 风格的 `Renderer / Feature / Pass` 组织模型。**
|
||
|
||
---
|
||
|
||
## 2. 当前状态判断
|
||
|
||
从现有代码看,下面这些能力已经成立:
|
||
|
||
1. `RenderGraph`、`CameraFramePlan`、stage dispatch、compile、execute 已经是工作状态。
|
||
2. `ScriptableRenderPipelineHost` 已经能作为 native 宿主,组合 `stage recorder + fallback renderer`。
|
||
3. `ManagedScriptableRenderPipelineAsset` 已经不只是 descriptor 空壳,能够通过 bridge 创建 managed asset runtime。
|
||
4. `MonoScriptRuntime` 已经提供真实的 `ManagedRenderPipelineBridge`,不再只是测试 seam。
|
||
5. `ScriptableRenderContext` 已经有最小可用能力,能:
|
||
- `RecordScene()`
|
||
- `RecordScenePhase(...)`
|
||
- `RecordSceneInjectionPoint(...)`
|
||
- `RecordFullscreenPass(...)`
|
||
6. managed pipeline 已经能通过 `ScriptableRenderContext` 驱动 native `NativeSceneRecorder` 和 fullscreen pass sequence。
|
||
7. `ScriptableRenderPipelineAsset` 已经可以参与:
|
||
- `ConfigureCameraRenderRequest(...)`
|
||
- `ConfigureCameraFramePlan(...)`
|
||
- `GetDefaultFinalColorSettings()`
|
||
|
||
也就是说,**SRP runtime 的“宿主打通”问题已经不是主矛盾了。**
|
||
|
||
当前真正的主矛盾变成了:
|
||
|
||
1. managed 侧还没有 `ScriptableRenderer`
|
||
2. managed 侧还没有 `ScriptableRendererFeature`
|
||
3. managed 侧还没有 `ScriptableRenderPass`
|
||
4. managed 侧还没有 `RenderPassEvent`
|
||
5. pipeline 仍然直接在自己内部录制 stage,缺少可组合的 renderer 层
|
||
|
||
这意味着你现在虽然已经能“用 C# 自定义渲染”,但它还不是 Unity/URP 那种成熟形态。
|
||
现在的形态更接近:
|
||
|
||
`Pipeline 直接写 RenderGraph 录制逻辑`
|
||
|
||
而不是:
|
||
|
||
`Pipeline -> Renderer -> Feature -> Pass`
|
||
|
||
---
|
||
|
||
## 3. 为什么下一步必须先做这个
|
||
|
||
如果这一层不先补起来,后面会出现三个结构性问题:
|
||
|
||
### 3.1 自定义能力会卡死在 `ScriptableRenderPipeline`
|
||
|
||
当前用户要改渲染逻辑,主要方式还是:
|
||
|
||
1. 继承 `ScriptableRenderPipeline`
|
||
2. 覆盖 `SupportsStageRenderGraph(...)`
|
||
3. 覆盖 `RecordStageRenderGraph(...)`
|
||
4. 直接手写 `RecordScenePhase / RecordSceneInjectionPoint / RecordFullscreenPass`
|
||
|
||
这条路能做 demo,但不能支撑成熟工程。
|
||
|
||
因为它缺少:
|
||
|
||
1. renderer 内部 pass 队列
|
||
2. feature 注入点
|
||
3. renderer data / pipeline data
|
||
4. 面向 camera/frame 的稳定数据载体
|
||
|
||
继续沿这条路堆功能,最后只会变成“能工作的脚本版 builtin forward”,而不是 Unity 意义上的 SRP/URP。
|
||
|
||
### 3.2 未来的阴影、后处理、体积、高斯没地方上移
|
||
|
||
你之前关心的那些东西,长期来看确实应该逐步从 C++ builtin 组织层往更高层移动,但它们要上移的不是裸 `Pipeline`,而是:
|
||
|
||
1. `Renderer`
|
||
2. `RendererFeature`
|
||
3. `RenderPass`
|
||
4. `RendererData`
|
||
|
||
如果这层不存在,那后面谈“把 shadow 组织权搬到 URP 层”“把 volumetric/gassian 搬到包层”,都会没有落点。
|
||
|
||
### 3.3 未来正式做 URP-like package 会缺承接面
|
||
|
||
真正的 URP-like package 不是只多几个 C# 类名,而是至少要有:
|
||
|
||
1. `PipelineAsset`
|
||
2. `RendererData`
|
||
3. `Renderer`
|
||
4. `RendererFeature`
|
||
5. `RenderPassEvent`
|
||
6. `RenderPass`
|
||
|
||
这几个层次如果不先在 SRP runtime 里长出来,后面直接开做 URP-like package 只会是壳子工程。
|
||
|
||
---
|
||
|
||
## 4. 本阶段目标
|
||
|
||
本阶段只做一件事:
|
||
|
||
**把当前“managed pipeline 直接录 stage”的模型,升级成“managed pipeline 驱动 managed renderer,renderer 再组织 feature/pass”的模型。**
|
||
|
||
收口后应该形成这样的关系:
|
||
|
||
`ScriptableRenderPipelineAsset`
|
||
`-> ScriptableRenderPipeline`
|
||
`-> ScriptableRenderer`
|
||
`-> ScriptableRendererFeature`
|
||
`-> ScriptableRenderPass`
|
||
`-> ScriptableRenderContext`
|
||
`-> Native RenderGraph / NativeSceneRenderer`
|
||
|
||
这一步完成后,你这个引擎才算真正进入“能继续往 Unity SRP/URP 方向长”的状态。
|
||
|
||
---
|
||
|
||
## 5. 本阶段明确不做的内容
|
||
|
||
这一阶段不做下面这些事:
|
||
|
||
1. 不做 deferred pipeline。
|
||
2. 不做 lightmap / baking。
|
||
3. 不做 editor 侧 inspector、资源面板、renderer asset 可视化编辑。
|
||
4. 不把阴影、体积、高斯、后处理全部迁到 C#。
|
||
5. 不直接暴露 raw RHI、raw RenderGraph handle 给脚本层。
|
||
6. 不为了兼容临时方案而长期保留双轨 API。
|
||
|
||
注意:
|
||
|
||
这不是说这些东西以后不做,而是说**现在最值钱的一刀不是它们**。
|
||
现在最值钱的是先把 renderer 组织层做对。
|
||
|
||
---
|
||
|
||
## 6. 目标架构
|
||
|
||
### 6.1 继续留在 C++ 的部分
|
||
|
||
1. `RHI`
|
||
2. `RenderGraph`
|
||
3. frame planning / stage dispatch / graph compile / execute
|
||
4. scene extraction / culling / frame data
|
||
5. native scene renderer 执行内核
|
||
6. shadow / fullscreen / scene phase 的底层执行 primitive
|
||
|
||
### 6.2 开始在 managed 侧稳定下来的部分
|
||
|
||
1. `ScriptableRenderPipelineAsset`
|
||
2. `ScriptableRenderPipeline`
|
||
3. `ScriptableRenderer`
|
||
4. `ScriptableRendererFeature`
|
||
5. `ScriptableRenderPass`
|
||
6. `RenderPassEvent`
|
||
7. `RenderingData / CameraData` 这类组织层数据
|
||
|
||
### 6.3 这一阶段的核心原则
|
||
|
||
1. C++ 负责执行内核,不负责未来用户级管线组织。
|
||
2. managed 负责 renderer 组织,不直接持有底层 RHI 细节。
|
||
3. `BuiltinForwardPipeline` 继续做默认 native backend,但不再是未来自定义渲染的最终承载层。
|
||
4. shadow / volumetric / gaussian 这类东西后续上移时,上移的是“组织权”,不是把所有 draw code 都改成 C#。
|
||
|
||
---
|
||
|
||
## 7. 分阶段实施
|
||
|
||
### Phase A: 建立 managed renderer 基础抽象
|
||
|
||
先把 managed 侧最基础的几块搭出来:
|
||
|
||
1. `ScriptableRenderer`
|
||
2. `ScriptableRendererFeature`
|
||
3. `ScriptableRenderPass`
|
||
4. `RenderPassEvent`
|
||
5. `RenderingData v1`
|
||
6. `CameraData v1`
|
||
|
||
第一版不要追求大而全,只解决最小闭环:
|
||
|
||
1. pass 能声明执行时机
|
||
2. renderer 能收集 pass
|
||
3. feature 能往 renderer 注入 pass
|
||
4. pipeline 能把 camera/frame 数据传给 renderer
|
||
|
||
### Phase B: 建立 renderer 的执行骨架
|
||
|
||
这一阶段要解决的是:
|
||
|
||
1. `ScriptableRenderer.EnqueuePass(...)`
|
||
2. pass 按 `RenderPassEvent` 排序
|
||
3. renderer 根据 event 把 pass 映射到当前已有的 native 能力:
|
||
- `RecordScenePhase(...)`
|
||
- `RecordSceneInjectionPoint(...)`
|
||
- `RecordFullscreenPass(...)`
|
||
4. renderer 能组织默认的主场景录制顺序
|
||
|
||
这里的关键不是“把所有 pass 都写复杂”,而是先把**组织模型**做出来。
|
||
|
||
### Phase C: 做一个最小前向 renderer
|
||
|
||
在 managed 侧做第一条真正能跑的 renderer 主链:
|
||
|
||
1. 默认 opaque pass
|
||
2. 默认 skybox pass
|
||
3. 默认 transparent pass
|
||
4. 默认 post-process/fullscreen pass
|
||
5. 允许 feature 在标准 injection point 前后插入 pass
|
||
|
||
注意这里的“前向 renderer”还不是最终版 URP,只是:
|
||
|
||
**一个 SRP runtime v2 上的 first-party renderer 验证样板。**
|
||
|
||
### Phase D: 把 pipeline asset 和 renderer data 关系收清楚
|
||
|
||
这一阶段要让 `ScriptableRenderPipelineAsset` 不再只是“创建 pipeline”,还要能稳定持有 renderer 配置关系。
|
||
|
||
第一版建议先做到:
|
||
|
||
1. asset 持有一个默认 renderer data
|
||
2. pipeline 从 asset 创建 renderer
|
||
3. renderer feature 由 renderer data 决定启用顺序
|
||
4. camera/frame 规划仍然通过现有 planning context 接入
|
||
|
||
也就是说,要先有:
|
||
|
||
`PipelineAsset -> RendererData -> Renderer`
|
||
|
||
这条关系。
|
||
|
||
### Phase E: 替换 probe 级实现,形成正式 smoke path
|
||
|
||
这一阶段不再满足于 probe/demo,而是要形成正式可回归的主链:
|
||
|
||
1. 用 renderer/pass 模型重做当前 managed forward probe
|
||
2. 验证主场景 + fullscreen 仍然能工作
|
||
3. 验证 pipeline 切换时生命周期正确
|
||
4. 验证 native fallback 路径仍然可用
|
||
|
||
这一步收口后,才算本阶段真正完成。
|
||
|
||
---
|
||
|
||
## 8. 第一批要改的文件
|
||
|
||
### Managed 侧
|
||
|
||
优先会落在这些位置:
|
||
|
||
1. `managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs`
|
||
2. `managed/XCEngine.ScriptCore/ScriptableRenderPipeline.cs`
|
||
3. `managed/XCEngine.ScriptCore/ScriptableRenderContext.cs`
|
||
4. 新增 `managed/XCEngine.ScriptCore/Rendering/*` 或等价目录
|
||
5. `managed/GameScripts/RenderPipelineApiProbe.cs`
|
||
|
||
第一批大概率会新增这些类型:
|
||
|
||
1. `ScriptableRenderer`
|
||
2. `ScriptableRendererFeature`
|
||
3. `ScriptableRenderPass`
|
||
4. `RenderPassEvent`
|
||
5. `RenderingData`
|
||
6. `CameraData`
|
||
|
||
### Native 侧
|
||
|
||
native 这一阶段不是主战场,但仍可能需要补桥接:
|
||
|
||
1. `engine/src/Scripting/Mono/MonoScriptRuntime.cpp`
|
||
2. `engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h`
|
||
3. `engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp`
|
||
|
||
只有当 managed renderer 需要额外上下文时,才增加新的 internal call / bridge 数据。
|
||
|
||
原则是:
|
||
|
||
**能用现有 `ScriptableRenderContext` 表达的,就不继续扩 raw native API。**
|
||
|
||
---
|
||
|
||
## 9. 验收标准
|
||
|
||
这一阶段收口时,至少要同时满足下面这些条件:
|
||
|
||
1. managed 侧存在正式的 `Renderer / Feature / Pass` 基础抽象。
|
||
2. `ScriptableRenderPipeline` 不再是唯一的直接录制组织层。
|
||
3. 一个 managed forward renderer 能通过 pass 队列稳定跑通主场景和 fullscreen。
|
||
4. feature 可以在标准 injection point 上插入 pass。
|
||
5. pipeline asset 能稳定持有 renderer 配置关系。
|
||
6. native `RenderGraph`、`NativeSceneRenderer`、fallback renderer 路径不被破坏。
|
||
7. editor 编译通过,并完成基础 smoke test。
|
||
|
||
只要上面任意一条不成立,这一阶段都不能算收口。
|
||
|
||
---
|
||
|
||
## 10. 完成本阶段之后,下一步才是什么
|
||
|
||
等这一阶段收口之后,下一步才适合正式进入更像 URP 的层级:
|
||
|
||
1. `UniversalRenderPipelineAsset`
|
||
2. `UniversalRendererData`
|
||
3. `UniversalRenderer`
|
||
4. 官方 first-party `RendererFeature`
|
||
5. 更成熟的 `RenderPassEvent`
|
||
6. 逐步把 shadow / volumetric / gaussian / post-process 的组织权上移
|
||
|
||
也就是说:
|
||
|
||
**不是现在不能往 Unity 的 SRP/URP 方向走,而是现在要先把“SRP runtime 的 renderer 层”补齐。**
|
||
|
||
这一步做对了,后面你再做:
|
||
|
||
1. URP-like package
|
||
2. deferred
|
||
3. lightmap
|
||
4. renderer asset 资源化
|
||
5. editor 可视化配置
|
||
|
||
都会顺很多。
|
||
|
||
---
|
||
|
||
## 11. 下一刀从哪里下
|
||
|
||
下一步建议直接从 managed 侧开刀,顺序如下:
|
||
|
||
1. 先加 `ScriptableRenderer / ScriptableRenderPass / RenderPassEvent` 基础类型。
|
||
2. 再把当前 probe 级 `ManagedForwardRenderPipelineProbe` 改造成 renderer 驱动。
|
||
3. 确认 renderer/pass 模型能复用现有 `ScriptableRenderContext`。
|
||
4. 只有在 managed 侧明确缺上下文时,再补 native internal call。
|
||
|
||
简单说,下一刀不是继续修 C++ 渲染内核,而是:
|
||
|
||
**开始正式搭 managed renderer 组织层。**
|