Glsl 为什么当memoryBarrier不';T

Glsl 为什么当memoryBarrier不';T,glsl,Glsl,以下GLSL计算着色器仅将inImage复制到outImage。它源自更复杂的后处理过程 在main()的前几行中,单个线程将64个像素的数据加载到共享数组中。然后,在同步之后,64个线程中的每个线程将一个像素写入输出图像 根据我的同步方式,我会得到不同的结果。我最初认为memoryBarrierShared()是正确的调用,但它会产生以下结果: 这与不同步或改用memoryBarrier()的结果相同 如果我使用barrier(),我会得到以下(期望的)结果: 条带宽度为32像素,如果我将

以下GLSL计算着色器仅将
inImage
复制到
outImage
。它源自更复杂的后处理过程

main()
的前几行中,单个线程将64个像素的数据加载到共享数组中。然后,在同步之后,64个线程中的每个线程将一个像素写入输出图像

根据我的同步方式,我会得到不同的结果。我最初认为
memoryBarrierShared()
是正确的调用,但它会产生以下结果:

这与不同步或改用
memoryBarrier()
的结果相同

如果我使用
barrier()
,我会得到以下(期望的)结果:

条带宽度为32像素,如果我将工作组大小更改为小于或等于32,则会得到正确的结果

这是怎么回事?我是否误解了
memoryBarrierShared()
的目的?为什么
barrier()

#version 430

#define SIZE 64

layout (local_size_x = SIZE, local_size_y = 1, local_size_z = 1) in;

layout(rgba32f) uniform readonly  image2D inImage;
uniform writeonly image2D outImage;

shared vec4 shared_data[SIZE];

void main() {
    ivec2 base = ivec2(gl_WorkGroupID.xy * gl_WorkGroupSize.xy);
    ivec2 my_index = base + ivec2(gl_LocalInvocationID.x,0);

    if (gl_LocalInvocationID.x == 0) {
        for (int i = 0; i < SIZE; i++) {
            shared_data[i] = imageLoad(inImage, base + ivec2(i,0));
        }
    }

    // with no synchronization:   stripes
    // memoryBarrier();        // stripes
    // memoryBarrierShared();  // stripes
    // barrier();              // works

    imageStore(outImage, my_index, shared_data[gl_LocalInvocationID.x]);
}
#版本430
#定义大小64
布局(本地大小x=大小,本地大小y=1,本地大小z=1)在中;
布局(rgba32f)统一只读图像2D inImage;
均匀写入图像2D输出图像;
共享vec4共享_数据[大小];
void main(){
ivec2 base=ivec2(gl_WorkGroupID.xy*gl_WorkGroupSize.xy);
ivec2 my_index=base+ivec2(gl_LocalInvocationID.x,0);
如果(gl_LocalInvocationID.x==0){
对于(int i=0;i
图像加载存储和好友的问题是,实现无法再确保着色器仅更改其专用输出值的数据(例如,片段着色器后的帧缓冲区)。计算着色器更是如此,它没有专用的输出,只通过将数据写入可写存储(如图像、存储缓冲区或原子计数器)来输出。这可能需要在各个过程之间手动同步,否则尝试访问纹理的片段着色器可能不会通过前一个过程(如计算着色器)的图像存储操作将最新数据写入该纹理

因此,您的计算着色器可能工作得很好,但与以下显示(或任何需要以某种方式读取此图像数据的)过程的同步失败。为此,存在该功能。根据在显示过程中读取该图像数据的方式(或者更准确地说,在计算着色器过程之后读取图像的过程),需要为该函数指定不同的标志。如果使用纹理读取,请使用
GL\u纹理\u获取\u屏障\u位​,如果再次使用图像加载,请使用
GL\u SHADER\u image\u ACCESS\u BARRIER\u位​,如果使用
glBlitFramebuffer
显示,请使用
GL\u FRAMEBUFFER\u BARRIER\u位​

虽然我在图像加载/存储和手动内存同步方面没有太多经验,但这只是我理论上的想法。因此,如果有人知道得更好,或者您已经使用了合适的
glMemoryBarrier
,请随时纠正我的错误。同样,这不一定是您唯一的错误(如果有)。但是链接的Wiki文章的最后两点实际上是针对您的用例的,我清楚地表明您需要某种
glMemoryBarrier

  • 在一次渲染过程中写入图像变量并在以后的过程中由着色器读取的数据不需要使用
    相干
    变量或
    
    memoryBarrier()
    。使用
    SHADER\u IMAGE\u ACCESS\u BARRIER\u位​设置障碍物​ 通行证之间是
    必要的

  • 着色器在一个渲染过程中写入的数据,以及在以后的过程中由另一种机制(例如,顶点或索引缓冲区拉动)读取的数据 不要使用连贯的变量或记忆载体()。使命感
    glMemoryBarrier
    中设置了适当的位​ 之间 通行证是必要的


编辑:实际上

共享变量访问使用非一致内存访问规则。 这意味着用户必须按顺序执行某些同步 以确保共享变量可见

共享变量都隐式声明为一致的
,所以您不需要
需要(并且不能使用)该限定符。但是,您仍然需要
提供适当的内存屏障

通常的内存屏障集可用于计算着色器,但 他们还可以访问
memoryBarrierShared()​;此屏障已关闭
专门用于共享变量排序<代码>groupMemoryBarrier()
​ 行为类似于
memoryBarrier()​,为所有类型的
变量,但它仅为当前工作组命令读/写操作

虽然工作组中的所有调用都被称为“并行”执行,但这并不意味着您可以假设所有调用都是并行的 在锁定步骤中执行。如果需要确保调用 写入某个变量以便您可以读取它,您需要 将执行与调用同步,而不仅仅是发出内存 屏障(您仍然需要内存屏障)。

要在工作组内的调用之间同步读写,必须使用
barrier()​ 功能。这迫使一个
工作组中所有调用之间的显式同步。
工作范围内的执行