欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

了解 RenderPass

最编程 2024-03-21 08:50:21
...


1. RenderPasses如何工作

简单来说,一个RenderPass就是渲染管线的单次运行。一个RenderPass将图像渲染到内存中的帧缓冲附件中。在渲染过程开始时,每个附件需要在块状内存中初始化而且在渲染结束时可能需要写回到内存中。

有些附件的生命周期可能是瞬间的。例如,一个应用在渲染期间使用颜色和深度附件,但是此应用只需要为以后的渲染操作保存颜色附件。在这样的场景中深度附件就是瞬间的并且可以在渲染通道结束时丢弃。

由于需要尽量减少RenderPass的开始和结束开销,Render Passes对于基于图块的渲染至关重要。

1.1 Vuluan里的RenderPasses

Vulkan通过API中的VkRenderPass结构体清晰地支持了RenderPasses,并且对于RenderPass包含的特别的帧缓存附件也添加了VkAttachmentDescription结构体。

每个附件描述必须清晰地定义在Render Pass开始时的loadOp操作及Render Pass结束时候的storeOp操作。所以,API需要应用开发人员添加明确的意图声明。

2. Mali GPUs和分块渲染

Mali GPUs将帧缓存划分成一系列小的子区域。在片段着色器阶段,每一个子块的帧缓存工作集都被保存在GPU中的内存中,这些内存和每个着色器内核紧紧绑定在一起。保存在GPU中的内存中降低了GPU访问外部DRAM的次数。下图展示了tile-based渲染的数据流:

为了最大利用tile-based渲染,您必须最大程度地减少分块内存的读和写。太多的外部存储的读和写同样减少了分块渲染的优势。尤其需要避免如下操作:

    - 在RenderPass开始时读入过时的且会被覆盖的帧缓存值。

    - 在RenderPass结束时写入临时的并且只会被当前RenderPass使用的值。

3. 高效的RenderPasses

为了确保每个RenderPass拥有最高性能,遵守下面的基本步骤移除一些多余的内存访问操作很有必要:

首先,确保应用中的每个逻辑RenderPass在提交给GPU后只会转变成一个物理RenderPass。所以,每个帧缓冲只应该绑定一次并且在切换到下个帧缓存前调用所有需要的绘制调用。这个步骤对OpenGL ES非常重要。

其次,降低RenderPasses的个数并且在可能的情况下合并相邻的RenderPasses。例如,一种常见的低效率就是一个3D渲染的RenderPass紧跟着一个覆盖在其上的2D UI RenderPass。

在大多数情况下,UI 绘制能够直接在3D渲染中使用,将两个RenderPasses合并成一个RenderPass。此技巧可以避免一次通过内存的往返操作。

3.1 最小化块加载的启动

Mali GPUs 可以在一条Render Pass启动时使用清除颜色初始化分块内存而不需要从内存中读回过时的帧缓冲中的内容。在绘制调用前,确保在每个RenderPass开始时清理或者作废了所有附件。除非你刻意绘制在前一帧绘制结果的上面。

对于Vulkan,可为每个附件设置loadOp值: 

    - VK_ATTACHMENT_LOAD_OP_CLEAR

    - VK_ATTACHMENT_LOAD_OP_DONT_CARE

注意:如果你使用命令VkCmdClear()去清理一个附件,或者手动地使用着色器去写一个常量值,这会导致一个per-fragment清理着色器。为了从迅速且固定函数的块初始化中收益,最好使用Render Pass的loadOp操作。

在Mali GPU中,start-of-pass的操作和start-of-pass无效化之间没有性能差异。

如果计划用不透明的图元完全覆盖屏幕,并且不依赖于起始值,则建议您使用无效操作而不是清除操作。

3.2 最小化块存储的结束

在一块渲染结束后,他被写回到主存中,对于许多应用,一些帧缓存附件可能是临时的,在Render Pass结束后不需要继续保存。驱动获知哪些附件可以被安全丢弃很重要。

对于Vulkan,对每个临时的附件设置storeOp为VK_ATTACHMENT_STORE_OP_DONT_CARE. 为了更加高效,应用甚至可以通过使用VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT来分配内存以及构建VkImage使用VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT 的方式避免为临时附件分配物理内存。

3.3 处理打包的Depth-Stencil

GPU通常使用压缩像素格式(例如D24S8)在内存中一起分配深度和模板附件。由于这种格式的打包性质,为了节省带宽,您必须在加载期间不读取任何附件,在存储期间也不写入任何附件。

为了可靠地获取最好的性能,建议:

• 如果只需要深度缓冲区,仅分配深度格式(D24或D24X8),并且不要附加模板附件。

• 如果只需要模板缓冲区,仅分配模板格式(S8),并且不要附加深度附件。

• 如果使用打包的深度模板附件,请始终附加两个附件,在装载时清除两个附件,并在存储时使这两个附件失效。

• 如果您使用压缩的深度模板附件,并且需要在以后的渲染过程中继续使用其中一个附件,请在渲染过程结束时使另一个附件无效。 使用帧缓冲区压缩方法可以节省一些带宽。

4. 示例

此示例描述了一个3D游戏渲染管线,通过四个Render Passes构建一帧。

1. FBO1 是一种off-screen Pass,可渲染速度纹理以进行运动模糊。 它使用颜色和深度附件。

2. FBO2是一种off-screen Pass,可渲染动态光源的深度阴影贴图。 它仅使用深度附件。

3. FBO3是实现主要3D渲染的off-screen Pass。它使用颜色,深度和模板附件,并读取FBO2的输出作为输入纹理。

以下RenderPass数据流程图说明了此渲染管道:

4.1 非高效实现方式

实际上,虽然此简短的调用序列可以工作,但它至少破坏了每个效率规则一次:

这是低效的渲染管线的RenderPass数据流图:

查看此图,我们可以确定一些问题。每个浅灰色箭头表示一些清除或无效操作节省的带宽。但是,每个橙色箭头都代表一些浪费的带宽,渲染通道在该带宽上不必要地读取或写入附件到主存储器。

发生这种情况是因为:

• 调用序列为FBO2创建两个硬件Render Passes,因为由FBO1渲染分开的两组不同的API调用进行渲染。

• FBO1具有冗余的颜色回读,因为调用序列没有清除颜色附件。

• FBO1具有冗余的深度回写,因为调用序列不会使深度附件无效。

• FBO3具有冗余的颜色回读,因为调用顺序不会清除颜色附件。

• FBO3具有冗余的深度和模板写回,因为调用顺序不会使附件无效。

注意:即使FBO0没有清除和无效操作,也不需要显式调用,因为EGL窗口表面的默认行为是在渲染过程开始时隐式清除所有窗口表面附件,然后在结束时使深度和模板无效。

如果我们假设应用程序以1080p渲染所有通道,使用32bpp的颜色(RGBA8)则,深度(D24X8)和深度模板(D24S8),则总浪费带宽为:

在60FPS时为2.99GB/s,系统内存带宽和电量预算的很大一部分都浪费在不必要的内存流量上。 帧缓冲压缩和其他GPU优化(例如隐藏表面移除)可以帮助减少这种情况。

但是,如果应用程序删除此冗余以保证没有开销,则效率更高。

4.2 高效实现方式

以下简短的调用序列实现了与效率低下的实现相同的渲染管道。 但是,这次它遵循我们的效率规则:

查看此调用序列创建的RenderPass图,与低效示例相比,效率节省显而易见:

每个浅灰色箭头表示通过清除或无效操作节省的一些带宽,并且与效率低下的示例相比,没有因不必要的图块加载或存储而浪费内存的带宽。

5. 多采样抗锯齿

Multi-Sample Anti-Aliasing(MSAA)是一种低代价的通过减少图元边缘的锯齿影响来提高渲染质量的方法。锯齿是图形像素采样混叠的结果。

由于增强现实和虚拟现实头戴式设备的普及,MSAA在移动设备上变得越来越重要。头戴设备会使像素变大,因为其镜片会缩放屏幕来填充视野。

在主要渲染过程中,MSAA通过使用每个像素的多个样本来获取颜色和深度,包括将它们存储在帧缓冲区中。渲染完成后,MSAA然后将这些样本减少到每个像素一个值。 Mali GPUs已针对每个像素存储4个样本进行了优化,在某些新产品中支持更高的MSAA级别,但会增加一些性能成本。

通常,外部DDR带宽的成本为100mw每GB/s。因此,此开销使用我们2.5瓦设备总功率预算中的790mw来写帧缓冲器。

这仅使用88mw的内存功耗。为了受益于这种低带宽的内部解析,应用程序必须选择加入并采取明确的步骤来使用该技术。

对于Vulkan,Render Pass应提供单个采样的pResolveAttachments来存储解析的数据,并将多采样附件的storeOp设置为VK_ATTACHMENT_STORE_OP_DONT_CARE。为了提高效率,应用程序甚至可以通过使用VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT分配后备内存,并使用VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT来构造VkImage,从而避免为临时附件分配物理后备内存。

当您正确地将所有额外的MSAA带宽保留在GPU内时,您可以显著地将系统带宽影响降低到原始实现所使用的带宽的九分之一。这大大提高了性能且降低了功耗。

6. 结语

在本文中,我们已经探讨了如何在应用程序中高效地构造Render Passes,以及如何实现一个更高效的渲染管道。尝试在Mali GPU上的应用程序上启用4xMSAA,并进行测试以查看视觉质量的差异以及启用它的相关性能成本。