缓冲纹理类似于一维纹理,但具有不是纹理对象的后备缓冲存储区(与任何其他纹理对象相反),而是通过绑定到TEXTURE_BUFFER
的实际缓冲对象来实现。使用缓冲纹理具有几个含义和一个用例,我认为它不能映射到任何其他类型的纹理。
请注意,缓冲纹理不是缓冲对象-缓冲纹理仅使用glTexBuffer与缓冲对象关联。
相比之下,缓冲纹理可以非常巨大。核心OpenGL 4.4规范的表23.53及其后续定义了最小最大值(即实现必须提供的最小值)texelsMAX_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
个纹素。
使用OpenGL 4.4(以及4.3和可能的早期4.x版本),MAX_TEXTURE_SIZE
是每个1D纹理2 ^ 16像素,因此缓冲区纹理仍然可以大4倍。在我的本地机器上,我可以分配一个2GiB的缓冲区纹理(实际上更大),但只有一个1GiB的1D纹理,当使用RGBAF32
纹素时。
缓冲区纹理的用例是随机(如果需要,则为原子)读/写访问(通过图像加载/存储)到着色器内部的大型数据存储。是的,您可以对一个或多个块内的uniform数组进行随机读取访问,但如果您必须处理大量数据并且必须使用多个块进行工作,那么这将变得非常繁琐,即使在单个阶段的所有uniform组件的最大组合大小(其中单个float组件的大小为4字节)中查看所有uniform块的最大组合大小,
MAX_(stage)_UNIFORM_BLOCKS * MAX_UNIFORM_BLOCK_SIZE + MAX_(stage)_UNIFORM_COMPONENTS * 4
在着色器阶段中并不是真正要处理的空间很大(取决于您的实现允许上述数字的最大大小)。
纹理和缓冲纹理之间的一个重要区别是,数据存储作为常规缓冲对象可以用于纹理无法工作的操作。该扩展提到:texelFetch
完成。缓冲纹理也没有mip-mapped,并且如您已经提到的,在提取期间没有过滤。
附录:texelFetch()
或图像加载/存储函数进行访问。使用缓冲纹理还意味着必须处理gvec4
返回值,无论是使用texelFetch()
还是imageLoad()
/imageStore()
。一旦您想要使用结构(或其数组),并且您不想考虑使用多个vec4
实例或使用多个缓冲纹理来实现类似的东西时,这将变得非常乏味。通过作为着色器存储访问的缓冲区,您可以简单地索引到数据存储并直接从缓冲区中提取一个或多个struct {}
实例。浏览相应ARB扩展的问题部分也是非常值得的。
性能影响
Daniel Rakos多年前进行了一些性能分析,既包括对统一缓冲区和缓冲区纹理的比较comparison of uniform buffers and buffer textures,也基于AMD OpenCL编程指南中的信息进行了一些更广泛的探索more general。现在有一个针对OpenCL optimization AMD平台的最新版本。
有许多因素会影响性能:
像往常一样,在关注性能时:实现一个可行的解决方案,并查看该解决方案是否足够快速满足您的需求。否则,请实现两个或多个解决问题的方法,分析它们并进行比较。
此外,供应商特定的指南可以提供大量见解。上述OpenCL用户和优化指南提供了高级架构视角和有关如何优化CL内核的具体提示-这些内容在开发着色器时也很相关。
我发现的一个用例是,在索引网格中保持唯一顶点的同时存储每个基元属性(通过gl_PrimitiveID在片段着色器中访问)。