OpenGL 优化的纹理表示

OpenGL optimised representation of textures

我读过很多地方,人们应该避免使用 3 字节长的 OpenGL 纹理,并且应该始终使用 4 字节总线对齐表示。牢记这一点,我有几个关于 glTextImage2D API 的问题。

根据文档,API 签名是:

void glTexImage2D(GLenum target,  GLint level,  GLint internalformat,
                  GLsizei width,  GLsizei height,  GLint border,  
                  GLenum format,  GLenum type,  const GLvoid * data);

我假设如果我有一个 RGB 图像并且我想要一个 RGBA 表示,那么将内部格式参数指定为 GL_RGBA 并将格式参数指定为 GL_RGB 就足够了吗?生成纹理时是否会在格式之间进行内部转换?

我的第二个问题是如果我有灰度数据怎么办(所以只有一个通道)。可以将其表示为 GL_RED 还是最好用 4 字节表示?

Am I correct in assuming that if I have an RGB image and I want a RGBA representation, it is suffice to specigy the internalformat parameter as GL_RGBA and the format parameter as GL_RGB? Would there be an internal conversion between the formats when generating the texture?

这会起作用,GL 会为每个纹素的 alpha 分量分配一个常量 1.0。 OpenGL 需要在像素传输期间将 compatible 图像数据转换为 GPU 的本机格式,这包括添加额外的图像通道、从浮点数转换为定点数、将字节交换为CPU 和 GPU.

之间的字节序差异

为了获得最佳的像素传输性能,您需要消除 GL 会做的所有额外工作。这意味着如果您使用 GL_RGBA8 作为您的内部格式,您的数据类型应该是 GL_UNSIGNED_BYTE 并且像素应该是 RGBA 的某种变体(通常 GL_BGRA 是快速路径 -数据可以直接从 CPU 复制到 GPU 而不改变)。

在CPU端仅存储3个组件显然会阻止直接复制,并且会使驱动程序做更多的工作。这是否重要取决于您传输像素数据的数量和频率。

My second question is what if I have grayscale data (so just one channel). Is it ok to represent this as GL_RED or is it also better to have a 4 byte representation for this?

GL_RED 不代表您的数据。这只会告诉 GL 像素传输数据包含哪些通道,您必须将它与数据类型(例如 GL_UNSIGNED_BYTE)结合起来才能理解它。

GL_R8 将是灰度图像的常见内部格式,这非常好。您需要关注的经验法则实际上是数据大小需要与 2 的幂对齐。所以 1、2、4 和 8 字节的图像格式都可以。奇怪的是 3,当您尝试使用 GL_RGB8 之类的东西时会发生这种情况(驱动程序将不得不填充该图像格式以进行对齐)。

除非你真的需要 32 位的渐变值 (42.9 亿级灰度!) 在你的灰度中,坚持使用 GL_R8(256 级)或GL_R16(65536 色)内部格式。

我不同意您发现的避免使用 RGB 格式的建议。我想不出避免使用 RGB 纹理的充分理由。一些 GPU 本身就支持它们,而其他许多则不支持。您有两种情况:

  1. GPU 不支持 RGB 纹理。它将使用很大程度上等同于RGBA的格式进行存储,并在采样时忽略A分量。
  2. GPU 支持 RGB 纹理。

在场景 1 中,您最终得到的结果与将 RGBA 指定为内部格式时得到的结果几乎相同。在方案 2 中,通过实际使用 RGB 内部格式,您可以节省 25% 的内存(以及相应的带宽)。

因此,使用 RGB 作为内部格式绝不会更糟,而且在某些系统上可能会更好。

您的代码片段中的内容在桌面 OpenGL 中是完全合法的。您提供的 RGB 数据将通过用 1.0 填充 A 组件扩展为 RGBA。在 OpenGL ES 中情况并非如此,您只能为每种内部格式使用数量非常可控的格式,这主要避免了 TexImageTexSubImage 操作期间的格式转换。

匹配内部格式和 format/type 以避免转换通常是有益的。但即便如此,也并不像看起来那么明确。假设您将 RGB 数据或 RGBA 数据加载到具有内部 RGBA 格式的纹理中进行比较。加载 RGBA 具有明显的优势,因为它不使用格式转换。另一方面,由于 RGB 数据较小,加载它需要较少的内存带宽,并且可能导致较少的缓存污染。

现在,现代计算机系统的内存带宽如此之高,以至于您无法通过从单个内核进行顺序访问来真正使其饱和。因此,避免转换的选项可能会更好。但这将非常依赖于平台和情况。例如,如果需要中间副本,则较小的数据量可能会胜出。特别是如果实际的 RGB 到 RGBA 扩展可以作为 GPU 执行的副本的一部分来完成。

我肯定会避免的一件事是在您自己的代码中进行转换。假设您从某处获取 RGB 数据,并且确实需要将其加载到 RGBA 纹理中。驱动程序具有针对这些转换的高度优化代码,或者它们甚至可能作为 GPU blit 的一部分发生。与您的代码创建另一个数据副本相比,将转换作为副本的一部分执行总是更好。

听起来很困惑?在查看性能时,这些类型的权衡非常普遍。通常情况下,为了使用 OpenGL 获得最佳性能,您需要针对不同的 GPUs/platforms.

使用不同的代码路径。