OpenGL:着色器存储缓冲区映射/绑定

时间:2013-10-06 03:33:22

标签: c++ opengl

我目前正在开发一个支持与深度无关的(也称为与订单无关的 alpha混合的程序。为此,我实现了一个每像素链表,使用标题的纹理(每个像素指向链表中的第一个条目)和链表自身的纹理缓冲对象。虽然这很好用,但我想用着色器存储缓冲区作为练习来交换纹理缓冲区对象。

我想我几乎得到了它,但是我花了大约一个星期的时间来达到我可以实际使用着色器存储缓冲区的程度。我的问题是:

  • 为什么我无法映射着色器存储缓冲区?

  • 为什么再次绑定着色器存储缓冲区会出现问题?

对于调试,我只显示着色器存储缓冲区的内容(它还没有包含链接列表)。我按以下方式创建了着色器存储缓冲区:

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);

在着色器中,着色器存储缓冲区定义为:

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

在渲染循环中,我执行以下操作:

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
// ...

此代码有效,但当我用以下代码替换 glBufferSubData 时,

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);

映射失败,返回 GL_INVALID_OPERATION 。似乎着色器存储缓冲区仍然绑定到某些东西,因此无法映射。我读了一些关于 glGetProgramResourceIndex http://www.opengl.org/wiki/GlGetProgramResourceIndex)和 glShaderStorageBlockBinding http://www.opengl.org/wiki/GlShaderStorageBlockBinding)的内容,但我真的不明白。

我的第二个问题是,为什么我不能打电话

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssb);

,也不是

glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssb);
glBufferSubData glMemoryBarrier 之后的渲染循环中

。此代码不应更改,因为这些调用与创建着色器存储缓冲区时相同。如果我无法绑定不同的着色器存储缓冲区,我只能使用一个。但我知道支持多个着色器存储缓冲区,所以我想我错过了其他东西(比如“释放”缓冲区)。

1 个答案:

答案 0 :(得分:1)

首先,glMapBufferRange失败只是因为GL_WRITE_ONLY不是它的有效参数。这用于旧的glMapBuffer,但是glMapBufferRange使用了一组标志来进行更精细的控制。在您的情况下,您需要GL_MAP_WRITE_BIT。而且由于您似乎完全覆盖了整个缓冲区,而没有关注以前的值,因此额外的优化可能是GL_MAP_INVALIDATE_BUFFER_BIT。所以用以下方法替换该呼叫:

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

在问题中没有详细描述另一个错误。但是先修复这个,也许它会帮助解决以下错误。