2

I'm currently working on a program which supports depth-independent (also known as order-independent) alpha blending. To do that, I implemented a per-pixel linked list, using a texture for the header (points for every pixel to the first entry in the linked list) and a texture buffer object for the linked list itself. While this works fine, I would like to exchange the texture buffer object with a shader storage buffer as an excercise.

I think I almost got it, but it took me about a week to get to a point where I could actually use the shader storage buffer. My question are:

  • Why I can't map the shader storage buffer?

  • Why is it a problem to bind the shader storage buffer again?

For debugging, I just display the contents of the shader storage buffer (which doesn't contain a linked list yet). I created the shader storage buffer in the following way:

glm::vec4* bufferData = new glm::vec4[windowOptions.width * windowOptions.height];
glm::vec4* readBufferData = new glm::vec4[windowOptions.width * windowOptions.height];

for(unsigned int y = 0; y < windowOptions.height; ++y)
{
    for(unsigned int x = 0; x < windowOptions.width; ++x)
    {
        // Set the whole buffer to red
        bufferData[x + y * windowOptions.width] = glm::vec4(1,0,0,1);
    }
}

GLuint ssb;
// Get a handle
glGenBuffers(1, &ssb);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssb);
// Create buffer
glBufferData(GL_SHADER_STORAGE_BUFFER, windowOptions.width * windowOptions.height * sizeof(glm::vec4), bufferData, GL_DYNAMIC_COPY);
// Now bind the buffer to the shader
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssb);

In the shader, the shader storage buffer is defined as:

layout (std430, binding = 0) buffer BufferObject
{
    vec4 points[];
};

In the rendering loop, I do the following:

glUseProgram(defaultProgram);

for(unsigned int y = 0; y < windowOptions.height; ++y)
{
    for(unsigned int x = 0; x < windowOptions.width; ++x)
    {
        // Create a green/red color gradient
        bufferData[x + y * windowOptions.width] =
            glm::vec4((float)x / (float)windowOptions.width,
            (float)y / (float)windowOptions.height, 0.0f, 1.0f);
    }
}

glMemoryBarrier(GL_ALL_BARRIER_BITS);  // Don't know if this is necessary, just a precaution
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, windowOptions.width * windowOptions.height * sizeof(glm::vec4), bufferData);
// Retrieving the buffer also works fine
// glMemoryBarrier(GL_ALL_BARRIER_BITS);
// glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, windowOptions.width * windowOptions.height * sizeof(glm::vec4), readBufferData);

glMemoryBarrier(GL_ALL_BARRIER_BITS);  // Don't know if this is necessary, just a precaution

// Draw a quad which fills the screen
// ...

This code works, but when I replace glBufferSubData with the following code,

glm::vec4* p = (glm::vec4*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, windowOptions.width * windowOptions.height, GL_WRITE_ONLY);
for(unsigned int x = 0; x < windowOptions.width; ++x)
{
    for(unsigned int y = 0; y < windowOptions.height; ++y)
    {
        p[x + y * windowOptions.width] = glm::vec4(0,1,0,1);
    }
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

the mapping fails, returning GL_INVALID_OPERATION. It seems like the shader storage buffer is still bound to something, so it can't be mapped. I read something about glGetProgramResourceIndex (http://www.opengl.org/wiki/GlGetProgramResourceIndex) and glShaderStorageBlockBinding (http://www.opengl.org/wiki/GlShaderStorageBlockBinding), but I don't really get it.

My second question is, why I can neither call

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssb);

, nor

glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssb);

in the render loop after glBufferSubData and glMemoryBarrier. This code should not change a thing, since these calls are the same as during the creation of the shader storage buffer. If I can't bind different shader storage buffers, I can only use one. But I know that more than one shader storage buffer is supported, so I think I'm missing something else (like "releasing" the buffer).

4
  • "My second question is, why I can neither call...in the render loop after..." - In which way "can't" you call those functions? You can always call them, rather tell us what happens if you do. Commented Oct 6, 2013 at 10:16
  • When I call them (and check for errors) after I write to the buffer (and wait for glMemoryBarrier), glGetError() returns GL_INVALID_OPERATION. This only happens when I write to the buffer, not when reading from it. Commented Oct 6, 2013 at 12:59
  • That's not quite right. Both calls go through without error, but in the next frame, glBufferSubData or glMapBufferRange both fail with GL_INVALID_OPERATION. Commented Oct 6, 2013 at 13:16
  • Now that you have it working, does the SSBO make any difference compared to the texture buffer apart from syntax? Commented Nov 9, 2013 at 6:04

1 Answer 1

1

First of all, the glMapBufferRange fails simply because GL_WRITE_ONLY is not a valid argument to it. That was used for the old glMapBuffer, but glMapBufferRange uses a collection of flags for more fine-grained control. In your case you need GL_MAP_WRITE_BIT instead. And since you seem to completely overwrite the whole buffer, without caring for the previous values, an additional optimization would probably be GL_MAP_INVALIDATE_BUFFER_BIT. So replace that call with:

glm::vec4* p = (glm::vec4*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 
                    windowOptions.width * windowOptions.height, 
                    GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

The other error is not described that well in the question. But fix this one first and maybe it will already help with the following error.

Sign up to request clarification or add additional context in comments.

2 Comments

It's almost right. I first got segfaults, but the I realized that the length argument was wrong, it has to be "windowOptions.width * windowOptions.height * sizeof(glm::vec4)", since it's not the number of elements, but the length in bytes. Now it works nicely. Thanks! But I still don't get why I can't call either glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssb) nor glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssb) after I write to the buffer. To be more precise, the call itself is alright, but in the next frame, the mapping fails.
Okay, I got it. I had a local variable of the same name inside the main function and a global variable. I created the buffer using the local variable, and tried to set the the active buffer to the global variable, which, of course, was not a valid buffer. Now everything works fine, thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.