GLSL 中断命令

发布于 2024-10-16 09:13:45 字数 555 浏览 8 评论 0原文

目前,我正在学习如何在 GLSL 中为我正在开发的游戏引擎创建着色器,并且我有一个关于该语言的问题令我困惑。我了解到,在低于 3.0 的着色器版本中,不能在循环条件下使用统一变量。例如,以下代码在早于 3.0 的着色器版本中不起作用。

for (int i = 0; i < uNumLights; i++)
{
   ...............
}

但是,是否可以将其替换为具有固定迭代次数的循环,但包含一个条件语句,如果在本例中 i 大于 uNumLights,该条件语句将中断循环?例如:

for (int i = 0; i < MAX_LIGHTS; i++)
{
    if(i >= uNumLights)
        break;
    ..............
}

这些不是等价的吗?后者应该在旧版本的 GLSL 中工作吗?如果是这样,这不是比我读过的其他技术(例如针对不同数量的灯光使用不同版本的着色器)更高效且更容易实现吗?
我知道这可能是一个愚蠢的问题,但我是初学者,我找不到这不起作用的原因。

Currently I am learning how to create shaders in GLSL for a game engine I am working on, and I have a question regarding the language which puzzles me. I have learned that in shader versions lower than 3.0 you cannot use uniform variables in the condition of a loop. For example the following code would not work in shader versions older than 3.0.

for (int i = 0; i < uNumLights; i++)
{
   ...............
}

But isn't it possible to replace this with a loop with a fixed amount of iterations, but containing a conditional statement which would break the loop if i, in this case, is greater than uNumLights?. Ex :

for (int i = 0; i < MAX_LIGHTS; i++)
{
    if(i >= uNumLights)
        break;
    ..............
}

Aren't these equivalent? Should the latter work in older versions GLSL? And if so, isn't this more efficient and easy to implement than other techniques that I have read about, like using a different version of the shader for different number of lights?
I know this might be a silly question, but I am a beginner and I cannot find a reason why this shouldn't work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

獨角戲 2024-10-23 09:13:45

GLSL 可能会令人困惑,因为 for() 向您建议必须存在条件分支,即使没有条件分支,因为硬件根本无法执行此操作(这适用于 if () 以同样的方式)。

在 SM3 之前的硬件上真正发生的是 OpenGL 实现中的 HAL 将完全展开循环,因此实际上不再有跳转。而且,这解释了为什么它很难处理非常数。

虽然从技术上来说无论如何都可以使用非常量来实现,但每次更改该制服时,实现都必须重新编译着色器,并且如果只允许您提供任何随意的数字,则可能会违反最大指令数。

这是一个问题,因为……然后呢?这是一个糟糕的情况。

如果您提供太大的常量,则在构建着色器时会出现“指令过多”编译器错误。现在,如果您在统一中提供一个愚蠢的数字,并且 HAL 必须生成新代码并针对此限制运行,OpenGL 能做什么?
您很可能在编译和链接后验证了您的程序,并且您很可能查询了着色器信息日志,并且 OpenGL 不断告诉您一切都很好。从某种程度上来说,这是一个具有约束力的承诺,它不能突然做出其他决定。因此,必须确保这种情况不会出现,唯一可行的解​​决方案是在不支持动态分支的硬件世代上不允许出现统一条件。
否则,glUniform 内部需要某种形式的验证来拒绝错误值。然而,由于这取决于成功(或不成功)的着色器重新编译,这意味着它必须同步运行,这使得它成为一种“不行”的方法。另外,考虑到 GL_ARB_uniform_buffer_object 在某些 SM2 硬件(例如 GeForce FX)上公开,这意味着您可以在 OpenGL 中抛出一个内容不可预测的缓冲区对象,并且仍然期望它以某种方式工作!在取消映射后,实现必须扫描缓冲区的内存以查找无效值,这是疯狂的。

与循环类似,if() 语句在 SM2 硬件上不会分支,即使它看起来像这样。相反,它将计算两个分支并执行条件移动。

GLSL can be confusing insofar as for() suggests to you that there must be conditional branching, even when there isn't because the hardware is unable to do it at all (which applies to if() in the same way).

What really happens on pre-SM3 hardware is that the HAL inside your OpenGL implementation will completely unroll your loop, so there is actually no jump any more. And, this explains why it has difficulties doing so with non-constants.

While technically possible to do it with non-constants anyway, the implementation would have to recompile the shader every time you change that uniform, and it might run against the maximum instruction count if you're just allowed to supply any haphazard number.

That is a problem because... what then? That's a bad situation.

If you supply a too big constant, it will give you a "too many instructions" compiler error when you build the shader. Now, if you supply a silly number in an uniform, and the HAL thus has to produce new code and runs against this limit, what can OpenGL do?
You most probably validated your program after compiling and linking, and you most probably queried the shader info log, and OpenGL kept telling you that everything was fine. This is, in some way, a binding promise, it cannot just decide otherwise all of a sudden. Therefore, it must make sure that this situation cannot arise, and the only workable solution is to not allow uniforms in conditions on hardware generations that don't support dynamic branching.
Otherwise, there would need to be some form of validation inside glUniform that rejects bad values. However, since this depends on successful (or unsuccessful) shader recompilation, this would mean that it would have to run synchronously, which makes it a "no go" approach. Also, consider that GL_ARB_uniform_buffer_object is exposed on some SM2 hardware (for example GeForce FX), which means you could throw a buffer object with unpredictable content at OpenGL and still expect it to work somehow! The implementation would have to scan the buffer's memory for invalid values after you unmap it, which is insane.

Similar to a loop, an if() statement does not branch on SM2 hardware, even though it looks like it. Instead, it will calculate both branches and do a conditional move.

瀞厅☆埖开 2024-10-23 09:13:45

(我假设您正在谈论像素着色器)。
第二个变体仅适用于支持着色器模型 >= 3 的 GPU。因为 GPU 着色器模型 uNumLights 不支持动态分支(例如将变量 uNumLights 放入 IF 条件)。 3 也可以。

此处您可以比较不同着色器模型之间支持和不支持的内容。

(I'm assuming you are talking about pixel shaders).
Second variant is going to work only on gpu which supports shader model >= 3. Because dynamic branching (such as putting variable uNumLights into IF condition) is not supported on gpu shader model < 3 either.

Here you can compare what is and isn't supported between different shader models.

擦肩而过的背影 2024-10-23 09:13:45

我刚刚想出了一个有趣的工作。看起来很愚蠢,我不能向你保证这是一个健康的选择,但它现在似乎对我有用:

将你的 for 循环设置为你允许的最大值。如果计数超出您的统一值,则在循环内放置一个条件以跳过繁重的例程。

uniform int iterations;

for(int i=0; i<10; i++){
     if(i<iterations){
          //do your thing...
     }
}

There is a fun work around I just figured out. Seems stupid and I can't promise you that it's a healthy choice, but it appears to work for me right now:

Set your for loop to the maximum you allow. Put a condition inside the loop to skip over the heavy routines, if the count goes beyond your uniform value.

uniform int iterations;

for(int i=0; i<10; i++){
     if(i<iterations){
          //do your thing...
     }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文