日別アーカイブ: 2012年2月19日

OpenGL ES 2.0 Adreno 205 と discard 命令の問題

GPU Adreno 205 は Fragment Shader で discard 命令を使うと
フリーズし、OS ごと再起動することがあるようです。
コメントchototsu さんより情報を頂きました。
ありがとうございました。

試したらあっさり再現してしまいました。
ドライバレベルで停止しているらしく発生すると何もできなくなります。
デバッガで強制的にプロセスを削除すると OS ごと再起動します。
またはシェーダー実行直後に何もしなくても OS がリブートします。

試した機種
Xperia Ray SO-03C Android 2.3.3
Qualcomm MSM8255 1.0GHz Adreno 205

Adreno 200/220 では発生しません。205 だけです。
ただ 205 でもフリーズしないケースもあったので、条件を変えて
症状を調べてみました。

結論としては
FragmentShader で Texture を使用している場合に、
discard 命令より後で Texture フェッチが発生すると固まります。

Alpha Test や Clip Plane 等で discard を使う場合は
十分注意した方がよさそうです。

以下検証した結果

●Texture を使っていないシェーダーの場合

(1) 無条件 discard は固まらない
(2) varying 依存 discard も固まらない

// (1)  問題なし
precision mediump float;
void main()
{
    discard;
}
// (2)  問題なし
precision mediump float;
varying vec2 vTexcoord;
void main()
{
    if( vTexcoord.x < 0.5 ){
        discard;
    }
    gl_FragColor= vec4(1.0, 1.0, 0.0, 1.0);
}

●Texture を使っているシェーダーの場合

(3) 無条件 discard はどこに挿入しても固まる。
(4) texture 依存 discard は shader の一番最後なら固まらない。
  ・すべての texture 読み込み命令のあとなら固まらない。
(5) varying 依存 discard も (4) と同じだが、texture フェッチの
  前でも固まらない場合があった。

// (3)  freeze
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    // discard; ここでも同じ
    gl_FragColor= texture2D( ColorMap, vTexcoord );
    discard;
}
// (4) - A  問題なし
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    lowp vec4  color= texture2D( ColorMap, vTexcoord );
    if( color.w < 0.7 ){
        discard;
    }
    gl_FragColor= color;
}
// (4) - B  freeze
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    lowp vec4  color= texture2D( ColorMap, vTexcoord );
    if( color.w < 0.7 ){
        discard;
    }
    color+= texture2D( ColorMap, vTexcoord + vec2(0.1,0.1) );
    gl_FragColor= color;
}
// (4) - C  問題なし
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    lowp vec4  color= texture2D( ColorMap, vTexcoord );
    color+= texture2D( ColorMap, vTexcoord + vec2(0.1,0.1) );
    if( color.w < 0.7 ){
        discard;
    }
    gl_FragColor= color;
}
// (5) - A  freeze
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    if( vTexcoord.x < 0.5 ){
        discard;
    }
    gl_FragColor= texture2D( ColorMap, vTexcoord );
}
// (5) - B  問題なし
precision mediump float;
varying vec2 vTexcoord;
uniform lowp sampler2D ColorMap;
void main()
{
    gl_FragColor= texture2D( ColorMap, vTexcoord );
    if( vTexcoord.x < 0.5 ){
        discard;
    }
}

テクスチャ命令との順番が関係しています。(3),(5) に関しては
例外もありますが discard が Texture と依存関係が無いので、
コンパイラによって命令の実行順が変更されたためだと考えられます。

対策としては discard を使わないか、すべてのテクスチャ命令のあと
出来るだけ最後に記述するようにします。

ただ幸いなことに、discard は半透明と同じように TBDR の PowerVR
と大変相性が悪い命令です。さまざまな GPU への対応を考えると、
おそらくテクスチャを多数読み込むような複雑なシェーダーでは、
discard があまり積極的に用いられていないのではないかと思います。
(Adreno でもあまり推奨されていません。)

また discard の用途は多くが Alpha Test だと思うので、この場合
必ず Texture 命令のあとに用いられます。
上と同じ理由でテクスチャ一枚きりの単純なものが多く、今まで
あまり問題になっていなかったのではないかと考えられます。

関連エントリ
さらに OpenGL ES 2.0 Mobile GPU の速度比較