Skip to content

着色器存储缓冲对象(Shader Storage Buffer Object, SSBO)

着色器存储缓冲对象(SSBO)是用于在 OpenGL 着色语言(GLSL)中存储和检索数据的缓冲对象。

SSBO 与统一缓冲对象(UBO)非常相似:着色器存储块的定义方式与统一块几乎相同,存储 SSBO 的缓冲对象绑定到 SSBO 绑定点,就像 UBO 一样。

与 UBO 的主要区别

特性SSBOUBO
最大大小保证至少 128MB,通常可达 GPU 内存上限保证至少 16KB
可写性可读写,支持原子操作只读
存储大小可变大小,支持运行时确定长度的数组固定大小
访问速度通常较慢通常较快
内存一致性需要 barrier 同步无需同步

性能考量

SSBO 访问通常比 UBO 慢。功能上,SSBO 可以视为通过图像加载存储(Image Load Store)访问的缓冲纹理(Buffer Texture)的更好接口。

着色器声明

SSBO 在 GLSL 中使用 buffer 关键字声明为接口块:

glsl
layout(std430, binding = 3) buffer DataBuffer {
    int data_SSBO[];
};

可变长度数组

SSBO 支持在块末尾声明可变长度数组:

glsl
layout(std430, binding = 2) buffer AnotherBlock {
    int some_int;
    float fixed_array[42];
    float variable_array[];  // 必须是最后一个成员
};

着色器中可以使用 length() 函数获取数组长度:

glsl
int count = variable_array.length();

内存限定符

存储块及其成员可以使用内存限定符:

限定符说明
coherent强制内存访问一致性,允许多个着色器调用之间通信
volatile告知编译器内存内容可能随时被外部修改
restrict承诺该变量是唯一能修改其所见内存的变量,允许编译器优化
readonly变量只能用于读取操作
writeonly变量只能用于写入操作
glsl
layout(std430, binding = 0) coherent restrict buffer MyBuffer {
    readonly int flags[];
    writeonly int results[];
};

限定符组合

writeonlyreadonly 不是互斥的——同时标记的变量仍可查询资源信息(如 image 变量的大小)。

内存同步

由于 SSBO 使用非一致内存访问,写入后需要适当的屏障:

glsl
// 在同一工作组内同步
barrier();
memoryBarrierBuffer();

// 在计算着色器中使用共享变量后
memoryBarrierShared();
barrier();

原子操作

SSBO 支持 intuint 类型的原子操作:

glsl
int atomicAdd(inout int mem, int data);
int atomicMin(inout int mem, int data);
int atomicMax(inout int mem, int data);
int atomicAnd(inout int mem, int data);
int atomicOr(inout int mem, int data);
int atomicXor(inout int mem, int data);
int atomicExchange(inout int mem, int data);
int atomicCompSwap(inout int mem, int compare, int data);

返回值

所有原子函数返回操作之前的原始值。

原子操作示例

glsl
layout(std430, binding = 0) buffer Counter {
    uint globalCounter;
};

void increment() {
    uint old = atomicAdd(globalCounter, 1);
    // old 是增加前的值
}

OpenGL 使用

创建和绑定

cpp
int data[SIZE];

GLuint ssbo;
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo);  // 绑定到绑定点 3
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);  // 解绑

部分更新

cpp
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, size, data);

读取数据

cpp
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
void* ptr = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
// 读取数据
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

内存屏障

SSBO 写入后,如果需要在后续绘制调用中使用,需要插入内存屏障:

cpp
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

数据结构映射

C/C++ 结构体可以映射到 SSBO:

cpp
struct ssbo_data {
    int foo;
    float bar[42];
    float baz[MY_SIZE];
};
glsl
layout(std430, binding = 0) buffer SSBOData {
    int foo;
    float bar[42];
    float baz[];
};

参考

  • 核心版本:OpenGL 4.3
  • 核心扩展:ARB_shader_storage_buffer_object

基于 OpenGL Wiki 的本地化项目