【问题标题】:What is the purpose of OpenGL texture buffer objects?OpenGL纹理缓冲对象的目的是什么?
【发布时间】:2014-01-29 07:44:16
【问题描述】:

我们使用缓冲区对象来减少来自 CPU-GPU 的复制操作,对于纹理缓冲区对象,我们可以将目标从顶点更改为缓冲区对象中的纹理。纹理缓冲区对象还有其他优势吗?另外,它不允许过滤,这样做有什么缺点吗?

【问题讨论】:

    标签: opengl textures opengl-3


    【解决方案1】:

    缓冲纹理类似于一维纹理,但有一个后备缓冲存储,它不是纹理对象的一部分(与任何其他纹理对象相比),但通过实际的缓冲对象绑定来实现到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 平台。

    影响性能的因素有很多:

    • 访问模式和产生的缓存行为
    • 缓存行大小和内存布局
    • 访问的内存类型(寄存器、本地、全局、L1/L2 等)及其各自的内存带宽
    • 同时执行其他操作可以很好地隐藏内存获取延迟
    • 您使用的是哪种硬件,即具有专用内存或某种统一内存架构的专用显卡
    • 等等等等

    一如既往地担心性能:实施一些可行的方法,看看该解决方案是否足够快以满足您的需求。否则,请实施两种或多种解决问题的方法,概要分析它们并进行比较。

    此外,供应商特定指南可以提供大量见解。上述 OpenCL 用户和优化指南提供了有关如何优化 CL 内核的高级架构视角和具体提示——这些内容在开发着色器时也很重要。

    【讨论】:

    • 您能否解释一下“缓冲区纹理的用例是随机访问着色器内的数据存储。这是统一缓冲区或着色器存储缓冲区无法做到的。”?我的印象是 SSBO 用于随机访问大型数组。
    • 我刚刚熟悉 OpenGL 4 并有这个问题。您的回答说明了缓冲区纹理相对于其他纹理和统一缓冲区的优势,与着色器存储缓冲区相比是否也存在用例/性能差异?
    • @kai:我大幅更新了答案。在大多数情况下,我不明白为什么 SSBO 应该表现得更好或更差,因为它们本质上只是线性数据数组,必须支持随机访问——尽管通过不同的方式。我能想到性能会不同的唯一原因是用于进行缓冲区纹理查找的纹理单元可能更有效地获取数据,但我没有什么可以支持的。试一试并分析 - 为同一问题实施两种方法应该不会花费您太多时间。
    • 嗯。 “缓冲区纹理的用例是随机的(如果需要,也是原子的)读/写访问 [...]” - 可以为此使用非缓冲区纹理,不是吗? :)
    • “这些机制中的一些不需要额外的数据副本” - 你能说一下哪些吗?
    【解决方案2】:

    我发现的一个用例是存储每个基元属性(在 gl_PrimitiveID 的帮助下在片段着色器中访问),同时仍保持索引网格中的唯一顶点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-08
      • 2011-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多