我正在编写一个着色器,它在不同的输入上多次使用相同的算法来计算一些中间结果,这些中间结果随后被合并以获得最终结果。数据依赖于游戏中的世界位置,对于一个组中的调用来说,它足够接近。因此我可以同时处理不同调用上的所有输入,然后在调用之间移动中间结果以计算最终结果。棘手的是,一些中间结果允许提前退出计算,这意味着一些调用依赖于其他调用的计算,而一些可能不依赖(因此,当结果在调用之间移动时,它们是不活动的)。
OpenGL有一个ARB shader baloot扩展,它允许从其他调用中阅读值。具体来说,就是 readInvocationARB(genUType value,uint invocationIndex) 函数。它的描述如下:
函数readInvocationARB()将从给定的返回到子组中所有活动调用。对于子组中所有活动调用,必须相同,否则结果未定义。
并且没有明确说明这取决于read-from调用是否处于活动状态。我决定测试如果调用处于非活动状态会有什么结果。
# version 460
# extension GL_ARB_shader_ballot : enable
uniform uint data;
layout(location = 0) out vec4 color;
void main() {
uint var;
var = data;
if(gl_SubGroupInvocationARB != 20) {
const uint result = readInvocationARB(var, 20);
color = vec4(result == data);
}
else color = vec4(1, 0, 0, 1);
}
结果是带有红点的白色图像,这意味着在本例中,从非活动的第20个调用是之前在变量中写入的。
然后我决定在 var 的值中添加condition:
# version 460
# extension GL_ARB_shader_ballot : enable
uniform uint threshold;
uniform uint data;
layout(location = 0) out vec4 color;
void main() {
uint var;
if(gl_SubGroupInvocationARB < threshold)
var = data;
else var = 10;
if(gl_SubGroupInvocationARB != 20) {
const uint result = readInvocationARB(var, 20);
color = vec4(result == data);
}
else color = vec4(1, 0, 0, 1);
return;
}
- data* uniform被设置为10(因此该值实际上总是相同的),而 threshold 变量的值无关紧要(无论结果如何总是相同的)。在第二种情况下,图像是黑色的,带有红点,这意味着不是先前写入变量的内容(实际上是0)。
我决定在另一台电脑上测试这两个着色器,这台电脑有AMD显卡(第一台有Nvidia)。在这两种情况下,输出的图像都是白色和红色的点,这与第一台电脑显示的不同。这不是一个严格的测试,但它表明不同的硬件和软件配置产生不同的结果相同的着色器程序。
所以问题是:
- 我是否遗漏了一些内容,并且未定义用于非活动调用的readInvocationARB()的结果,在这种情况下,我可以在哪里找到此扩展的完整描述?或者,
- 定义了结果,但我做了一些其他错误的事情,或者产生的输出之一不正确。
1条答案
按热度按时间wswtfjt71#
正如规范中引用的部分所述,
invocationIndex
必须是子组中的活动调用,因此,如果它 * 不是 * 子组中的活动调用,则您传递了函数错误数据,从而实现了未定义的行为。由于分支的原因,不知道哪些调用属于同一子组,也就是说,您没有确保20是同一子组中的活动调用索引,也没有检查
ballotARB(true) & (0x1 << 20)
是否为真。由于分支发散依赖于实现,并且你没有做任何事情来验证20是否是一个有效的活动调用索引,所以你的代码是否具有定义良好的行为取决于依赖于实现的东西,因此,它可能因实现而异。