2017-11-10 3 views
0

私のライトシェイダーでは、一部の携帯電話でパフォーマンスの問題が発生しています。 シェーダは、ノーマルマップに基づいて、ラインライトとポイントライトを計算します。 ターゲット上のヒットを示しています(これは2Dトップダウンシューティングゲームです)。古い携帯電話でのOpenGL ESシェーダの巨大なパフォーマンスの問題

私はSony Xperia Z5 Compactで開発していますが、すべてが動作するのは です。それから私は非常に古いサムスンのギャラクシーS2でそれを試しました、 それは本当に遅いです。私は電話の時代のために気にしなかった。

今私はGalaxy S4とGalaxy S5で試しましたが、それは よりも速くGalaxy S2上で実行されているようです。私もhuuuugeのコンパイル時間(約90秒)を持っていましたが、コードを最適化することによって数秒のようにそれを持ってきました(私はそれがまだ厄介だと思う、私は実際にシェイダーにはありません)。

私は本当にここでボトルネックとは何か、なぜそれらのS4とS5の上で実行されていないか分からない。

これは私のシェーダです:

#ifdef GL_ES 
#define LOWP lowp 

precision highp float; 

#else 
    #define LOWP 
#endif 


#define MAX_NUM_POINTLIGHTS 16 


uniform vec3 lpos[MAX_NUM_POINTLIGHTS]; // Light position 
uniform vec3 foff[MAX_NUM_POINTLIGHTS]; // Light falloff 
uniform vec4 acol[MAX_NUM_POINTLIGHTS]; // Ambient color 
uniform vec4 lcol[MAX_NUM_POINTLIGHTS]; // Light color 



//textures 
uniform sampler2D u_texture1; // diffuse texture 
uniform sampler2D u_texture2; // normalmap 


varying vec4 vColor; 
varying vec2 vTexCoord; 
varying float vFlags; 


uniform vec2 Resolution;  //resolution of screen 

const float WORLD_WIDTH = 1440.0; 
const float WORLD_HEIGHT = 2560.0; 

vec3 getPointLightColor(const vec4); 
vec3 rotateVector(const vec3 vector, const float angle); 
vec2 screenCoordToWorldCoord(const vec2 screencoord); 
vec3 calculatePointLight(const vec4 DiffuseColor, vec3 LightPos, const vec3 Falloff, const vec4 LightColor, const vec4 AmbientColor); 



const float stdratio = WORLD_HEIGHT/WORLD_WIDTH; 
vec2 worldFragCoord; 
const float worldRatio_W_DIV_H = WORLD_WIDTH/WORLD_HEIGHT; 
const vec2 worldSize = vec2(WORLD_WIDTH, WORLD_HEIGHT); 



// Light variables 
vec3 NormalMap; 
vec2 worldFragCoordNormalized; 
vec3 N; 


void main() { 

    worldFragCoord = screenCoordToWorldCoord(gl_FragCoord.xy); 

    // Common light calculations 
    NormalMap = texture2D(u_texture2, vTexCoord).rgb; 
    worldFragCoordNormalized = worldFragCoord/vec2(1440.0, 2560.0); 
    N = normalize(NormalMap * 2.0 - 1.0); 


    vec4 DiffuseColor = texture2D(u_texture1, vTexCoord); 
    vec2 fragcoord = gl_FragCoord.xy; 

    vec3 pointcolor = getPointLightColor(DiffuseColor); 



    vec4 finalColor; 

    // green channel of vColor indicates hit 

    if (vColor.g > 0.0 && vColor.a == 0.0) { 
     vec4 fragcol = vec4(pointcolor, DiffuseColor.a); 
     vec4 addColor; 
     if (vColor.g > 0.67) 
      addColor = vec4(1.0,1.0,1.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.52) 
      addColor = vec4(1.0,0.0,0.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.37) 
      addColor = vec4(0.0,0.0,1.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.22) 
      addColor = vec4(1.0,1.0,0.0, DiffuseColor.a*vColor.g); 
     else 
      addColor = vec4(0.0,1.0,1.0, DiffuseColor.a*vColor.g); 

     finalColor = addColor*addColor.a + fragcol*(1.0-addColor.a); 
    } 
    else 
     finalColor = vec4(pointcolor, DiffuseColor.a); 



    gl_FragColor = finalColor; 

} 



vec3 rotateVector(const vec3 vector, const float angle){ 

    float degree = radians(360.0*angle); // Angle is normalized to 0 - 1 

    float cos_ = cos(degree); 
    float sin_ = sin(degree); 

    return vec3(vector.x*cos_ - vector.y*sin_, vector.x*sin_ + vector.y*cos_, vector.z); 
} 



vec3 calculatePointLight(const vec4 DiffuseColor, vec3 LightPos, const vec3 Falloff, const vec4 LightColor, const vec4 AmbientColor){ 


    if (LightPos.x == 0.0 && LightPos.y == 0.0) 
     return vec3(0.0); 



    LightPos.xy = LightPos.xy/worldSize; 


    //The delta position of light 
    vec3 LightDir = vec3(LightPos.xy - worldFragCoordNormalized, LightPos.z); 

    //Correct for aspect ratio 
    LightDir.x *= worldRatio_W_DIV_H; 

    //Determine distance (used for attenuation) BEFORE we normalize our LightDir 
    float D = length(LightDir); 

    //normalize our vectors 

    vec3 L = normalize(LightDir); 
    vec3 NN = N; 
    if (vColor.a == 0.0) 
     NN = normalize(rotateVector(NN, vColor.r)); 

    //Pre-multiply light color with intensity 
    //Then perform "NN dot L" to determine our diffuse term 
    vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(NN, L), 0.0); 

    //pre-multiply ambient color with intensity 
    vec3 Ambient = AmbientColor.rgb * AmbientColor.a; 

    //calculate attenuation 
    float Attenuation = 1.0/(Falloff.x + (Falloff.y*D) + (Falloff.z*D*D)); 

    //the calculation which brings it all together 
    vec3 Intensity = Ambient + Diffuse * Attenuation; 
    vec3 FinalColor = DiffuseColor.rgb * Intensity; 


    return FinalColor; 
} 



vec3 getPointLightColor(const vec4 DiffuseColor){ 

    vec3 sum = vec3(0.0); 

    for (int i = 0; i < MAX_NUM_POINTLIGHTS; i++) 
    { 
     sum += calculatePointLight(DiffuseColor, lpos[i], foff[i], lcol[i], acol[i]); 
    } 

    return sum; 

} 




vec2 screenCoordToWorldCoord(const vec2 screencoord){ 
    float ratio = Resolution.y/Resolution.x; 

    vec2 resCoord; 
    if (ratio == stdratio){ 
     // Ratio is standard 
     resCoord = screencoord * (WORLD_HEIGHT/Resolution.y); 
    } else if (ratio > stdratio) { 
     // Screen gets extended vertically (black bars top/bottom) 

     float screenheight = Resolution.x * stdratio; 
     float bottom = (Resolution.y - screenheight)/2.0; 
     resCoord = vec2(screencoord.x, screencoord.y - bottom); 
     resCoord *= (WORLD_WIDTH/Resolution.x); 

    } else { 
     // Screen gets extended horizontally (black bars left/right) 

     float screenwidth = Resolution.y/stdratio; 
     float left = (Resolution.x - screenwidth)/2.0; 
     resCoord = vec2(screencoord.x - left, screencoord.y); 
     resCoord *= (WORLD_HEIGHT/Resolution.y); 

    } 

    return resCoord; 
} 
+1

私の電話よりも消化する時間を見つけようとしますが、一般的なルールとして、ループや条件は問題です。不必要な作業をしたり、クランプやステップなどを組み合わせて効果的に廃棄します。 – Tommy

+0

それらのループは前に展開されていましたが、定数を使ってループを読むのはうまくいきました。あなたはそれらの条件がこれを引き起こすと思いますか? – Draz

+0

ええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええ、 'if(i )に追加すると、その実装の戦略に応じて、条件付き分岐が再導入される可能性があります。いくつかは選択的に再コンパイルします。条件付きでは、SIMDy並列化GPUの適用を妨げることが多いので、これを引き起こす可能性があります。ピクセルAとピクセルA + 1が同じ命令ストリームに従うことがもはや真でない場合、両方を同時に実行することは困難です。 – Tommy

答えて

1

は、ここで私はそれに取り組むために行うだろうものです:

  1. screenCoordToWorldCoordを削除します。これは単純な変換です。マトリクス乗算やドットプロダクトを使用して行うこともできますし、より良い方法として、頂点シェーダに移動して結果をgl_FragCoordから構築するのではなく、さまざまな方法で渡すこともできます。

  2. ライトカウントごとに異なるバージョンのシェーダをコンパイルし、forループを展開します。 calculatePointLightの上部にあるifも削除されます。

  3. 残りのif文をすべて削除します。一部のデバイスでは条件文が嫌いです。代わりに数式を使ってロジックを実行すると、ステップ関数が役立ちます。

  4. ドロップする方法はありますかrotateVector?私はそれが何をしているのか分かりませんが、高価で、フラグメントシェーダでは必要ではないような感じです。最低でも、結果には関係なく結果が同じであるため、内部ループに入る必要はありません。 sin/cosを使用するのではなく、何らかの行列乗算を行う方が良いかもしれません。

  5. 精度を正しく使用してください。 lowp/mediumpでは、highpよりもはるかに高速に計算できます。親指の規則 - 色はlowp、法線はmediump、位置はhighpです。

  6. CPUを軽く間引きしますか?私はすべての光がすべてのピクセルに影響するとは思わない。あなたのシーンをタイルにチョップして、最も重要なライトを数えさえすれば、もっと少ない労力を費やすことができます。

  7. LightPos.xy/worldSizeは、ピクセルごとに1回ではなくCPU上で1回行うことができるようです。

私は恐れがありません。

+0

妥当な音です、ありがとうございます。私はそれをすべて変更しようとします。関数rotateVectorは法線マップの法線を回転させています – Draz

+0

私は 'lowp'を' mediump'とは違って実装している現代的なデバイスは認識していません - どちらもfp16ハードウェアにマッピングされる傾向があります。 – solidpixel

関連する問題