如何创建一个紧密打包的无符号字节统一缓冲区?

Posted

技术标签:

【中文标题】如何创建一个紧密打包的无符号字节统一缓冲区?【英文标题】:How can I create a tightly packed uniform buffer of unsigned bytes? 【发布时间】:2020-02-29 20:49:10 【问题描述】:

基本上是标题。这是一个包含数百万个数字的数组,所以我想尽量减少它在显卡上占用的空间。不幸的是,std140 布局中的数组具有 16 字节的基本对齐方式,而 GLSL 没有内置的 unsigned char 类型,所以我不确定如何有效地做到这一点。

【问题讨论】:

这个问题仍然缺乏细节,例如数据的大小、访问模式等。GPU 确实通过纹理和图像对象原生支持 8 位整数类型。除此之外,您可以通过uints 的数组将这些东西打包成一个线性SSBO。 通常,数组对齐指定数组中的第一个元素将在 16 字节边界上,而不是每个元素都将从 16 字节边界开始。 @jwdonahue:不在具有sdt140 布局的OpenGL 中。但是更高版本支持具有std430 布局的SSBO,这点不同。 你能把每个整数打包四个字节,然后在你的着色器中解码每个字节吗?整数的对齐方式为 4,因此应该正确打包。 @wcochran 不,每个数组元素的对齐方式是 std140 布局中 16 个字节的倍数,无论确切类型如何 【参考方案1】:

GLSL 没有与字节等效的类型。但是由于它的所有类型都由特定数量的字节组成,因此您可以从更大的类型中提取所需的字节。如uint,要求大小为32位。

当然,字节序将是一个问题。声明 GPU 的字节序与为其提供数据的 CPU 的字节序相匹配。但是,当您从特定的 uint 获取字节时,您必须考虑到这一点。

当然,std140 布局不会让您创建一个紧密排列的 uint 数组。所以不要;创建一个紧密排列的 uvec4 数组。那是 4 个整数。

所以要从这样的 UBO 中获取特定的无符号字节,您必须这样做:

layout(binding = #, std140) uniform BlockName

  uvec4 byte_array[NUM_BYTES / 16]; //NUM_BYTES must be a multiple of 16.
;

uint get_byte(uint byte_ix)

  uint byte_in_uint = byte_ix % 4;
  uint uint_in_vec = (byte_ix / 4) % 4;
  uint vec_ix = byte_ix / 16;

  uint bytes = byte_array[vec_ix][uint_in_vec];
  return (bytes >> ((4 - byte_in_uint) * 8)) && 0xFF; //Little-endian. For Big-endian, remove the "4 -" part.

话虽这么说:

这是一个包含数百万个数字的数组

嗯,大多数卡片不允许您通过一个统一的块访问数百万个内容。统一块大小的限制仅保证至少为 16KB,大小通常在 64KB 左右。几乎没有“数百万”的东西。缓冲区对象本身可以保存您可以让系统分配的任何内容,但任何特定的glBindBufferRange(GL_UNIFORM_BUFFER, ...) 调用都必须限制为统一块大小。

如果您需要单个着色器来潜在地访问更多数据,那么您将不得不使用 SSBO 或缓冲区纹理。

【讨论】:

以上是关于如何创建一个紧密打包的无符号字节统一缓冲区?的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO2:缓冲区

带字节的着色器存储缓冲区对象

如何读取和写入位到字节数组

如何将无符号字符数组存储为浮点值?

在 Java 中使用字节缓冲区创建数据包字节数组

在字节缓冲区中复制结构