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用語のユニフォームまたは属性)または定数値ですか?
Sean Middleditch 2013

あなたが話していることの例としていくつかのコードを投稿できますか?
ネイサンリード

質問をコードで更新しました。
RodgerDodger 2013

@ user1286792:GLSLにメインループがないという事実は変わりません。あなたが何を話しているのかはっきりしない。これを実行することで正確に何が救われると思いますか?
Nicol Bolas 2013

@NicolBolas質問をより明確にするために更新しました。うまくいけば、それは将来の誰かにとって今有用です。
RodgerDodger 2013

回答:


33

私はあなたが尋ねようとしていることを理解していると思います。私はあなたの主な関心がの外で定義された不均一な変数であると思いますmain()

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

GPUとGLSLがどのように機能するかを見てみましょう。GPUには、スタックまたはコールアクティベーションレコードがありません。CコンパイラがほとんどのCPUで実行できるような、GLSLのスコープまたはローカルバリアブルをシミュレートする方法はありません。存在するのはレジスターだけです。レジスターは、ユニフォームレジスター、シェーダーステージの入力、出力、およびそのシェーダーの呼び出しに固有のローカルレジスターファイルです。

つまり、関数、スタック、ヒープなどは存在しないため、どこでも宣言されたすべての変数はレジスターに存在します。それらがGLSLの一部のスコープに対してローカルであっても、ファイル全体に対してグローバルであっても、違いはありません。彼らはただの登録です。

ただし、レジスタアロケータはGLSL標準の一部ではありません。高レベルのGLSLコードをGPUが理解する低レベルのマシンコードに変換する場合、OpenGLの実装によって品質レベルが異なります。コンパイラのより複雑な部分の1つ(GLSLなど)は、たとえば)です。スタックやヒープがないため、GPUには大規模なレジスタファイルがあるため、アロケータをより簡単にすることができます。レジスターの割り当てです。これは、特定の変数がどのレジスタを占有するかを決定するコンパイラの一部です。Cは通常、非常に小さなレジスタファイル(特にx86の場合)を処理する必要があり、レジスタのスピル(変数をスタックに移動する)とエイリアス(関数を呼び出す前に変数をRAMに保存する)を処理する必要があるため、少し難しくなります。出力を要求する奇数の命令は特定のレジスタ(x86のidiv

ただし、レジスタファイルは無限ではありません。ハードウェアでサポートされているレジスタよりも多くの変数がある場合、コンパイラはすべての変数をレジスタに収めようとする必要があります。通常、これには何らかの形の活性範囲チェックが必要です。つまりxn、ある計算に変数を使用し、それを二度と使用しない場合、コンパイラーはこれを判別xnし、後で占有されているレジスターが別の変数によって使用される可能性があることを認識できるため、レジスターよりも多くの変数を許可できます(非常に長い)一度にあまり多くのライブ変数がないため)。

ただし、コンパイラはこれを行わない場合があります。ありません。または、それはいくつかの場合にのみそれを行うかもしれません。スコープが単純なコンパイラでは、問題の解決がはるかに簡単です。ローカル関数変数に割り当てられたすべてのレジスターは、変数が無効であることを認識しているため、関数の終了後に再利用できます。グローバル変数にはそのような簡単な保証はありません。したがって、一部の機能が低いコンパイラーもライフタイムを最適化しない可能性があり、グローバル変数は常にレジスターを消費します。これによって速度低下することはありませんが、ドライバによっては、書き込むことができるシェーダーのサイズが制限される場合があります。

一般的に、すべての変数をローカライズしておくことを強くお勧めします。意味のある変数の使用にできるだけ近い定義を維持します。これは、GLSLだけでなく、すべてのプログラミング言語に適用されます。また、可能な限り、すべての「変数」constを作成することをお勧めします。これも、特定の最適化が可能であることを特定の能力の低いコンパイラーにヒントにすることができ、さらに重要なことに、コードをより自己文書化し、保守を容易にします。

そしてもちろん、ここにあなたの義務的な「テストし、確実に見つけるためのただのプロフィール」のアドバイスがあります。シェーダーをグローバルの有無にかかわらず記述し、プロファイルします。オンラインでのパフォーマンスに関するアドバイスはすべて信頼されておらず、想定が乱されているか、古くなっていると見なされます。


それがまさに私の意図したことです。あなたは私の質問に完璧に答え、また私が求めていることをよりよく説明してくれました。
RodgerDodger 2013

1
実際、非constではなくconstとして配列を宣言すると、シェーダー全体が遅くなります(LOTが遅くなります)。私は私のGTX 460にその問題に気づいた
タラ

私は奇妙なエラーを取り除いたばかりで、変数がメインの外で宣言されたため、シェーダーのコンパイルに失敗したAdreno GPU(OpenGL ES 3.1)であると強く疑っています。
コモドロ

私が今まで見た中で最も完全なSOの答えの1つ-よくできました!
duhaime

今日は新しいことを学びます。今日、私は学ぶ、私は本当にGLSLを知らないか理解していない。それを使用して円柱空間を変換したジオメトリgifを作成できるからといって、それがどのように機能するかを理解しているわけではありません。
cmarangu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.