充分利用 Delta Color Compression

最初发布时间:
Chris Brennan

更新 - RDNA/RDNA2

以下信息在 RDNA/RDNA2 架构方面仍然普遍适用,但解压缩发生的时间除外。

带宽一直是 GPU 上稀缺的资源。一方面,随着 HBM 等内存标准的不断发展,硬件取得了显著的进步;但另一方面,游戏以更高的分辨率、更大的缓冲区和更多的数据进行渲染,消耗了大量带宽。其中很大一部分带宽用于读取和写入渲染目标。尚未得到充分利用的是,渲染目标往往存储着缓慢变化的颜色数据。例如,天空是蓝色的,变化很小,但 GPU 会独立地处理每个像素,就好像它们包含所有唯一、不相关的值一样。

这种情况随着 Delta Color Compression(简称 DCC)的引入而改变。这是一种领域特定的压缩技术,它试图利用这种数据连贯性来减少所需的带宽。它在很多方面与典型的压缩器相似,但针对 3D 渲染进行了调整,是无损的。关键思想是处理整个块而不是单个像素。在一个块内,只有一个值以全精度存储,其余值以差值(delta)的形式存储——因此得名。如果颜色相似,差值可以比输入值使用更少的比特。DCC 在基于 GCN 1.2 或更新版本的独立 GPU 和 APU 上启用。实际的硬件实现比我刚才描述的要复杂得多。例如,它会根据访问模式(以及数据本身)调整其块大小,以优化潜在的随机访问。

新的压缩器位于 Color Block 中,允许图形像 Depth Block 压缩深度和模板目标一样压缩颜色渲染目标。这意味着不需要特殊的设置:如果目标表面被压缩,渲染就会直接通过压缩器。否则,管线不受影响。

压缩只是故事的一半,因为数据通常比写入时读取的次数要多得多。为了在读取时也能节省带宽,着色器核心已经具备了读取新的压缩颜色以及所有现有压缩表面的能力。这使得在渲染到纹理的场景中可以完全跳过解压缩操作——也就是说,从渲染目标到纹理的转换屏障实际上是一个无操作(no-op),不会触发昂贵的解压缩。

注意事项(Dos and Don'ts)

虽然 DCC 是一种“透明”功能,意味着它不需要开发者进行特定的设置,但仍有一些需要注意的细节才能最好地利用它。

1) 清零到 0.0 和 1.0

清零到 0.0 和 1.0 等常见值会比任意值更快,并且能节省更多带宽。

  • 颜色更容易处理不透明或透明的黑色或白色,所以对于 ARGB 表面的清零颜色,请使用 {1.0, 0.0, 0.0, 0.0}、{0.0, 1.0, 1.0, 1.0},或者全为 0.0 或全为 1.0。对于双通道格式,任何 1.0 和 0.0 的组合效果最好。
  • 深度为 0.0 或 1.0
  • 模板为 0x00
  • 尽量只在确定需要时进行清零;如果您确定会写入表面的所有区域,通常不需要清零。

2) 除非确实需要,否则不要将渲染目标标记为可由着色器读取

当已知着色器不会读取它们时,可由着色器读取的目标压缩效果不如那些不需要被读取的目标。正如前面提到的,着色器核心已得到扩展,可以直接访问压缩数据,但如果禁止着色器核心访问,还有额外的压缩选项可用。MSAA 深度目标如果不需要被标记为“着色器资源”,则会受到最严重的影响,因为它们否则可以被极好地压缩。

3) 尝试使用 32 位浮点深度缓冲区格式 (D32F) 而非 16 位 (D16) 以获得更好的性能

当用作着色器资源时,D32F 实际上可能比 D16 压缩得更小,并且在不是着色器兼容时压缩方式完全相同。它们仅在分配大小和解压缩时的带宽上有所不同,而解压缩通常不那么频繁(但当一个具有许多微小三角形的密集网格渲染到一个小屏幕区域时可能会发生)。D32F 还允许您使用反向 Z 以增加精度,这样可以几乎免费地利用。请记住,在 GCN 上,没有真正的 24 位深度目标。在底层,它们被处理为 32 位,只是丢弃了 8 位精度——因此从 D24 切换到 D32 目标没有成本。

4) 尝试使用 mipmapping 或预滤波,而不是稀疏读取图像

稀疏读取本来就不好,因为它们会使缓存产生抖动。压缩会使稀疏读取更糟,因为现在它会使两个缓存产生抖动。您是否曾发现更少的飞行波次或每个波次的有效线程更少会提高性能?一个可能的原因是缓存被抖动了。特别是,我们有时会观察到,在启用压缩的情况下,从阴影贴图过滤的稀疏读取性能会急剧下降。这尤其发生在以“超”图形质量模式渲染时,因为设置通常会调到最高。如果阴影看起来有噪声和锯齿,说明缓存正在被抖动!预滤波或选择较低分辨率会看起来更好,运行也更快。

5) 尽可能写入所有颜色通道,即使有些是重复写入的

部分写入需要特别小心。在非压缩数据的情况下,可以简单地屏蔽写入。对于压缩数据,这不起作用,因此必须先读取、解压缩、更新,然后写回以保留未触及的通道。为了有效利用压缩,最好在数据不再需要混合时完全覆盖底层数据,因此如果可能,请同时写入所有通道。

6) 组织 G-Buffer 数据以最大化压缩

如果将字段任意打包到 G-Buffer 中,请将高度相关的位放在每个通道的最高有效位 (MSBs) 中,并将噪声数据放在最低有效位 (LSBs) 中。这将压缩得更好,因为它响应典型的数据模式类似。

何时仍会发生解压缩?

 即使您已遵循了上述所有规则,DCC 有时仍可能被禁用,因为并非 GPU 的所有部分都能读取和写入压缩数据。在这些情况下,屏障会导致解压缩。理解这些情况何时可能发生很重要。

  • 同时写入和读取压缩目标时。
    • 元数据和数据之间不存在一致性,因此会导致损坏。
    • 驱动程序必须谨慎行事——即使可能存在同时访问,它也会解压缩,因此请确保在使用显式 API(Direct3D® 12、Vulkan™)时正确设置使用标志,并在未打算写入时取消绑定表面和/或使用写掩码。
  • 当着色器可以作为无序访问视图 (UAV) 写入资源时
    • 将纹理标记为可以通过着色器作为 UAV 写入,目前会完全禁用压缩。
    • 某些驱动程序可能仍然允许快速清零,然后在着色器 UAV 写入绑定时解压缩,直到下一次完全清零。
  • 在使用复制引擎之前。
    • 通常一旦被标记为复制源或目标,我们首先会解压缩,因为我们不知道它将去往何处。
    • 在相同类型和大小的表面之间可以进行原始复制……如果驱动程序知道这将发生。

Chris Brennan

Chris Brennan 是 AMD 的 GPU 硬件架构师。他从第一代 Radeon 的纹理管线开始,然后转向 3D 应用研究,并回到 Xbox® 360 的硬件工作,他现在负责固定函数图形管线和压缩。

相关视频

© . This site is unofficial and not affiliated with AMD.