问题描述
我需要在计算着色器中为数组的每个条目传递两个布尔值。现在,我从 cpu 获取它们,但稍后我想从另一个在该计算着色器之前运行的计算着色器生成这些值。我按如下方式工作:
使用 glm::bvec2
我可以将相对打包的布尔值放入内存(bvec 每字节存储一个布尔值。可能会更好,但现在可以了,我总是可以手动打包)。然后,我使用 vkMapMemory
将数据放入 Vulkan 缓冲区(然后我将其复制到设备本地缓冲区,但这在这里可能无关紧要)。
不幸的是,GLSL 的 bvec2 并不等同于它(或者至少如果我使用它,它不会给我预期的值,也许我做错了?使用 bvec2 changes[]
在以下情况下会产生错误的结果代码。我怀疑对齐不匹配)。因此,计算着色器按如下方式访问该数组:
layout (binding = 2,scalar) buffer Changes
{
uint changes[];
};
void main() {
//uint is 4 byte,glm::bvec2 is 2 byte
uint changeIndex = gl_GlobalInvocationID.x / 2;
//order seems to be reversed in memory:
//v1(x1,y1) followed by v2(x2,y2) is stored as: 0000000(y2) 0000000(x2) 0000000(y1) 0000000(x1)
uint changeOffset = (gl_GlobalInvocationID.x % 2) * 16;
uint maskx = 1 << (changeOffset + 0);
uint masky = 1 << (changeOffset + 8);
uint uchange = changes[changeIndex];
bvec2 change = bvec2(uchange & maskx,uchange & masky);
}
这有效。进行了一些试验和错误,但我们去了。我现在有两个问题:
在这个应用程序中,性能对我来说非常重要,因为我正在尝试对事物进行基准测试。内存使用优化是次要的,但也值得考虑。由于在优化 GLSL 方面相对缺乏经验,我很高兴您能给我任何建议。
解决方法
既然 glm::bvec2
将一个布尔值存储为两个字节,那么 GL_EXT_shader_8bit_storage
extension 提供的显式 8 位无符号整数向量类型 u8vec2
在这里会更方便吗?不过,我不知道您使用的 Vulkan 驱动程序是否支持必要的功能(我假设它是 storageBuffer8BitAccess
)。
Andrea 的评论提到了一个有用的扩展名:GL_EXT_shader_8bit_storage
。
由于对布尔值的写入访问是并行完成的,因此紧密打包的唯一选择是原子。我选择通过在一个字节中存储两个布尔值来交换内存效率以换取性能,“浪费”6 位。代码如下:
#extension GL_EXT_shader_8bit_storage : enable
void getBools(in uint data,out bool split,out bool merge) {
split = (data & 1) > 0;
merge = (data & 2) > 0;
}
uint setBools(in bool split,in bool merge) {
uint result = 0;
if (split) result = result | 1;
if (merge) result = result | 2;
return result;
}
//usage:
layout (binding = 4,scalar) buffer ChangesBuffer
{
uint8_t changes[];
};
//[...]
bool split,merged;
getBools(uint(changes[invocationIdx]),split,merged);
//[...]
changes[idx] = uint8_t(setBools(split,merge));
注意构造函数,扩展提供的数据类型不提供任何算术运算,使用前必须转换。