代码之家  ›  专栏  ›  技术社区  ›  chafner

C++函数调用后SSBO的内容无效

  •  2
  • chafner  · 技术社区  · 10 年前

    我有一个叫SSBO的 sparseMatrix 以及以下操作顺序:

    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。

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

    • 如果我移动 函数2() 到年底 函数1() ,结果值很好。
    • 如果我移动 函数2() 直接进入 callerFunc() ,结果值很好。
    • 如果我再添加一个 glGetBufferSubData() 在结束时调用SSBO 函数1() ,中的值查询 函数2() 结果很好。
    • 如果我放置 glFinish() 之后 glClearBuffer 呼叫或结束时 函数1() ,中的值 函数2() 正确。如果我将 glFinish() 在开始 函数2() 不过,这并没有改变任何事情。
    • 放置 glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT) 任何地方都无济于事。

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

    编辑: 我把电话换成了 glClearBufferSubData(...) 使用一个计算机着色器,用一个常量值填充数据存储,现在行为如预期。但我仍然不知道是什么导致了这个问题。

    编辑2: 谢谢你的回答,但实际上我用对了。当我在这里发布代码时,我忘记输入偏移参数,对此很抱歉:(我在另一长串连续计算调度过程中再次遇到了问题。我尝试了很多方法,最终它帮助设置了GL_TEXTURE_FETCH_BARRIER_BIT内存屏障,而不是GL_SHADER_STORAGE_BARRIER BIT屏障,尽管计算着色器仅在SSBO上工作。我不知道为什么。

    1 回复  |  直到 10 年前
        1
  •  2
  •   CoffeDeveloper    10 年前

    错误在于您的使用方式

    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屏障)。

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