“着色器存储缓冲区对象”(SSBO)和图像加载存储操作之间有什么区别
什么时候应该使用一个而不是另一个?
它们都可以进行原子操作,我假设它们存储在相同类型的内存中。而且无论它们是否存储在相同类型的内存中,它们是否具有相同的性能特征?
编辑:最初的问题是在 SSBO 和统一缓冲区对象之间询问,它是在 SSBO 和图像加载存储之间。
着色器存储缓冲区对象和图像纹理之间的区别以及为什么要使用它们是它们可以使用接口块。
图像只是纹理,这意味着数据结构中只有 vec4。不仅 vec4,它还可以有其他格式,但数据结构将是许多一种数据类型。
其中,SSBO 是通用的。他们可以在单个接口块中使用 int、float、vec3 数组的组合。
因此,SSBO 比图像纹理更灵活。
您的问题已经或多或少地在http://www.opengl.org/wiki/Shader_Storage_Buffer_Object得到了明确的回答。它说:
SSBO 很像统一缓冲区对象。着色器存储块由接口块 (GLSL) 以与统一块几乎相同的方式定义。存储 SSBO 的缓冲区对象绑定到 SSBO 绑定点,就像制服的缓冲区对象绑定到 UBO 绑定点一样。等等。
它们之间的主要区别是:
SSBO 可以更大。所需的最小 UBO 大小为 16KB;所需的最小 SSBO 大小为 16MB,典型大小约为 GPU 内存大小。
SSBO 是可写的,甚至是原子的;UBO 是制服。SSBO 读写使用不连贯的内存访问,因此它们需要适当的屏障,就像 Image Load Store 操作一样。
SSBO 可以有无限的存储空间,最高可达缓冲区范围;UBO 必须具有特定的、固定的存储大小。这意味着您可以在 SSBO 中拥有任意长度的数组。数组的实际大小,基于缓冲区绑定的范围,可以在运行时在着色器中使用无界数组变量的长度函数查询。
正如其他人所提到的,SSBO 具有更大的存储空间并支持原子操作,接受的答案还提到 SSBO 是通用的,因为它们允许用户组合不同的类型。但就个人而言,我只想指出,我认为这通常很糟糕,在 SSBO 中使用接口块或结构并不总是理想的。这是一个例子:
假设您在 C++ 中有一个这样的结构:
struct Foo {
glm::vec4 position;
glm::vec4 velocity;
glm::vec4 padding_and_range; // range is a float padded to a vec4
};
它对应于 glsl 中的 SSBO 缓冲区:
struct Foo {
vec4 position;
vec4 velocity;
vec4 padding_and_range; // range is a float padded to a vec4
};
layout(std430, binding = 0) readonly buffer SSBO {
Foo data[];
} foo;
尽管 SSBO 缓冲区能够容纳 的数组struct Foo
,但请注意,必须根据std430
内存布局考虑填充,您必须将您的填充float range
到 a vec4
,然后使用foo.data[i].padding_and_range.w
它来访问它。这很容易出错,更不用说浪费内存空间了,尤其是当您的 SSBO 很大(用于计算着色器)并且您的Foo
结构很复杂(需要大量填充)时。除此之外,您经常需要像这样在循环中填充缓冲区数据:
Foo* foos = reinterpret_cast<Foo*>(glMapNamedBufferRange(ssbo, offset, size, GL_MAP_READ_BIT));
for (int i = 0; i < n_foos; i++) {
Foo& foo = foos[i];
foo.position = glm::vec4(1.0f);
foo.velocity = glm::vec4(2.0f);
foo.padding_and_range = glm::vec4(glm::vec3(0.0f), 3.5f);
}
glUnmapNamedBuffer(ssbo);
glNamedBufferData
而不是简单地使用or一次性将数据写入其中glNamedBufferSubData
。
处理 struct 的更好方法是将每个 struct 元素存储到一个单独的 SSBO 中,以便每个 SSBO 缓冲区数组都紧凑且同质。尽管性能可能不会更好,但它有助于保持代码干净且更具可读性。除了使用结构之外,您还想使用:
layout(std430, binding = 0) buffer FooPosition {
vec4 position[];
};
layout(std430, binding = 1) buffer FooVelocity {
vec4 velocity[];
};
layout(std430, binding = 2) buffer FooRange {
float range[];
};