GLSL memoryBarrier()

时间:2017-08-10 03:48:34

标签: opengl glsl gpu memory-barriers

OpenGL红皮书版本8(GL 4.3)示例11.19将imageLoad()置于while循环中,继续轮询,直到前一个原语的至少一个片段更新了此值。这本书说

  

示例11.19显示了一个非常简单的内存屏障用例。它允许   要确保片段之间的某种程度的排序。在顶部   functionUsingBarriers(),一个简单的循环用于等待内容   一个内存位置,以达到我们当前的原始ID。 因为我们知道   没有来自同一原语的两个片段可以落在同一个原语上   像素,我们知道当我们在体内执行代码时   函数,来自前一个原语的至少一个片段   处理即可。然后我们开始修改内存的内容   片段的位置使用非原子操作。我们向其他着色器发出信号   我们通过写入共享内存位置来完成的调用   最初在函数顶部进行了轮询。

     

确保将修改后的图像内容写回内存   在其他着色器调用开始进入函数体之前,我们使用   在更新彩色图像和更新之间调用memoryBarrier   原始计数器以强制执行排序。

然而,GL规范4.3说

  

有一个由另一个调用写入的调用轮询内存假定另一个调用已经启动并且可以完成其写入

那么我们如何确保先前原语的片段调用已经启动并完成其写入?

发布src代码

#version 420 core

layout (rgba32f} uniform coherent image2D my_image;

// Declaration of function
void functionUsingBarriers(coherent uimageBuffer i)

{

    uint val;

    // This loop essentially waits until at least one fragment from
    // an earlier primitive (that is, one with gl_PrimitiveID - 1)
    // has reached the end of this function point. Note that this is
    // not a robust loop as not every primitive will generate
    // fragments.
    do
    {
        val = imageLoad(i, 0).x;
    } while (val != gl_PrimitiveID);

    // At this point, we can load data from another global image
    vec4 frag = imageLoad(my_image, gl_FragCoord.xy);

    // Operate on it...
    frag *= 0.1234;
    frag = pow(frag, 2.2);

    // Write it back to memory
    imageStore(my_image, gl_FragCoord.xy, frag);

    // Now, we’re about to signal that we’re done with processing
    // the pixel. We need to ensure that all stores thus far have
    // been posted to memory. So, we insert a memory barrier.
    memoryBarrier();

    // Now we write back into the original "primitive count" memory
    // to signal that we have reached this point. The stores
    // resulting from processing "my_image" will have reached memory
    // before this store is committed due to the barrier.
    imageStore(i, 0, gl_PrimitiveID + 1);

    // Now issue another barrier to ensure that the results of the
    // image store are committed to memory before this shader
    // invocation ends.
    memoryBarrier();
}

1 个答案:

答案 0 :(得分:1)

此代码(及其附带的文本)是错误的废话。请考虑以下声明:

  

因为我们知道同一个图元中没有两个片段可以落在同一个像素上,所以我们知道当我们在函数体中执行代码时,至少有一个来自前一个图元的片段已被处理。

即使我们假设网格中的基元不重叠(一般来说也不是一个合理的假设),这意味着关于GPU在基元之间的工作分布精确地 。 / p>

OpenGL规范明确了这一点:

  

相同着色器类型的调用的相对顺序是未定义的。一个   在处理原始B时由着色器发出的存储可能先于之前完成   到原始A的存储,即使在原语之前指定了原始A.   B.这甚至适用于片段着色器;片段着色器输出   总是以原始顺序写入帧缓冲区,由执行的存储区写入   片段着色器调用不是。

     

...

     

着色器调用顺序的上述限制也使某些形式   单个基元集内的着色器调用之间的同步是不可实现的。例如,让另一个调用写入一个调用轮询内存假定另一个调用已经启动并且可以完成   它的写作。做出这种保证的唯一情况是当输入时   从一个着色器调用的输出生成一个着色器调用   前一阶段。

是的,OpenGL规范专门将此称为无法执行的操作。我不知道如何进入正式的OpenGL书,但你的直觉是正确的:它是完全错误的。这就是为什么ARB_fragment_shader_interlock存在的原因:因为否则,你就不能做这样的事情。