iPad 1上的着色器性能较低

时间:2012-03-06 16:02:05

标签: performance ipad opengl-es-2.0 shader

我的绘画应用程序是使用OpenGL ES 1.0和一些Quartz编写的。 我正在尝试使用OpenGL ES 2.0重写它以获得更好的性能和新功能。 我写了2个着色器:一个渲染用户对纹理的输入,然后根据一些规则将这个纹理与一些其他纹理混合。 突然我意识到iPad第一代上的第二个着色器工作时间太长 - 我只有10-15 fps。 iPad 2与60+ fps完美配合。我有点震惊,因为原始应用程序(OpenGL ES 1.0)在两个设备上都能正常工作。它只渲染两个多边形(但几乎全屏)。 我已经尝试了一些优化,比如改变精度,评论一些数学运算,硬编码一些纹理调用 - 它有点帮助,但我仍然远离60 fps。只有当我完全评论这个着色器的调用时,我才有60 fps。

我错过了什么吗?我对OpenGL没有多少经验,但我相信这个着色器必须在两代设备上都能正常工作,就像原始应用程序一样。我的顶点和片段着色器是:

===============顶点着色器===================

uniform mat4 modelViewProjectionMatrix;

attribute vec3 position;
attribute vec2 texCoords;

varying vec2 fTexCoords;

void main()

{ 

    fTexCoords = texCoords;

    vec4 postmp = vec4(position.xyz, 1.0);
    gl_Position = modelViewProjectionMatrix * postmp;


}

===============片段着色器===================

        precision highp float;  

        varying lowp vec4 colorVarying;
        varying highp vec2 fTexCoords;
        uniform sampler2D texture; // black & white user should paint
        uniform sampler2D drawingTexture; // texture with user drawings I rendered earlier
        uniform sampler2D paperTexture; // texture of sheet of paper 
        uniform float currentArea; // which area we should not shadow
        uniform float isShadowingOn; // bool - should we shadow some areas of picture    

        void main()
        {
            // I pass 1024*1024 texture here but I only need 560*800 so I do some calculations to find real texture coordinates

            vec2 convertedTexCoords = vec2(fTexCoords.x * 560.0/1024.0, fTexCoords.y * 800.0/1024.0); 

            vec4 bgImageColor = texture2D(texture, convertedTexCoords);        
            float area = bgImageColor.a;        
            bgImageColor.a = 1.0;            
            vec4 paperColor = texture2D(paperTexture, convertedTexCoords);       
            vec4 drawingColor = texture2D(drawingTexture, convertedTexCoords);

    // if special area         
            if ( abs(area - 1.0) < 0.0001) {            
                // if shadowing ON        
                if (isShadowingOn == 1.0) {               
                   // if color of original image is black        
                    if ( (bgImageColor.r < 0.1) && (bgImageColor.g < 0.1) && (bgImageColor.b < 0.1) ) {        
                        gl_FragColor = vec4(bgImageColor.rgb, 1.0) * vec4(0.5, 0.5, 0.5, 1.0);         
                    }                    
                    // if color of original image is grey

                    else if ( abs(bgImageColor.r - bgImageColor.g) < 0.15 && abs(bgImageColor.r - bgImageColor.b) < 0.15 && abs(bgImageColor.g - bgImageColor.b) < 0.15 && bgImageColor.r < 0.8 && bgImageColor.g < 0.8 && bgImageColor.b < 0.8){   gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb * 0.4 - drawingColor.rgb * 0.4, 1.0);} 


                 else 
                 {    
                 gl_FragColor = vec4(bgImageColor.rgb, 1.0) * vec4(0.5, 0.5, 0.5, 1.0);     
                    }
                } 

                // if shadowing is OFF        
                else {           
                    // if color of original image is black    
                if ( (bgImageColor.r < 0.1) && (bgImageColor.g < 0.1) && (bgImageColor.b < 0.1) ) {
                    gl_FragColor = vec4(bgImageColor.rgb, 1.0); 
                } 

                    // if color of original image is gray
                else if ( abs(bgImageColor.r - bgImageColor.g) < 0.15 && abs(bgImageColor.r - bgImageColor.b) < 0.15 && abs(bgImageColor.g - bgImageColor.b) < 0.15 
                 && bgImageColor.r < 0.8 && bgImageColor.g < 0.8 && bgImageColor.b < 0.8){
                    gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb * 0.4 - drawingColor.rgb * 0.4, 1.0);

                    } 

                    // rest
                else {
                    gl_FragColor = vec4(bgImageColor.rgb, 1.0); 
                }


                }
            } 

    // if area of fragment is equal to current area
        else if ( abs(area-currentArea/255.0) < 0.0001 ) { 
            gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb - drawingColor.rgb, 1.0); 
        }

    // if area of fragment is NOT equal to current area 
        else {
            if (isShadowingOn == 1.0) {
                gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb - drawingColor.rgb, 1.0) * vec4(0.5, 0.5, 0.5, 1.0);        
            } else {
                gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb - drawingColor.rgb, 1.0);
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

在着色器中进行分支是非常昂贵的,因为它消除了GPU并行运行着色器的可能性,并且在片段着色器中有很多分支(一个着色器)无论如何应该尽可能快)。更糟糕的是,您根据GPU本身计算的值进行分支,这也会大大降低您的性能。

你真的应该尝试删除尽可能多的分支,而不是让GPU做一些“额外的工作”,例如。不试图优化纹理图集并渲染所有内容(如果可能的话),这仍然会比当前版本更快。如果这不起作用,请尝试将着色器拆分为多个较小的着色器,每个着色器仅执行较大着色器的特定部分并在CPU上而不是在GPU上进行分支(每次绘制调用时只需执行一次此操作不是每个“像素”)。

答案 1 :(得分:0)

除了JustSid关于着色器分支的有效观点之外,我还看到其他一些错误。首先,如果我只是通过Imagination Texhnologies的PVRUniSco编辑器(你真的应该得到它,并且是their free SDK的一部分)运行这个片段着色器,我看到了:

PVRUniScoEditor results

显示42个周期的最佳情况,该着色器最差为52周期。从similar case of fragment shader tuning I asked about开始,我发现11-16周期片段着色器需要35-68毫秒才能在iPad 1(15 - 29 FPS)上渲染。你需要让它变得更紧,以获得合理的渲染时间。

要消除某些分支,您可以使用步进功能或使用Alpha通道玩技巧。我已经完成了这项工作,并且看到了着色器渲染时间的大幅减少。我不会传递isShadowingOn制服,但我会把它分成两个着色器,用于打开和关闭不同的情况。

除了分支,我可以看到您正在执行bgImageColorpaperColordrawingColor的从属纹理读取,因为计算了要在片段中获取的纹理坐标着色器。这在iOS设备中基于图块的延迟渲染器上非常昂贵,因为它阻止了纹理提取的某些优化被使用。我建议不要计算每个片段,而是将此计算移动到顶点着色器,并将结果作为变量传递给片段着色器。使用不同的坐标来获取纹理,你会看到性能的巨大提升。

你可以做些小事来调整它。例如,

gl_FragColor = vec4((paperColor.rgb * bgImageColor.rgb - drawingColor.rgb) * 0.4, 1.0);

应该比

略快
gl_FragColor = vec4(paperColor.rgb * bgImageColor.rgb * 0.4 - drawingColor.rgb * 0.4, 1.0);

编辑器将对您的着色器进行实时编译,因此您可以在代码中尝试这些操作,并根据估计的GPU周期查看结果。