【问题标题】:OpenGL optimised representation of texturesOpenGL 优化的纹理表示
【发布时间】:2015-03-11 16:19:37
【问题描述】:

我读过很多地方,应该避免使用 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 表示,将 internalformat 参数指定为 GL_RGBA 并将 format 参数指定为 GL_RGB 就足够了?生成贴图的时候会不会有格式之间的内部转换?

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

【问题讨论】:

  • 对于GL_RGBGL_RGBA,任何真实世界的GPU 都将使用每个纹素4 字节对齐作为internalFormat。您所指的建议只是想确保选择与 GPU 将在内部使用的最匹配的 客户端 format,而不是切换 internalFormat。你不会在你的场景中获得任何东西。
  • 抱歉有点糊涂了。因此,如果我的数据是 RGB,如果我用 internalformat=GL_RGBA 和 format=GL_RGB 调用它,我会失去任何性能吗?
  • 当您的输入是GL_RGB 时,选择GL_RGBA 而不是GL_RGB 作为internalFormat 不会有任何影响。你还担心什么?纹理上传或纹理采样的性能(在这种情况下两者都不会改变,但您的问题不是很清楚)。
  • 客户端内存中的数据将与 GPU 内存中数据的内部组织完全不同。这不仅仅是 2D 数组的线性存储,就像人们可能天真地假设的那样。数据将以更加缓存友好的方式组织,通常使用 Z 阶曲线一般原理的一些高级版本,并且特定格式是高度特定于实现的。这也是为什么在客户端应该首选对齐良好的数据格式的原因 - 在任何情况下都会发生重播,但对齐会使该过程更加高效。
  • 是的。使用纹理根本不会对 RGB 与 RGBA 造成任何损失。只有上传有一些额外的工作。实际上,这个成本并没有听起来那么高。通常,限制是总线传输(和多个数据副本)。如果您关心快速纹理上传/流式传输,则应使用 PBO 并注意避免隐式同步。

标签: opengl graphics 3d textures


【解决方案1】:

我不同意您提出的避免使用 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 操作期间进行格式转换。

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

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

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

听起来令人困惑?在查看性能时,这些权衡是非常常见的。通常情况下,要获得 OpenGL 的最佳性能,您需要为不同的 GPU/平台使用不同的代码路径。

【讨论】:

    【解决方案2】:

    我是否正确假设如果我有一个 RGB 图像并且我想要一个 RGBA 表示,将 internalformat 参数指定为 GL_RGBA 并将 format 参数指定为 GL_RGB 就足够了?生成贴图时会不会有格式之间的内部转换?

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

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

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

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

    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 级灰度)用于内部格式。

    【讨论】:

    • 顺便说一下,避免使用尺寸过小的内部格式。它们在 GL 3.1 中被删除,并且对于数据的内部表示方式总是产生一些歧义。为了获得最佳像素传输性能,您需要知道内部使用的位数。因此,您可以使用 GL_RGBA8 (8-bits per-component) 而不是 GL_RGBA (unsized) 作为内部格式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    • 1970-01-01
    • 2014-07-10
    • 2012-11-22
    • 1970-01-01
    相关资源
    最近更新 更多