【发布时间】:2014-01-29 07:44:16
【问题描述】:
我们使用缓冲区对象来减少来自 CPU-GPU 的复制操作,对于纹理缓冲区对象,我们可以将目标从顶点更改为缓冲区对象中的纹理。纹理缓冲区对象还有其他优势吗?另外,它不允许过滤,这样做有什么缺点吗?
【问题讨论】:
我们使用缓冲区对象来减少来自 CPU-GPU 的复制操作,对于纹理缓冲区对象,我们可以将目标从顶点更改为缓冲区对象中的纹理。纹理缓冲区对象还有其他优势吗?另外,它不允许过滤,这样做有什么缺点吗?
【问题讨论】:
缓冲纹理类似于一维纹理,但有一个后备缓冲存储,它不是纹理对象的一部分(与任何其他纹理对象相比),但通过实际的缓冲对象绑定来实现到TEXTURE_BUFFER。使用缓冲区纹理有几个含义,而且,AFAIK 是一个无法映射到任何其他类型纹理的用例。
请注意,缓冲区纹理不是缓冲区对象 - 缓冲区纹理仅使用 glTexBuffer 与缓冲区对象关联。
相比之下,缓冲区纹理可能很大。核心 OpenGL 4.4 规范的表 23.53 和以下定义了 texels MAX_TEXTURE_BUFFER_SIZE 的最小最大值(即实现必须提供的最小值)。存储在缓冲区对象中的潜在纹素数量计算如下(见GL_ARB_texture_buffer_object):
floor(<buffer_size> / (<components> * sizeof(<base_type>))
钳制到MAX_TEXTURE_BUFFER_SIZE 的结果值是可寻址纹素的数量。
示例:
您有一个存储 4MiB 数据的缓冲区对象。您想要的是用于寻址 RGBA 纹素的缓冲区纹理,因此您选择内部格式 RGBA8。可寻址的纹素数为
floor(4MiB / (4 * sizeof(UNSIGNED_BYTE)) == 1024^2 texels == 2^20 texels
如果您的实现支持这个数字,您可以处理缓冲区对象中的所有值。以上内容并不太令人印象深刻,并且可以通过当前实现的任何其他纹理简单地实现。但是,我写这个答案的机器支持2^28 == 268435456 texels。
使用 OpenGL 4.4(和 4.3,可能还有更早的 4.x 版本),MAX_TEXTURE_SIZE 是每个 1D 纹理 2 ^ 16 个纹素,因此缓冲区纹理仍然可以是 4 倍大。在我的本地机器上,我可以分配一个 2GiB 的缓冲区纹理(实际上更大),但在使用 RGBAF32 texels 时只能分配一个 1GiB 的 1D 纹理。
缓冲区纹理的用例是对着色器内的大型数据存储进行随机(和原子,如果需要)读/写访问(后者通过图像加载/存储)。是的,您可以对一个或多个块内的制服数组进行随机读取-访问,但如果您必须处理大量数据并且必须处理多个块,即使这样,它也会变得非常乏味,查看单个阶段的所有统一块中所有统一组件的最大组合大小(其中单个浮点组件的大小为 4 字节),
MAX_(stage)_UNIFORM_BLOCKS *
MAX_UNIFORM_BLOCK_SIZE +
MAX_(stage)_UNIFORM_COMPONENTS * 4
在着色器阶段并没有太多空间可以使用(取决于您的实现允许上述数字有多大)。
纹理和缓冲区纹理之间的一个重要区别是,数据存储作为常规缓冲区对象,可用于纹理根本不起作用的操作中。扩展名提到:
使用缓冲区对象提供存储允许纹理数据 以多种不同的方式指定:通过缓冲区对象加载 (BufferData)、直接 CPU 写入 (MapBuffer)、帧缓冲区回读 (EXT_pixel_buffer_object 扩展)。也可以加载缓冲区对象 通过变换反馈(NV_transform_feedback 扩展),它捕获 由 GL 处理的顶点的选定变换属性。一些 这些机制不需要额外的数据副本,这将是 使用传统的类似 TexImage 的入口点时需要。
使用缓冲区纹理的一个含义是,只能通过texelFetch 在着色器中进行查找。缓冲区纹理也不是 mip-mapped,而且正如您已经提到的,在提取期间没有过滤。
附录:
从 OpenGL 4.3 开始,我们有了所谓的
Shader Storage Buffer。这些也提供对大型数据存储的随机(原子)读/写访问,但不需要像缓冲区纹理那样使用texelFetch() 或图像加载/存储函数进行访问。使用缓冲区纹理还意味着必须处理 gvec4 返回值,包括 texelFetch() 和 imageLoad() / imageStore()。一旦您想使用结构(或其数组)并且您不想使用vec4 的多个实例或使用多个缓冲区纹理来实现类似的东西,这将变得非常乏味。使用作为着色器存储访问的缓冲区,您可以对数据存储进行简单的索引,并直接从缓冲区中提取一些struct {} 的一个或多个实例。
此外,由于它们与统一块非常相似,因此使用它们应该相当简单 - 如果您知道如何使用统一缓冲区,那么您在学习如何使用着色器存储缓冲区方面没有很长的路要走。
也绝对值得浏览相应ARB extension 的问题部分。
性能影响
Daniel Rakos 几年前进行了一些性能分析,包括 comparison of uniform buffers and buffer textures,以及基于 AMD 的 OpenCL 编程指南中的信息的 more general 小注释。现在有一个最新版本,专门针对OpenCL optimization 一个 AMD 平台。
影响性能的因素有很多:
一如既往地担心性能:实施一些可行的方法,看看该解决方案是否足够快以满足您的需求。否则,请实施两种或多种解决问题的方法,概要分析它们并进行比较。
此外,供应商特定指南可以提供大量见解。上述 OpenCL 用户和优化指南提供了有关如何优化 CL 内核的高级架构视角和具体提示——这些内容在开发着色器时也很重要。
【讨论】:
我发现的一个用例是存储每个基元属性(在 gl_PrimitiveID 的帮助下在片段着色器中访问),同时仍保持索引网格中的唯一顶点。
【讨论】: