我之前已经遇到过这样的问题:我希望通过执行以下操作来混合图像单元中的颜色值:
vec4 texelCol = imageLoad(myImage, myTexel);
imageStore(myImage, myTexel, texelCol+newCol);
在多个片段对'myTexel'具有相同值的情况下,这显然是不可能的,因为无法在imageLoad和imageStore命令之间创建原子性,而其他shaderinvocations可能会更改其间的纹素颜色。 / p>
现在有人告诉我,人们通过使用uint纹理上的原子命令创建信号量来解决这个问题,这样着色器会在访问texel之前以某种方式在while循环中等待,并且只要它是空闲的,就会自动写入itno整数纹理来阻止其他片段着色器调用,处理颜色纹理元素,并在完成后再次以原子方式释放整数纹素。
但我无法理解这是如何真正起作用的,以及这些代码会是什么样子?
真的可以这样做吗?可以将GLSL片段着色器设置为在while循环中等待吗?如果有可能,有人可以举个例子吗?
答案 0 :(得分:8)
是的,您可以这样做,但只有在混合了事物的订单时才会这样做。否则你需要使用通常的技术。
基本上你只是在实施spinlock。只有一个锁定变量,你才能拥有整个纹理的锁定。
首先需要一张与原子操作相同的图像。它必须是GL_R32UI
的整数纹理,并且uimage
格式为r32ui
才能匹配。它应该用0来初始化。原子图像和“混合”图像都必须声明为“连贯”。
在原子整数图像上的位置执行imageAtomicCompSwap
操作。您将其与0进行比较,并且您将其设置为1的值。
如果#1返回1,则表示其他人拥有该锁。回到步骤#1。
如果#1返回0,那么您现在可以独占访问texel(因为texel现在有1个,这要归功于比较/交换操作)。继续。
执行混合操作。在混合操作后发出memoryBarrier
。
执行imageAtomicExchange
,其值为0.这会解锁螺旋锁。
这种作用的原因是由于原子。 GLSL确保原子是原子的。 imageAtomicCompareSwap
执行读/条件修改/写操作作为原子序列。并且因为它是原子的,所以不可能其他着色器调用以抢占或中断操作。这意味着着色器运行的线程数无关紧要:如果其中100个调用imageAtomicCompareSwap(..., 0, 1)
,其中只有一个将获得1作为返回值,其余的将获得0(对于相同的texel,当然)。
因此只有一个线程会获得锁定;其他人必须等待。
memoryBarrier()
函数的使用将确保等待的其他线程在读取时将获取修改后的数据。同样,您需要使用coherent
限定符来处理您正在执行此操作的图像。