OpenGL ES着色器在较旧的手机上出现巨大的性能问题

OpenGL ES着色器在较旧的手机上出现巨大的性能问题

问题描述:

我正在使用我的光手在一些手机上面临一些性能问题。 着色器根据正常图计算线条和点光线。 这也表明命中目标(这是一个2D自上而下的射手游戏)。OpenGL ES着色器在较旧的手机上出现巨大的性能问题

我正在开发索尼Xperia Z5 Compact,其中一切正常 就好。然后我在我非常古老的三星Galaxy S2上试了一下,其中 真的很慢。由于电话的年龄我不在乎。

现在我尝试了Galaxy S4和Galaxy S5,但它似乎并没有比Galaxy S2更快地运行 。我也有编辑时间(大约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(我 Tommy

这是我该怎么做才能解决它是什么:

  1. 删除screenCoordToWorldCoord。这是一个简单的转换,你可以用矩阵乘或者几个点积来完成,或者更好的是,将工作移动到顶点着色器,并将结果传递给变量而不是从gl_FragCoord构造。

  2. 为每个光照计数编译不同版本的着色器,并展开for循环。您也可以将if放在calculatePointLight的顶部。

  3. 删除所有剩余的if语句 - 某些设备讨厌条件。用数学来代替逻辑,step函数有帮助。

  4. 有什么方法可以丢弃rotateVector?我无法弄清楚它在做什么,但它很昂贵,并且觉得它在片段着色器中不应该是必需的。至少,它不需要在内部循环中,因为无论光线如何,结果都是相同的。使用某种矩阵乘法可能会更好,而不是使用sin/cos

  5. 正确使用精度。有些设备可以在lowp/mediump比在highp更快地做数学。经验法则 - 颜色为lowp,法线为mediump,职位为highp

  6. 在CPU上做一些光线剔除。我想不是所有的灯都会影响每个像素。如果你可以将场景切成瓦片,只让最重要的灯光数过去,那么你可以做更少的工作。

  7. LightPos.xy/worldSize看起来你可以在CPU上做一次,而不是每个像素一次。

没有快速修复恐怕。

+0

听起来很合理,谢谢。我会尽力改变这一切。函数rotateVector正在旋转法线贴图的法线信息 – Draz

+0

我不知道任何实现'lowp'的现代设备与'mediump'有什么不同 - 它们都倾向于映射到fp16硬件。 – solidpixel