opengl 如何在计算着色器中对SSBO使用原子操作

bttbmeg0  于 2023-06-22  发布在  其他
关注(0)|答案(1)|浏览(158)

示例代码

下面是一个基本的计算着色器来说明我的问题

layout(local_size_x = 64) in;

// Persistent LIFO structure with a count of elements
layout(std430, binding = 0) restrict buffer SMyBuffer
{
    int count;
    float data[];
} MyBuffer;

bool AddDataElement(uint i);
float ComputeDataElement(uint i);

void main()
{
    for (uint i = gl_GlobalInvocationID.x; i < some_end_condition; i += gl_WorkGroupSize.x)
    {
        if (AddDataElement(i))
        {
            // We want to store this data piece in the next available free space
            uint dataIndex = atomicAdd(MyBuffer.count, 1);
            // [1] memoryBarrierBuffer() ?
            MyBuffer.data[dataIndex] = ComputeDataElement(i);
        }
    }
}

说明

SMyBuffer是一个元素堆栈(data[]),其中count是当前元素数量。当满足特定条件时,计算着色器以原子方式递增计数。此操作返回用于索引data[]以存储新元素的前一个索引。这保证了没有两个着色器调用覆盖彼此的元素。
另一个计算着色器最终从该堆栈中弹出值并使用它们。当然,在两个计算着色器分派之间需要glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT)

提问

所有这些都很好,但我想知道我是否只是在时间上很幸运,我想验证我对API的使用。
那么,是否还需要其他东西来确保存储在SSBO中的计数器工作(参见1)?我希望atomicAdd()负责内存同步,因为否则它就没有什么意义了。如果一个原子操作的效果只在单个线程中可见,那么它的意义何在?
关于内存障碍,OpenGL wiki指出:
请注意,原子计数器在功能上不同于原子映像/缓冲区变量操作。后者仍然需要连贯的限定词、障碍等。
这让我想知道是否有一些我没有正确理解的东西,实际上需要一个memoryBarrierBuffer()。但是如果是这样的话,在其中一个线程到达后续的memoryBarrierBuffer()之前,如何阻止2个线程执行atomicAdd()
另外,是否会改变glDispatchCompute()调度单个工作组还是多个工作组?

up9lanfz

up9lanfz1#

您不需要memoryBarrierBuffer()调用,因为glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT)将暂停第二个着色器(消费者)发出的任何读取,直到第一个着色器的所有写入完成。
分派的工作组数量不会改变答案,因为来自glDispatchCompute()的所有写入都需要完成。

相关问题