【问题标题】:Synchronise atomic counter across multiple gpu's跨多个 gpu 同步原子计数器
【发布时间】:2015-01-28 19:17:23
【问题描述】:

我在计算着色器中使用原子计数器,将atomic_uint 绑定到动态GL_ATOMIC_COUNTER_BUFFER(与此opengl-atomic-counter tutorial lighthouse3d 类似)。

我在粒子系统中使用原子计数器来检查所有粒子是否已达到条件;当所有粒子都在正确的位置时,我希望看到counter==numParticles

我在每一帧都映射缓冲区,并检查原子计数器是否计算了所有粒子:

GLuint *ptr = (GLuint *) glMapBuffer( GL_ATOMIC_COUNTER_BUFFER, GL_READ_ONLY );
GLuint particleCount = ptr[ 0 ];
glUnmapBuffer( GL_ATOMIC_COUNTER_BUFFER );
if( particleCount == numParticles() ){ // do stuff }

在单个 GPU 主机上,代码运行良好,particleCount 始终达到 numParticles(),但在多 GPU 主机上,particleCount 永远无法达到 numParticles()

我可以直观地检查是否已达到条件并且测试应该为真,但是particleCount 会上下改变每一帧,但从未达到 numParticles()。

在我取消映射particleCount之前,我已经在GL_ATOMIC_COUNTER_BARRIER_BIT上尝试了opengl内存屏障:

glMemoryBarrier(GL_ATOMIC_COUNTER_BARRIER_BIT);
GLuint *ptr = (GLuint *) glMapBuffer( GL_ATOMIC_COUNTER_BUFFER, GL_READ_ONLY );
GLuint particleCount = ptr[ 0 ];
glUnmapBuffer( GL_ATOMIC_COUNTER_BUFFER );
if( particleCount == m_particleSystem->numParticles() )
{ // do stuff }

在计算着色器中增加计数器之前,我尝试了一个 glsl 屏障:

memoryBarrierAtomicCounter();
atomicCounterIncrement( particleCount );

但原子计数器似乎没有跨设备同步。

让原子计数器与多个设备一起工作的正确同步方法是什么?

【问题讨论】:

    标签: c++ opengl glsl compute-shader


    【解决方案1】:

    在这种情况下,您选择的内存屏障实际上是​​不合适的。

    该屏障 (GL_ATOMIC_COUNTER_BARRIER_BIT) 会更改原子计数器可见例如刷新缓存并以特定顺序运行着色器),但它没有要做的是确保在映射、读取和取消映射缓冲区之前所有并发着色器都已完成。

    由于您的缓冲区正在被映射和读回,因此您不需要该屏障 - 该屏障用于着色器通道之间的一致性。您真正需要的是确保在您尝试使用 GL 命令读取数据之前完成所有访问原子计数器的着色器,为此您需要 GL_BUFFER_UPDATE_BARRIER_BIT

    • GL_BUFFER_UPDATE_BARRIER_BIT:

    通过glBuffer(Sub)DataglCopyBufferSubDataglProgramBufferParametersNVglGetBufferSubData 进行读/写,或在屏障之后缓冲由glMapBuffer(Range) 映射的对象内存将反映在屏障之前由着色器写入的数据。

    此外,通过在屏障之后发出的这些命令进行的写入将等待任何着色器对屏障之前启动的同一内存的写入完成。


    您可能从错误的角度考虑障碍。您需要的屏障取决于内存读取需要与哪种类型的操作保持一致。

    我建议重温一下不连贯的内存访问usecases

    (1)   渲染命令之间的着色器写入/读取

    一个Rendering Command 不连贯地写入,另一个读取。这里根本不需要coherent(GLSL 限定符)​。只需在发出读取渲染命令之前使用glMemoryBarrier​,使用适当的访问位。

    (2)   Shader 写入,其他 OpenGL 操作读取

    同样,coherent​ 不是必需的。在执行读取之前,您必须使用glMemoryBarrier​,使用适合感兴趣的读取操作的位域。

    如果(1),您想要的屏障实际上是​​GL_ATOMIC_COUNTER_BARRIER_BIT,因为它会在共享相同原子计数器的不同着色器通道之间强制执行严格的内存和执行顺序规则。

    如果(2),你想要的障碍是GL_BUFFER_UPDATE_BARRIER_BIT“感兴趣的阅读操作”glMapBuffer (...),如上所示,在GL_BUFFER_UPDATE_BARRIER_BIT下。

    在您的情况下,您正在使用 GL API 读回缓冲区。您需要 GL 命令来等待所有待处理的着色器完成写入(对于不连贯的内存访问 - 图像加载/存储、原子计数器等,这不会自动发生)。 那是教科书案例(2)

    【讨论】:

    • 我已将障碍切换到GL_BUFFER_UPDATE_BARRIER_BIT,但我仍然看到与原始帖子相同的问题。
    • @schwyzl:是的,我认为这不能解决您的问题,但我永远无法在评论中解释所有这些。您是否使用单个计算着色器调度来执行此操作?如果每帧有多个调度,那么您可能还需要在它们之间设置屏障。
    • 是的,它是跨 vec3(x,1,1) 组的每帧单个调度计算。我glDispatchCompute,然后glMemoryBarrierGL_BUFFER_UPDATE_BARRIER_BIT,然后映射原子计数器缓冲区,使用和取消映射,然后glMemoryBarrierGL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT并绘制粒子。
    • glMemoryBarrier(GL_ALL_BARRIER_BITS) 是同样的问题。
    猜你喜欢
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多