GLSL while 循环性能独立于其内部完成的工作

GLSL while loop performance is independent from work done inside of it

我目前正在尝试在利用非常简单的 BVH 的片段着色器中实现路径跟踪器

BVH 路口的代码基于以下思想:

bool BVHintersects( Ray ray ) {

    Object closestObject;

    vec2 toVisit[100]; // using a stack to keep track which node should be tested against the current ray
    int stackPointer = 1;
    toVisit[0] = vec2(0.0, 0.0);  // coordinates of the root node in the BVH hierarcy    



    while(stackPointer > 0) {
        stackPointer--;  // pop the BVH node to examine

        if(!leaf) {
            // examine the BVH node and eventually update the stackPointer and toVisit
        }

        if(leaf) {
            // examine the leaf and eventually update the closestObject entry
        }
    }
}

上面代码的问题是,在第二次光反射时,一些非常奇怪的事情开始发生,假设我是这样计算光反射的:

vec3 color  = vec3(0.0);
vec3 normal = vec3(0.0);
// first light bounce
bool intersects = BVHintersect(ro, rd, color, normal);

vec3 lightPos = vec3(5, 15, 0);

// updating ray origin & direction
ro = ro + rd * (t - 0.01);
rd = normalize(lightPos - ro);

// second light bounce used only to calculate shadows
bool shadowIntersects = BVHintersect(ro, rd, color, normal);

对 BVHintersect 的第二次调用将 运行 无限期地进行,因为 while 循环永远不会退出,但是根据我对第二次调用所做的许多测试,我确信 stackPointer 最终会成功返回到 0 ,实际上,如果我将以下代码放在 while 循环下面:

int iterationsMade = 0;
while(stackPointer > 0) {
    iterationsMade++;
    if(iterationsMade > 100) {
        break;
    }
    // the rest of the loop
    // after the functions ends it also returns "iterationsMade"

变量 "iterationsMade" 始终低于 100,while 循环不会无限地 运行,但性能方面就好像我进行了“100”次迭代,即使 "iterationsMade"永远不会大于 10 或 20。将硬编码的“100”增加到更大的值会线性降低性能

导致此行为的可能原因是什么?如果第二次调用 BVHIntersect 的次数从不超过 10-20 次,那么第二次调用 BVHIntersect 时卡在 while 循环中的可能原因是什么?

BVHintersect 函数的来源: https://pastebin.com/60SYRQAZ

所以,关于着色器(或大多数 SIMD 环境)中的循环有一件有趣的事情:

整个 wave 的执行时间至少与最慢的线程一样长。所以,如果一个线程需要进行大约 100 次迭代,那么它们都需要 100 次迭代。根据您的平台和编译器,循环可能会展开 100 次迭代(或您选择的任何上限)。 break 之后的任何内容都不会影响最终输出,但仍需要处理展开循环的其余部分。 Early-out 并不总是可能的。

有很多方法可以解决这个问题,但也许最直接的方法是在多次传递中使用较低的最大迭代值来执行此操作。

我还会 运行 你的着色器通过编译器查看生成的代码。比较具有不同最大迭代次数的不同版本,并查看编译着色器的长度等内容。

See this answer for a little more information.