在多个gpu上同步原子计数器

时间:2015-01-19 12:13:17

标签: c++ opengl glsl compute-shader

我在计算着色器中使用原子计数器,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()。

在我取消映射GL_ATOMIC_COUNTER_BARRIER_BIT之前,我在particleCount尝试了一个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 );

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

同步的正确方法是什么,以便原子计数器可以与多个设备一起使用?

1 个答案:

答案 0 :(得分:2)

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

该障碍(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)