11

我以前已经遇到了我想通过执行以下操作在图像单元中混合颜色值的问题:

vec4 texelCol = imageLoad(myImage, myTexel);
imageStore(myImage, myTexel, texelCol+newCol);

在多个片段可以为“myTexel”具有相同值的情况下,这显然是不可能的,因为无法在 imageLoad 和 imageStore 命令之间创建原子性,并且其他着色调用可能会更改其间的纹素颜色。

现在有人告诉我,人们正在通过使用 uint 纹理上的原子命令创建信号量来解决这个问题,这样着色器会在一段时间循环中以某种方式等待,然后再访问 texel,并且一旦它空闲,就以原子方式将其写入整数纹理来阻止其他片段着色器调用,处理颜色纹素,并在完成后再次原子地释放整数纹素。

但是我无法思考这如何真正起作用以及这样的代码会是什么样子?

真的有可能做到这一点吗?可以将 GLSL 片段着色器设置为在 while 循环中等待吗?如果可能的话,有人可以举个例子吗?

4

1 回答 1

10

基本上,您只是在实现一个spinlock。只是不是一个锁变量,而是整个纹理的锁。

从逻辑上讲,你在做什么是有道理的。但就 OpenGL 而言,这实际上是行不通的。

看,OpenGL 着色器执行模型声明调用以相对于彼此在很大程度上未定义的顺序执行。但是自旋锁只有在保证各个线程之间的向前进展的情况下才起作用。基本上,自旋锁要求正在旋转的线程不能使执行系统无法执行它正在等待的线程。

OpenGL不提供这样的保证。这意味着一个线程完全有可能锁定一个像素,然后停止执行(无论出于何种原因),而另一个线程出现并阻塞该像素。被阻塞的线程永远不会停止执行,拥有锁的线程永远不会重新开始执行。

这在真实系统中如何发生?好吧,假设您有一个片段着色器调用组在三角形的一些片段上执行。它们都锁定了像素。但是由于锁定区域内的条件分支,它们在执行中出现了分歧。执行的分歧可能意味着其中一些调用被转移到不同的执行单元。如果目前没有可用的,那么它们会有效地暂停,直到有一个可用。

现在,假设出现了一些其他片段着色器调用组,并在发散组之前分配了一个执行单元。如果该组试图对来自发散组的像素进行自旋锁定,那么它实质上是在使发散组的执行时间处于饥饿状态,等待永远不会发生的偶数。

现在显然,在真正的 GPU 中存在不止一个执行单元,但您可以想象,由于存在大量调用组,这种情况完全有可能偶尔阻塞工作。

于 2012-05-18T13:58:08.570 回答