为什么我们需要多个渲染通道和子通道?
Why do we need multiple render passes and subpasses?
我以前有过 DirectX12 的使用经验,我不记得 Vulkan 中有类似渲染通道的东西,所以我无法做类比。如果我理解正确,则不需要同步同一子通道内的命令缓冲区。那么,为什么要复杂化并制作多个呢?为什么我不能只使用一个命令缓冲区并将所有与帧相关的信息放在那里?
这两个功能主要存在于 tile-based GPUs, which are common in mobile but, historically, uncommon on desktop computers. That's why DX12 doesn't have an equivalent, and Metal (iOS) does. Though both Nvidia's and AMD's 最近的架构中,现在也做基于图块渲染的变体,并且最近的 Windows-on-ARM PC 使用 Qualcomm 芯片(基于图块的 GPU ), 看看 DX12 是如何进化的会很有趣。
渲染通道的好处是在像素着色期间,您可以将帧缓冲区数据保存在片上内存中,而不是不断地读写外部内存。缓存有一些帮助,但如果不对像素着色进行重新排序,缓存往往会抖动很多,因为它不够大,无法存储整个帧缓冲区。一个相关的好处是,如果您无论如何都要完全覆盖它们,则可以避免读取以前的帧缓冲区内容,并且避免在渲染过程结束时写出帧缓冲区内容(如果在渲染过程结束后不需要它们)。在许多应用程序中,基于图块的 GPU 永远不必从外部存储器读取和写入深度缓冲区数据或多重采样数据,这节省了大量带宽和功率。
子通道是一项高级功能,在某些情况下,它允许驱动程序有效地将多个渲染通道合并为一个。目标和底层机制类似于 OpenGL ES Pixel Local Storage extension,但 API 略有不同,以允许更多 GPU 架构支持它并使其更具可扩展性/面向未来。这有助于基本延迟着色的经典示例:第一个子通道为每个像素写出 gbuffer 数据,随后的子通道使用它来照亮和遮蔽像素。 Gbuffer 可能很大,因此将所有这些都保存在芯片上并且永远不必将其读取或写入主内存是一件大事,尤其是在带宽和功耗往往更受限制的移动 GPU 上。
假设 GPU 无法直接渲染图像。想象一下,它只能渲染到特殊的帧缓冲区内存存储,这与常规图像内存完全分开。您不能直接与此帧缓冲区内存对话,也不能从中分配。但是,在渲染操作期间,您可以将图像中的数据复制到其中,从中读取数据到图像中,当然也可以渲染到这个内存中。
现在假设您的特殊帧缓冲区内存的大小是固定的,该大小小于您要渲染到的整个帧缓冲区的大小(可能小很多)。为了能够渲染大于帧缓冲区内存的图像,您基本上必须多次为这些目标执行所有渲染命令。为了避免运行多次顶点处理,你需要一种方法来存储顶点处理阶段的输出。
此外,在生成渲染命令时,您需要了解如何分配您的帧缓冲区内存。如果渲染到一个 32-bpp 图像与渲染到两个图像,则可能需要以不同方式划分帧缓冲区内存。分配帧缓冲区内存的方式会影响片段着色器代码的工作方式。毕竟,在渲染操作期间,片段着色器可以直接访问此帧缓冲区渲染内存。
这是渲染通道模型的基本思想:您正在渲染到大小不确定的特殊帧缓冲区内存。渲染通道系统复杂性的每个方面都基于这个概念模型。
子通道是您准确确定当前要渲染的对象的部分。因为这会影响帧缓冲区内存排列,所以图形管线总是通过引用渲染通道的子通道来构建。同样,要在子通道中执行的辅助命令缓冲区必须提供它将在其中使用的子通道。
当渲染通道实例开始在队列上执行时,它(概念上)将我们打算渲染的附件图像复制到帧缓冲区渲染内存中。在渲染通道结束时,我们渲染的数据被复制回附件图像。
在执行渲染过程实例期间,附件图像的数据被视为 "indeterminate"。虽然模型表示我们正在复制到帧缓冲区渲染内存中,但 Vulkan 不希望强制实现在直接渲染到图像时实际复制内容。
因此,Vulkan 仅声明没有操作可以访问用作附件的图像,除了那些访问图像作为附件。例如,您不能将附件图像作为纹理读取。但您可以将其作为输入附件阅读。
这是对基于图块的渲染器工作方式的概念性描述。这是作为 Vulkan 渲染通道架构基础的概念模型。渲染目标是不可访问的内存;它们是特殊的东西,只能通过特殊的方式获得。
您不能"just"从 G 缓冲区中读取,因为当您渲染到该 G 缓冲区时,它存在于特殊的帧缓冲区内存中,不是 还在图像中。
我以前有过 DirectX12 的使用经验,我不记得 Vulkan 中有类似渲染通道的东西,所以我无法做类比。如果我理解正确,则不需要同步同一子通道内的命令缓冲区。那么,为什么要复杂化并制作多个呢?为什么我不能只使用一个命令缓冲区并将所有与帧相关的信息放在那里?
这两个功能主要存在于 tile-based GPUs, which are common in mobile but, historically, uncommon on desktop computers. That's why DX12 doesn't have an equivalent, and Metal (iOS) does. Though both Nvidia's and AMD's 最近的架构中,现在也做基于图块渲染的变体,并且最近的 Windows-on-ARM PC 使用 Qualcomm 芯片(基于图块的 GPU ), 看看 DX12 是如何进化的会很有趣。
渲染通道的好处是在像素着色期间,您可以将帧缓冲区数据保存在片上内存中,而不是不断地读写外部内存。缓存有一些帮助,但如果不对像素着色进行重新排序,缓存往往会抖动很多,因为它不够大,无法存储整个帧缓冲区。一个相关的好处是,如果您无论如何都要完全覆盖它们,则可以避免读取以前的帧缓冲区内容,并且避免在渲染过程结束时写出帧缓冲区内容(如果在渲染过程结束后不需要它们)。在许多应用程序中,基于图块的 GPU 永远不必从外部存储器读取和写入深度缓冲区数据或多重采样数据,这节省了大量带宽和功率。
子通道是一项高级功能,在某些情况下,它允许驱动程序有效地将多个渲染通道合并为一个。目标和底层机制类似于 OpenGL ES Pixel Local Storage extension,但 API 略有不同,以允许更多 GPU 架构支持它并使其更具可扩展性/面向未来。这有助于基本延迟着色的经典示例:第一个子通道为每个像素写出 gbuffer 数据,随后的子通道使用它来照亮和遮蔽像素。 Gbuffer 可能很大,因此将所有这些都保存在芯片上并且永远不必将其读取或写入主内存是一件大事,尤其是在带宽和功耗往往更受限制的移动 GPU 上。
假设 GPU 无法直接渲染图像。想象一下,它只能渲染到特殊的帧缓冲区内存存储,这与常规图像内存完全分开。您不能直接与此帧缓冲区内存对话,也不能从中分配。但是,在渲染操作期间,您可以将图像中的数据复制到其中,从中读取数据到图像中,当然也可以渲染到这个内存中。
现在假设您的特殊帧缓冲区内存的大小是固定的,该大小小于您要渲染到的整个帧缓冲区的大小(可能小很多)。为了能够渲染大于帧缓冲区内存的图像,您基本上必须多次为这些目标执行所有渲染命令。为了避免运行多次顶点处理,你需要一种方法来存储顶点处理阶段的输出。
此外,在生成渲染命令时,您需要了解如何分配您的帧缓冲区内存。如果渲染到一个 32-bpp 图像与渲染到两个图像,则可能需要以不同方式划分帧缓冲区内存。分配帧缓冲区内存的方式会影响片段着色器代码的工作方式。毕竟,在渲染操作期间,片段着色器可以直接访问此帧缓冲区渲染内存。
这是渲染通道模型的基本思想:您正在渲染到大小不确定的特殊帧缓冲区内存。渲染通道系统复杂性的每个方面都基于这个概念模型。
子通道是您准确确定当前要渲染的对象的部分。因为这会影响帧缓冲区内存排列,所以图形管线总是通过引用渲染通道的子通道来构建。同样,要在子通道中执行的辅助命令缓冲区必须提供它将在其中使用的子通道。
当渲染通道实例开始在队列上执行时,它(概念上)将我们打算渲染的附件图像复制到帧缓冲区渲染内存中。在渲染通道结束时,我们渲染的数据被复制回附件图像。
在执行渲染过程实例期间,附件图像的数据被视为 "indeterminate"。虽然模型表示我们正在复制到帧缓冲区渲染内存中,但 Vulkan 不希望强制实现在直接渲染到图像时实际复制内容。
因此,Vulkan 仅声明没有操作可以访问用作附件的图像,除了那些访问图像作为附件。例如,您不能将附件图像作为纹理读取。但您可以将其作为输入附件阅读。
这是对基于图块的渲染器工作方式的概念性描述。这是作为 Vulkan 渲染通道架构基础的概念模型。渲染目标是不可访问的内存;它们是特殊的东西,只能通过特殊的方式获得。
您不能"just"从 G 缓冲区中读取,因为当您渲染到该 G 缓冲区时,它存在于特殊的帧缓冲区内存中,不是 还在图像中。