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

Posted

技术标签:

【中文标题】OpenGL:着色器存储缓冲区映射/绑定【英文标题】:OpenGL: Shader storage buffer mapping/binding 【发布时间】:2013-10-06 03:33:22 【问题描述】:

我目前正在开发一个支持depth-independent(也称为order-independentalpha blending的程序。为此,我实现了一个每像素链接列表,使用标题的纹理(每个像素指向链接列表中的第一个条目)和链接列表本身的纹理缓冲区对象.虽然这工作正常,但我想将纹理缓冲区对象与着色器存储缓冲区交换作为练习。

我想我差不多明白了,但我花了大约一周的时间才真正可以使用着色器存储缓冲区。我的问题是:

为什么我不能映射着色器存储缓冲区?

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

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

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

glBufferSubDataglMemoryBarrier 之后的渲染循环中。这段代码不应该改变任何事情,因为这些调用与创建着色器存储缓冲区期间的调用相同。如果我不能绑定不同的着色器存储缓冲区,我只能使用一个。但我知道支持多个着色器存储缓冲区,所以我认为我缺少其他东西(比如“释放”缓冲区)。

【问题讨论】:

“我的第二个问题是,为什么我不能在...之后的渲染循环中调用...” - 以哪种方式“不能” 你调用那些函数?你可以随时打电话给他们,如果你打电话告诉我们会发生什么。 当我在写入缓冲区(并等待 glMemoryBarrier)后调用它们(并检查错误)时,glGetError() 返回 GL_INVALID_OPERATION。这只发生在我写入缓冲区时,而不是从它读取时。 这不太对。两个调用都没有错误地通过,但在下一帧中,glBufferSubData 或 glMapBufferRange 都失败并显示 GL_INVALID_OPERATION。 既然你已经开始工作了,除了语法之外,SSBO 与纹理缓冲区相比有什么不同吗? 【参考方案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);

问题中没有很好地描述另一个错误。但是先解决这个问题,也许它已经有助于解决以下错误。

【讨论】:

几乎是对的。我第一次遇到段错误,但我意识到长度参数是错误的,它必须是“windowOptions.width * windowOptions.height * sizeof(glm::vec4)”,因为它不是元素的数量,而是长度字节。现在它工作得很好。谢谢!但是我仍然不明白为什么在写入缓冲区后不能调用 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssb) 或 glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssb) 。更准确地说,调用本身是好的,但是在下一帧,映射失败。 好的,我明白了。我在主函数中有一个同名的局部变量和一个全局变量。我使用局部变量创建了缓冲区,并尝试将活动缓冲区设置为全局变量,这当然不是有效的缓冲区。现在一切正常,再次感谢!

以上是关于OpenGL:着色器存储缓冲区映射/绑定的主要内容,如果未能解决你的问题,请参考以下文章

着色器存储缓冲区中的 OpenGL 顶点

OpenGL着色器存储缓冲区/memoryBarrierBuffer

帧缓冲区和在opengl中使用着色器

将 C++ 容器提供给 OpenGL?

无法从 OpenGL 中的着色器访问先前渲染的纹理

如何在 WebGL 中实现阴影映射?