Make a shader storage buffer in different shader programs accessible

1.6k Views Asked by At

What layout and binding do i have to do to make a (working) shader storage buffer readable in a second shader program? I set up and populated a SSBO which i bound successfully and used in a geometry shader. That shader reads and writes to that SSBO - no problems so far. No rendering done there.
In the next step, my rendering pass (second shader program) shall have access to this data. The idea is to have a big data set while the vertex shader of the second program only uses some indices per render call to pick certain values of that SSBO.

Do i miss some specific binding commands or did i place them at the wrong spot?
Is the layout consistent in both programs? Did i mess up the instances?
I just can't find any examples of a SSBO used in two programs..

Creating, populating and binding:

float data[48000];
data[0] = -1.0;
data[1] = 1.0;

data[2] = -1.0;
data[3] = -1.0;

data[4] = 1.0;
data[5] = -1.0;

data[6] = 1.0;
data[7] = 1.0;

data[16000] = 0.0;
data[16001] = 1.0;

data[16002] = 0.0;
data[16003] = 0.0;

data[16004] = 1.0;
data[16005] = 0.0;

data[16006] = 1.0;
data[16007] = 1.0;


GLuint ssbo;
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), &data, GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo);  

Instancing in geometry shader

layout(std140, binding = 1) buffer mesh
{
    vec2 points[8000];
    vec2 texs[8000];
    vec4 colors_and_errors[8000];
} mesh_data;  

Second instance in the vertex shader of the other program

layout(std140, binding = 1) buffer mesh
{
    vec2 points[8000];
    vec2 texs[8000];
    vec4 colors_and_errors[8000];
} mesh_data;  

Are the instances working against each other? Right now am not posting my bindings done in the render loop since i am not sure what i am doing there. I tried to bind before/ after changing the used program; without success.
Does anybody have an idea?

EDIT: Do i also have to bind the SSBO to the second program outside of the render loop? In a different way than the first binding?

EDIT: Although i did not solve this particular problem, i found a work-around that might be even more in the sense of opengl.
I used the SSBO of the first program as vertex attributes in the second program. This and the indexed-rendering function of opengl solved this issue.

(Should this be marked as solved?)

1

There are 1 best solutions below

3
On

It seems like you're most of the way there, but there are a few things you should watch out for.

Is the layout consistent in both programs? layout(std140, binding = 1) buffer mesh

You need to be careful about this layout. std140 will round up alignments to vec4, so will no longer line up with the data you're providing from the C code. In this case, std430 should work for you.

Do i also have to bind the SSBO to the second program outside of the render loop? In a different way than the first binding?

Once you've bound the SSBO once, assuming both programs are using the same binding point (in your example, they are) then you should be fine. Sharing data between programs is fine, but synchronisation is required. You can enforce this with a memory barrier.

You don't mention VAOs, but you will only be able to use SSBOs after you've bound a VAO (not on the default one).

I think this might be best explained with an example.

Vertex shader for the first program. It uses the buffer data for its position and texture coords and then flips the positions in Y.

layout(std430, binding = 1) buffer mesh {
    vec4 points[3];
    vec2 texs[3];
} mesh_data;
out highp vec2 coords;
void main() {
    coords = mesh_data.texs[gl_VertexID];
    gl_Position = mesh_data.points[gl_VertexID];
    mesh_data.points[gl_VertexID] = vec4(gl_Position.x, -gl_Position.y, gl_Position.zw);
}

Verted shader for the second program. It just uses the data but doesn't modify it.

layout(std430, binding = 1) buffer mesh {
    vec4 points[3];
    vec2 texs[3];
} mesh_data;
out highp vec2 coords;
void main() {
    coords = mesh_data.texs[gl_VertexID];
    gl_Position = mesh_data.points[gl_VertexID];
}

In the application, you need to bind a VAO.

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

Then setup your SSBO.

float const data[] = {
    -0.5f, -0.5f, 0.0f, 1.0,
    0.0f,  0.5f,  0.0f, 1.0,
    0.5f,  -0.5f, 0.0f, 1.0,

    0.0f, 0.0f,
    0.5f, 1.0f,
    1.0f, 0.0f
};
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo);

Make the draw calls using the first program.

glUseProgram(first_program);
glDrawArrays(GL_TRIANGLES, 0, 3);

Insert a memory barrier to ensure the writes complete from the preceding draw call before the next draw call tries to read from the buffer.

glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

Make the draw calls using the second program.

glUseProgram(second_program);
glDrawArrays(GL_TRIANGLES, 0, 3);

I hope that clarifies things! Let me know if you have any further questions.