4

現在、約15k行のLinux(313.0 nvドライバーを使用したNVIDIA 360Mカード)でOpenGL 3.1(GLSLバージョン330を使用)アプリケーションを作成しています。私の問題は、私の頂点シェーダーの 1 つで、実際には何もしないはずのコードに最小限の変更を加えるだけで、大幅なパフォーマンスの低下が発生する可能性があることです。

例えば:

// With this solution my program runs with 3-5 fps
for(int i = 0; i < 4; ++i) {
  vout.shadowCoord[i] = uShadowCP[i] * w_pos;
}

// But with this it runs with 30+ fps
vout.shadowCoord[0] = uShadowCP[0] * w_pos;
vout.shadowCoord[1] = uShadowCP[1] * w_pos;
vout.shadowCoord[2] = uShadowCP[2] * w_pos;
vout.shadowCoord[3] = uShadowCP[3] * w_pos;

// This works with 30+ fps too
vec4 shadowCoords[4];
for(int i = 0; i < 4; ++i) {
  shadowCoords[i] = uShadowCP[i] * w_pos;
}
for(int i = 0; i < 4; ++i) {
  vout.shadowCoord[i] = shadowCoords[i];
}

またはこれを考慮してください:

uniform int uNumUsedShadowMaps = 4; // edit: I called this "random_uniform" in the original question

// 8 fps
for(int i = 0; i < min(uNumUsedShadowMaps, 4); ++i) {
    vout.shadowCoord[i] = vec4(1.0);
}

// 30+ fps
for(int i = 0; i < 4; ++i) {
  if(i < uNumUsedShadowMaps) {
    vout.shadowCoord[i] = vec4(1.0);
  } else {
    vout.shadowCoord[i] = vec4(0.0);
  }
}

この問題が発生したシェーダー コード全体を参照してください: http://pastebin.com/LK5CNJPD

何がこれらを引き起こす可能性があるかについて、どんなアイデアも高く評価されるように.

4

1 に答える 1

3

私はついに問題の原因を突き止め、解決策も見つけました。

しかし、解決策に飛びつく前に、この「バグ」を再現できる最小限のシェーダー コードを貼り付けさせてください。

頂点シェーダー:

#version 330 

vec3 CountPosition(); // Irrelevant how it is implemented.

uniform mat4 uProjectionMatrix, uCameraMatrix;

out VertexData {
    vec3 c_pos, w_pos;
    vec4 shadowCoord[4];
} vout;

void main() {
    vout.w_pos = CountPosition();
    vout.c_pos = (uCameraMatrix * vec4(vout.w_pos, 1.0)).xyz;
    vec4 w_pos = vec4(vout.w_pos, 1.0);

    // 20 fps
    for(int i = 0; i < 4; ++i) {
        vout.shadowCoord[i] = uShadowCP[i] * w_pos;
    }

    // 50 fps
    vout.shadowCoord[0] = uShadowCP[0] * w_pos;
    vout.shadowCoord[1] = uShadowCP[1] * w_pos;
    vout.shadowCoord[2] = uShadowCP[2] * w_pos;
    vout.shadowCoord[3] = uShadowCP[3] * w_pos;

    gl_Position = uProjectionMatrix * vec4(vout.c_pos, 1.0);
}

フラグメント シェーダー:

#version 330

in VertexData {
    vec3 c_pos, w_pos;
    vec4 shadowCoord[4];
} vin;

out vec4 frag_color;

void main() {
    frag_color = vec4(1.0);
}

そして面白いことに、頂点シェーダーを最小限に変更するだけで、両方のソリューションを 50 fps で動作させることができます。main 関数は次のように変更する必要があります。

void main() {
    vec4 w_pos = vec4(CountPosition(), 1.0);
    vec4 c_pos = uCameraMatrix * w_pos;

    vout.w_pos = vec3(w_pos);
    vout.c_pos = vec3(c_pos);

    // 50 fps
    for(int i = 0; i < 4; ++i) {
        vout.shadowCoord[i] = uShadowCP[i] * w_pos;
    }

    // 50 fps
    vout.shadowCoord[0] = uShadowCP[0] * w_pos;
    vout.shadowCoord[1] = uShadowCP[1] * w_pos;
    vout.shadowCoord[2] = uShadowCP[2] * w_pos;
    vout.shadowCoord[3] = uShadowCP[3] * w_pos;

    gl_Position = uProjectionMatrix * c_pos;
}

違いは、上のコードがシェーダーから変数を読み取るのに対し、下のコードはそれらの値を一時変数に保存し、出力変数にのみ書き込むことです。

結論:

シェーダーの変化を読み取​​ることは、一時的な変数を 1 つ減らすための最適化として使用されることがよくあります。または、少なくともインターネット上の多くの場所で見てきました。前の事実にもかかわらず、変化するアウトを読み取ることは、実際には無効な OpenGL 操作である可能性があり、GL を未定義の状態にする可能性があり、コードのランダムな変更が悪いことを引き起こす可能性があります。

これについての最もよいことは、GLSL 330 仕様が、以前に書き込まれたアウト バリエーションからの読み取りについて何も述べていないことです。それはおそらく私がすべきことではないからです。


PS

i < min(uNumUsedShadowMaps, 4)また、元のコードの 2 番目の例はまったく異なるように見えるかもしれませんが、この小さなコード スニペットではまったく同じように動作することに注意してください。out 変数が読み取られると、for ループの as 条件で非常に遅くなります。バリエーションは書かれているだけで、パフォーマンスに変化はなく、i < min(uNumUsedShadowMaps, 4)50 fps でも動作します。

于 2013-10-18T23:59:42.850 に答える