什么是 Render Graph?

Render Graph 或者说 Frame graph 是对复杂渲染管线的一个高度抽象,以图(Graph)的形式呈现渲染过程中的各个步骤,不同的渲染任务之间的依赖关系,以及它们对资源(如纹理、缓冲区等)的使用。

Frame graphs are a design pattern for handling complex rendering pipelines, which are currently used in industry. Their usage is motivated by handling barriers, queue synchronization and memory aliasing in the background by abstracting the rendering pipeline of a frame on a higher level.
—— https://github.com/gfx-rs/gfx/wiki/Frame-graphs

解决什么问题?

在传统的渲染管线中,渲染过程通常被划分为多个阶段,如下图所示:

这些阶段之间存在着输入和输出的依赖关系,其中一个阶段的输出作为下一个阶段的输入

Render Graph 的主要思想是将渲染过程表示为一个有向无环图(DAG),其中节点表示渲染通道(Render pass),边表示依赖关系。每个渲染通道执行特定的渲染操作,可具有输入和输出资源,例如Texture、Frame Buffer和执行的 Shader/Program。例如,假设节点 A 的输出Texture是节点 B 的输入Texture,那么节点 B 就依赖于节点 A。

通过概括渲染流程中的依赖关系,确保渲染阶段按正确的顺序执行,并且在需要时可基于一定的同步机制(Fence、Semaphore、Resource barriers)尽可能地并行执行渲染通道。

Render Graph 的目标是为了解决大型渲染引擎里复杂渲染管线中的一些问题。如资源生命周期管理、渲染效率、渲染过程的可视化调试等等。

Render Graph 不仅在游戏引擎中广泛应用:

它的理念也在现代图形 API 中可窥一斑,如VulkanRender PassDirectX 12Command ListMetalRender Pass等。

Render Graph 的优点

  • 高可复用性:通过将渲染过程分解为模块化的独立节点,使得渲染代码更易于维护和扩展。同时多个渲染阶段能够共用节点、复用资源,提高渲染效率。
  • 高效资源管理:准确地追踪每个资源在何时何地被创建、使用和销毁。自动的资源生命周期管理。
  • 并行优化:更激进的资源异步处理,分离子图(Subgraph)到多个 CommandQueue 或者将没有依赖关系的节点分别提交到不同的 CommandBuffer 并行执行,充分压榨多核。
  • 高度灵活:上层可以基于这个机制自由定义和组织渲染管线,更好地适应不同的渲染需求和平台限制。
  • 易于调试:基于DAG,很方便将渲染图可视化,用于分析渲染阶段之间的依赖关系和执行顺序,以及节点状态、性能分析和统计信息等等。

Render Graph 的并行优化 tips

  1. 并行节点:没有依赖关系的节点可以同时执行(需要图形API支持),从而利用多核处理器的并行能力。
  2. 异步任务:某些节点可能涉及到耗时的计算或等待外部资源的加载,将其标记为异步节点,可以使用异步任务来执行这些操作,以避免阻塞整个渲染流程。
  3. 分离渲染:将渲染节点按依赖关系可分成多个子渲染图(Subgraphs),每个子渲染图包含一组节点。子渲染图可以在独立的 Command Queue 上执行。这种方式适用于具有明显的渲染分离点的场景。
  4. 并行资源访问:可以使用资源屏障(Resource Barriers)来管理对共享资源的访问。通过正确地设置资源屏障,可以实现对多个节点对共享资源的并行读取和写入,从而提高渲染的并行性。

代码实现

Render Graph 的实现细节可以参考上面的代码实现。

Refs:

总结

本文旨在学习 Render Graph,对于基于各个图形API的 Render Graph 的实现我并没有更深入研究,对于Render Graph 的理解也只流于表面。如果有错误的地方,欢迎留言指正。Thanks!