Bind an SSBO to a fragment shader

DomAyre picture DomAyre · Jul 31, 2014 · Viewed 7.6k times · Source

I have a an SSBO which stores vec4 colour values for each pixel on screen and is pre populated with values by a compute shader before the main loop.

I'm now trying to get this data onscreen which I guess involves using the fragment shader (Although if you know a better method for this I'm open to suggestions)

So I'm trying to get the buffer or at least the data in it to the fragment shader so that I can set the colour of each fragment to the corresponding value in the buffer but I cannot find any way of doing this?

I have been told that I can bind the SSBO to the fragment shader but I don't know how to do this? Other thoughts I had was somehow moving the data from the SSBO to a texture but I can't work that out either

UPDATE:

In response thokra's excellent answer and following comments here is the code to set up my buffer:

//Create the buffer
GLuint pixelBufferID;
glGenBuffers(1, &pixelBufferID);

//Bind it
glBindBuffer(GL_SHADER_STORAGE_BUFFER, pixelBufferID);

//Set the data of the buffer
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * window.getNumberOfPixels, new vec4[window.getNumberOfPixels], GL_DYNAMIC_DRAW);

//Bind the buffer to the correct interface block number
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, pixelBufferID);

Then I call the compute shader and this part works, I check the data has been populated correctly. Then in my fragment shader, just as a test:

layout(std430, binding=0) buffer PixelBuffer
{
    vec4 data[];
} pixelBuffer

void main()
{
    gl_FragColor = pixelBuffer.data[660000];
}

What I've noticed is that it seems to take longer and longer the higher the index so at 660000 it doesn't actually crash its just taking an silly amount of time.

Answer

thokra picture thokra · Jul 31, 2014

Storage buffers work quite similarly to uniform buffers. To get a sense of how those work I suggest something like this. The main differences are that storage buffer can hold substantially higher amounts of data and the you can randomly read from and write to them.

There are multiple angles of working this, but I'll start with the most basic one - the interface block inside your shader. I will only describe a subset of the possibilities when using interface blocks but it should be enough to get you started.

In contrast to "normal" variables, you cannot specify buffer variables in the global scope. You need to use an interface block (Section 4.3.9 - GLSL 4.40 Spec) as per Section 4.3.7 - GLSL 4.40 Spec:

The buffer qualifier can be used to declare interface blocks (section 4.3.9 “Interface Blocks”), which are then referred to as shader storage blocks. It is a compile-time error to declare buffer variables at global scope (outside a block).

Note that the above mentioned section differs slightly from the ARB extension.

So, to get access to stuff in your storage buffer you'll need to define a buffer interface block inside your fragment shader (or any other applicable stage):

layout (binding = 0) buffer BlockName
{
  float values[]; // just as an example
}; 

Like with any other block without an instance name, you'll refer to the buffer storage as if values were at global scope, e.g.:

void main()
{
  // ...

  values[0] = 1.f;

  // ...
}    

On the application level the only thing you now need to know is that the buffer interface block BlockName has the binding 0 after the program has been successfully linked.

After creating a storage buffer object with your application, you first bind the buffer to the binding you specified for the corresponding interface block using

glBindBufferBase(GLenum target​, GLuint index​, GLuint buffer​);

for binding the complete buffer to the index or

glBindBufferRange(GLenum target​, GLuint index​, GLuint buffer​, GLintptr offset​, GLsizeiptr size​);

for binding a subset specified by an offset and a number of of the buffer to the index.

Note that index refers to the binding specified in your layout for the corresponding interface block.

And that's basically it. Be aware that there are certain limits for the storage buffer size, the number of binding points, maximum storage block sizes and so on. I refer you to the corresponding sections in the GL and GLSL specs.

Also, there is a minimal example in the ARB extension. Reading the issues sections of extension also often provides further insight into the exposed functionality and the rationale behind it. I advise you to read through it.

Leave a comment if you run into problems.