【问题标题】:OpenGL Compute Shader - correct memory barrier usageOpenGL Compute Shader - 正确的内存屏障使用
【发布时间】:2016-08-12 09:24:15
【问题描述】:

我希望能够使用计算着色器读取和写入 SSBO 的潜在相同元素,作为流体模拟的一部分,但我在同步时遇到了问题。我有一个运行 16 次的测试着色器,下面有三个选项,希望能显示我正在尝试做的事情。

layout  (std430, binding=8) coherent buffer Debug
{
  int debug[ ];
};

shared int sharedInt;

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

void main()
{
    ///////     1.     ///////
    sharedInt = debug[0];
    memoryBarrierShared();
    barrier();
    debug[0] = sharedInt[0] + 1;
    memoryBarrierShared();
    barrier();

    // Print debug[0]: 1


    ///////     2.     ///////
    atomicAdd(debug[0], 1);

    // Print debug[0]: 16


    ///////     3.     ///////
    sharedInt = debug[0];
    memoryBarrierShared();
    barrier();
    atomicExchange(debug[0], debug[0]+1);
    memoryBarrierShared();
    barrier();

    // Print debug[0]: 1
}

*为了清楚起见,我一次只运行一个选项。

我试图为所有这些获得的结果是调试 [0] 等于 16,尽管我需要在我的模拟中使用类似于第一个或第三个选项的东西,因为我需要读取和写入SSBO 在同一个线程中。

我不确定我是否理解共享变量的作用,并且据我所知 memoryBarrierShared() 应该使工作组中的每个线程都可以看到 sharedInt 的读写,但如果我这样做的话只派出一个工作组,结果是一样的。

感谢您的帮助。

【问题讨论】:

  • 不清楚您要做什么,为什么会有共享变量,或者您的障碍打算完成什么。您说您希望 debug[0] 为 16,但目前还不清楚为什么 #2 不是该问题的可接受解决方案。
  • 很抱歉。我无法使用#2,因为我必须从 debug[0] 读取,然后再添加 1。在模拟中,我有一个 ssbo,它表示一个 3D 网格或存储当前每个单元格的粒子的单元格。对于每个单元格,我存储粒子数和单元格中粒子的索引。填充网格时,为每个粒子执行计算着色器,找到它所在的单元格,然后根据单元格中的粒子数,将其添加到适当的内存插槽,因此我必须读取粒子数,然后增加它。

标签: opengl gpu shared-memory memory-barriers compute-shader


【解决方案1】:

要点是变体 1 和 3 中的那些添加不是原子操作的一部分。首先从共享变量/ssbo 中读取,然后执行加法,然后写入。如果所有调用读取相同的值,则它们都具有相同的加法结果并写入相同的值。

要使加法成为原子操作的一部分,您可以使用 atomicAdd,就像您在变体 2 中所做的那样。

这是带有一些解释的注释代码:

///////     1.     ///////

// all invocations read debug[0] into the shared variable (presumably 0)
sharedInt = debug[0];

// syncing. but since all wrote the same value into the SSBO, they all would read the same value from it,
// since values written in one invocation are always visible in the same invocation.
memoryBarrierShared();
barrier();

// all invocations do the addition and add 1 to that shared variable (but not write to the shared variable)
// then they all write the result of the addition (1) to the SSBO
debug[0] = sharedInt[0] + 1;

// another syncing that does not help if the shader ends here.
memoryBarrierShared();
barrier();

// since they all write 1, there is no other output possible than a 1 in the SSBO.
// Print debug[0]: 1


///////     2.     ///////
// all invocations tell the "atomic memory unit" (whatever that is exactly)
// to atomicly add 1 to the SSBO.
// that unit will now, sixteen times, read the value that is in the SSBO,
// add 1, and write it back. and because it is does so atomicly,
// these additions "just work" and don't use old values or the like,
// so you have a 16 in your SSBO.
atomicAdd(debug[0], 1);

// Print debug[0]: 16


///////     3.     ///////

// as above, but this has even less effect since you don't read from sharedInt :)
sharedInt = debug[0];
memoryBarrierShared();
barrier();

// all invocations read from debug[0], reading 0.
they all add 1 to the read value, so they now have 1 in their registers.
// now they tell the "atomic memory unit" to exchange whatever there is in
// debug[0] with a 1. so you write a 1 sixteen times into debug[0] and end up with a 1.
atomicExchange(debug[0], debug[0]+1);
memoryBarrierShared();
barrier();

// Print debug[0]: 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 2010-12-07
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多