GLSL-在主要功能范围之外声明全局变量


12

在GLSL中声明超出主函数范围的变量是否有帮助?这些变量实际上是否可以重用并且效率更高?

这是有问题的代码:

varying vec2 vposition;
uniform float seed;
uniform float top;
uniform float bottom;
uniform float phi;
uniform float theta;
uniform float scaledPI;
uniform float yn;
uniform float ym;
uniform float rx;
uniform float ry;
uniform float radius;

const float PI = 3.141592653589793238462643383;

float left;
float right;
float mscaled;
float xn;
float xm;
void main() {
    float t = vposition.y * yn + ym;

    if(t <= 0.0 || t >= PI){
        left = phi - PI;
        right = phi + PI;
    }else{
        mscaled = scaledPI / (1 - abs(Math.cos(theta)));
        mscaled = mscaled < PI ? mscaled : PI;
        left = phi - mscaled;
        right = phi + mscaled;
    }
    xn = (left - right) / ((-rx / 2.0) - (rx / 2.0));
    xm = left - ((-rx/2.0) * xn);
    float p = vposition.x * xn + xm;

    vec3 coords = vec3( sin(p) * sin(t), cos(t), cos(p) * sin(t) );
    float nv = surface( vec4( coords, seed ) );
    gl_FragColor = vec4( vec3( nv, nv, nv ), 1.0 );
}

3
这个问题令人困惑。GLSL没有主循环。您是说main()功能吗?您的值实际上是全局变量(以GLSL的说法是均匀值还是属性)还是常量值?
肖恩·米德迪奇

您可以张贴一些代码作为您所谈论内容的示例吗?
内森·里德

我用代码更新了问题。
RodgerDodger

@ user1286792:这不会改变GLSL 没有主循环的事实。目前尚不清楚您在说什么。您到底在想什么,这样做会节省下来?
Nicol Bolas 2013年

@NicolBolas我已经更新了问题,以使其更加清晰。希望现在对将来的某人有用。
RodgerDodger 2013年

Answers:


33

我想我明白您要问的问题。我认为您最关心的是在之外定义的非均匀变量main()

float left;
float right;
float mscaled;
float xn;
float xm;

让我们看一下GPU和GLSL的工作方式。GPU没有堆栈或呼叫激活记录。无法像GL编译器在大多数CPU上一样在GLSL中模拟作用域或局部变量。存在的全部是寄存器,它们是统一寄存器,着色器阶段的输入,输出以及该着色器调用所独有的本地寄存器文件。

换句话说,因为没有函数,堆栈或堆之类的东西,所以声明在任何地方的所有变量都位于寄存器中。它们对于GLSL中的某个作用域而言是局部的,还是对于整个文件而言是全局性的,没有什么区别。它们只是寄存器。

但是,寄存器分配器不是GLSL标准的一部分。将高级GLSL代码转换为GPU可以理解的低级机器代码时,不同的OpenGL实现可能具有不同的质量级别。寄存器分配是编译器(GLSL或其他)更复杂的部分之一。这是编译器的部分,它确定给定变量占用哪些寄存器。C有点难,因为它通常必须处理非常小的寄存器文件(尤其是在x86上),并且必须处理寄存器溢出(将变量移至堆栈)和别名(在调用函数之前将变量保存回RAM)和要求输出在特定寄存器中的奇数指令(x86idiv例如)。由于没有堆栈或堆,GPU的寄存器文件很大,因此分配器可以更简单。

但是,寄存器文件不是无限的。如果变量多于硬件支持的寄存器,则编译器将不得不尝试将所有变量放入寄存器中。这通常需要某种形式的活动范围检查。也就是说,如果您使用一个变量xn进行一次计算,然后再也不使用它,则编译器可以确定该xn变量,然后知道以后所占用的寄存器可能会被另一个变量使用,因此允许的变量多于寄存器的数量(这么长因为一次没有太多实时变量)。

但是,编译器可能不会这样做。没有。或者它可能仅在某些情况下才这样做。范围使更简单的编译器更容易解决。分配给局部函数变量的所有寄存器都可以在该函数退出后重用,因为它知道变量已失效。全局变量没有这么容易的保证。因此,一些能力较弱的编译器可能也不会优化其生存期,并且全局变量将始终占用寄存器。这不会使任何事情变慢,但是可能会在某些驱动程序上限制您可以编写的着色器的大小。

通常,我强烈建议将所有变量都保持本地化。尽可能使定义尽可能接近变量的用法。这适用于所有编程语言,而不仅仅是GLSL。我还建议在可能的每种情况下都使每个“变量” const。这再次向某些能力较弱的编译器暗示了某些优化是可能的,更重要的是,它使您的代码更具自记录性且易于维护。

当然,这是您的强制性“公正的个人资料,可以测试并确定查找”建议。编写带有或不带有全局变量的着色器,并对其进行配置。在线上的任何和所有性能建议都应该受到信任,并假定是假装过时或过时的。


那正是我的意思。您完美地回答了我的问题,还更好地解释了我的要求。
RodgerDodger 2013年

1
实际上,有时将数组声明为const而不是非const会使整个着色器变慢(很多变慢)。我注意到这个问题在我的GTX 460
塔拉

我只是摆脱了一个奇怪的错误,并强烈怀疑这是一个Adreno GPU(OpenGL ES 3.1)无法编译着色器,因为变量是在main外部声明的。
comodoro

我见过的最彻底的SO答案之一-做得好!
duhaime,

今天,我学到一些新东西。今天,我学习了,我并不真正了解或了解glsl。仅仅因为我可以用它来制作圆柱形空间转换的gif,并不意味着我了解它的工作原理。
cmarangu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.