URP Render Graph
URP Render Graph - Unity 6 的现代渲染框架
Render Graph 是 Unity 6 URP/HDRP 组织渲染流程的现代方式,把渲染 Pass 与资源抽象为一张有向无环图(DAG),自动管理资源生命周期与优化。
引言:为什么需要 Render Graph
传统自定义渲染(ScriptableRenderPass 旧写法)里,开发者手动管理渲染目标(RT)的创建、复用、释放与 Pass 间依赖 —— 容易出错、难优化、难在不同配置下复用。
Render Graph 的思想:你只声明每个 Pass 读什么、写什么资源;框架据此构建依赖图,然后自动完成:剔除无用 Pass、管理资源生命周期、复用/别名内存、插入同步。开发者从"手动管理"变成"声明意图"。
一、核心概念
Render Graph 的工作流程分为三个阶段:
- 各 Pass 声明读/写哪些资源 —— 声明阶段收集所有 Pass 的资源依赖关系。
- 构建有向无环图(DAG) —— 根据 Pass 与资源的依赖关系自动生成依赖图。
- 编译期分析 —— 框架对 DAG 进行三项自动优化:
- 剔除无输出贡献的 Pass
- 计算资源生命周期,复用/别名内存
- 确定执行顺序与同步
- 执行 —— 根据编译结果执行真正的渲染工作。
| 概念 | 说明 |
|---|---|
| Pass(节点) | 一次渲染工作,声明它读/写的资源 |
| Resource(资源) | 纹理/缓冲,用句柄(如 TextureHandle) 引用,多为框架管理的临时资源 |
| 依赖图(DAG) | 由"Pass 读写资源"关系自动构成 |
| 导入资源(Imported) | 外部已有的 RT(如相机目标)导入图中使用 |
二、Render Graph 自动做了什么
这是它相对手动管理的核心价值:
| 自动优化 | 说明 |
|---|---|
| Pass 剔除 | 对最终输出无贡献的 Pass 自动不执行 |
| 资源生命周期 | 按图分析每个资源何时创建、何时不再需要,自动分配/释放 |
| 内存别名(Aliasing) | 生命周期不重叠的资源复用同一块内存 -> 省显存/带宽 |
| 同步 / Barrier | 自动插入必要的资源状态转换与同步 |
对移动端 TBR 尤其有价值:正确的资源生命周期与别名能减少 RT 的 load/store 和显存占用。
三、写法思路(概念)
URP 的 Render Graph 写法大致是:在 ScriptableRendererFeature / ScriptableRenderPass 的 记录(Record) 阶段,向 RenderGraph 添加 Pass 并声明资源读写,把实际渲染放到 Pass 的执行回调里。
// 概念示意(确切 API 以版本为准)
class MyPass : ScriptableRenderPass
{
// 记录阶段:声明 Pass 与其读写的资源
public override void RecordRenderGraph(RenderGraph rg, ContextContainer ctx)
{
// 从 URP 提供的资源数据里取相机颜色/深度等句柄
var resources = ctx.Get<UniversalResourceData>();
TextureHandle src = resources.activeColorTexture;
// 创建临时纹理句柄
TextureHandle tmp = rg.CreateTexture(descTmp);
// 添加一个光栅 Pass,声明读 src、写 tmp
using (var builder = rg.AddRasterRenderPass<PassData>("MyPass", out var data))
{
data.src = src;
builder.UseTexture(src); // 声明读
builder.SetRenderAttachment(tmp, 0); // 声明写
builder.SetRenderFunc((PassData d, RasterGraphContext c) =>
{
// 实际绘制:Blit / DrawRenderers 等
});
}
}
}要点:
- 声明式:
UseTexture/SetRenderAttachment等告诉框架依赖,框架据此排图。 - 句柄而非实体:资源在编译后才真正分配,记录阶段拿到的是句柄。
- URP 资源数据:通过
ContextContainer(如UniversalResourceData/UniversalCameraData)获取相机颜色、深度等。
四、Pass 类型(概念)
URP Render Graph 通常提供几类 Pass 以对应不同工作:
| Pass 类型 | 用途 |
|---|---|
| Raster Pass | 常规光栅渲染(绘制几何、全屏 Blit) |
| Compute Pass | 计算着色器工作 |
| Unsafe Pass | 需要更底层控制、不完全走图管理时的逃生口 |
(具体名称/用法以版本 API 为准。)
五、迁移与实践
- 从旧
ScriptableRenderPass迁移:把手动 RT 管理改为声明式资源句柄;Execute逻辑迁到 Pass 的执行回调。 - 不要在记录阶段做实际渲染:记录只声明,渲染在执行回调里。
- 善用临时资源:交给 RenderGraph 管理生命周期,享受别名优化,别自己长期持有 RT。
- 调试:URP 提供 Render Graph Viewer 可视化图、资源与 Pass,排查依赖/剔除问题。
- 收益:更少的手动错误、跨配置自动优化的资源使用、更好的带宽/显存表现。
六、核心概念速查
| 概念 | 说明 |
|---|---|
| Render Graph | 把渲染 Pass 与资源建成 DAG,自动管理与优化 |
| 声明式 | Pass 只声明读/写资源,框架排图 |
| TextureHandle | 资源句柄,编译后才真正分配 |
| 内存别名 | 生命周期不重叠的资源复用内存 |
| Pass 剔除 | 对输出无贡献的 Pass 自动跳过 |
| RecordRenderGraph | 记录阶段入口(版本相关) |
| ContextContainer / UniversalResourceData | 获取相机颜色/深度等资源 |
| Render Graph Viewer | 可视化调试工具 |
总结
URP Render Graph 将渲染流程抽象为有向无环图,通过声明式 API 让开发者仅描述 Pass 与资源的读写关系,框架自动完成 Pass 剔除、资源生命周期管理、内存别名和同步 Barrier 插入。相比传统手动管理 RT 的方式,Render Graph 减少了编码错误,实现了跨配置自动优化,尤其在移动端 TBR 架构下能有效降低显存占用和带宽消耗。迁移时需将旧 Execute 逻辑迁至 Pass 执行回调,在记录阶段仅做声明,并善用临时资源以发挥别名优化收益。
