SSBO的内容在c++函数调用后失效



我有一个名为sparseMatrix的SSBO,其操作顺序如下:

void callerFunc()
{
    func1();
    func2();
}
/* Clear buffer data store and fill with compute shader */
void func1()
{
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, sparseMatrix);
    GLfloat floatZero = 0.0f;
    glClearBufferSubData(GL_SHADER_STORAGE_BUFFER, GL_R32F, **EDIT: 0**, sizeof(GLfloat)*size, GL_RED, GL_FLOAT, &floatZero);
    /* use shader program, bind uniforms */
    glDispatchCompute(numWorkGroups,1,1); // fills buffer by adding a few numbers
}
/* Download data store contents and print */
void func2()
{
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, sparseMatrix);
    GLfloat* temp = new GLfloat[size];
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLfloat)*size, temp);
    /* print values to console */
}

func1()func2()之间没有呼叫。

打印到控制台的值是垃圾(每个浮点数都是-107374176.000000)。我在两台机器上进行了测试,一台是GeForce GTX 570,另一台是GeForce GT 750M,结果完全相同,包括下面的修改。驱动版本为335.23.

我尝试对代码进行以下所有更改(每个更改单独):

  • 如果我移动func2()的内容到func1()的末尾,值会变好。
  • 如果我把func2()的内容直接移动到callerFunc(),值就会变好。
  • 如果我在func1()的末尾在SSBO上添加一个额外的glGetBufferSubData()调用,func2()中的值查询结果很好。
  • 如果我在glClearBuffer呼叫之后或func1()结束时放置glFinish(),则func2()中的值是正确的。如果我把glFinish()放在func2()的开头,它不会改变任何东西。
  • glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT)放在任何地方也没有用。

有人能解释这种奇怪的行为吗?

EDIT:我用计算机着色器替换了对glClearBufferSubData(...)的调用,该着色器用恒定值填充数据存储,现在行为如预期的那样。但是我仍然不知道是什么引起了这个问题。

编辑2:谢谢你的回答,但实际上我用对了。当我在这里发布代码时,我忘记了输入offset参数,对不起:(我在另一个连续计算调度的长列表中再次遇到了这个问题。我尝试了很多东西,最终它帮助放置了GL_TEXTURE_FETCH_BARRIER_BIT内存屏障,而不是GL_SHADER_STORAGE_BARRIER_BIT屏障,尽管计算着色器纯粹在ssbo上工作。我不知道为什么。

错误在于您如何使用

glClearBufferSubData

函数。

看说明书:

glClearBufferSubData

你基本上没有提供偏移量,我猜为什么你没有得到任何编译错误,因为你缺少一个参数

代码示例:

GLfloat zeroFloat = 0.0f;
glClearBufferSubData(GL_SHADER_STORAGE_BUFFER,    //target
                     GL_R32F,                     //internal format
                     0,                    //you were missing this: offset
                     sizeof(GLfloat)*size, //size
                     GL_RED,               //format
                     GL_FLOAT,             //type
                     &zeroFloat);           //data

编辑回复以反映评论中的请求:

GPU可能在管道的不同级别使用缓存,因此在BufferObject中的更改不会立即从管道的其他阶段可见。内存屏障强制指定目标的一致性,以便在屏障之前的每个写操作将在屏障之后可见。如果在屏障之后有任何写操作,那么您就有麻烦了。

write
Memory Barrier
read

你提到了一个很长的计算,那么(假设没有驱动程序错误)SSBO的当前内容可能依赖于纹理。因此,纹理上的内存屏障确保SSBO的内容具有纹理数据。那么SSBO上的内存屏障似乎是不必要的,因为当SSBO被访问时,它已经有了正确的数据(在这种情况下,理论上你需要设置纹理和SSBO位:在更新SSBO之前设置纹理屏障,在使用SSBO之前设置SSBO屏障)。

如果你可以用小代码重现这个问题,那么它可能是一个驱动程序错误。记住在不同硬件的多台机器上测试代码,因为您仍然可能由于缺少内存屏障而得到意想不到的结果。

最新更新