移动平台上shader的一点优化
前一阵子将引擎的bloom后处理移植到了移动平台上,跑起来效率比较低,虽然在降采样、高亮和高斯模糊的pass上都有缩水,降帧降的还是比较厉害。
我试图去掉降采样那一步,结果发现镜头移动起来画面会抖动得很厉害,只得作罢。降低采样的数量要么依旧抖动,要么对效率的贡献不大。
后来在翻看苹果的OpenGL ES Programming Guide for iOS的时候,发现这么一段话:
Dynamic texture lookups, also known as dependent texture reads, occur when a fragment shader computes texture coordinates rather than using the unmodified texture coordinates passed into the shader. Dependent texture reads are supported at no performance cost on OpenGL ES 3.0–capable hardware; on other devices, dependent texture reads can delay loading of texel data, reducing performance. When a shader has no dependent texture reads, the graphics hardware may prefetch texel data before the shader executes, hiding some of the latency of accessing memory.
大意就是动态纹理查找会丢弃预处理的数据,带来效率问题。至于 什么是动态纹理查找,苹果的官网上还有个例子:
1 2 3 4 5 6 7 8 |
varying vec2 vTexCoord; uniform sampler textureSampler; void main() { vec2 modifiedTexCoord = vec2(1.0 - vTexCoord.x, 1.0 - vTexCoord.y); gl_FragColor = texture2D(textureSampler, modifiedTexCoord); } |
这个例子在fragment shader中对纹理坐标进行了计算,因此导致dynamic texture lookups,建议将这段运算移动到vertex shader中完成。
在我们shader中有很多这样的写法,例如在模糊时这样写,这样都会带来效率问题。
1 2 3 |
for (int i=0;i<9;i++) { color += texture2D(texture, texCoord + offset[i]); } |
于是我将降采样和模糊的运算移动到vertex shader中去做,结果帧率没有上升,反而又降了不少。我将 texCoord声明为vec4,然后将其的xy分量和zw分量分别用于纹理查找,想着这样能省一些varying,却弄巧成拙。
1 2 3 4 5 6 |
varying mediump vec4 texCoord0; ... ... color += texture2D(tex0, texCoord0.xy); color += texture2D(tex0, texCoord0.zw); ... |
在Unreal的Bringing AAA graphics to mobile platforms里,也针对Texture Lookups专门做了说明:
- 不要在pixel shader里对纹理坐标做数学运算,在vertex shader里面算好传过来。
- 不要使用纹理坐标的zw分量,这样也会被当作是一次动态纹理查找。用xy来查找纹理,用zw来存别的数据好了。
遵循这两条指引,Unreal修改了god ray的shader代码,具体效果我们能够在Infinity Blade中看到。而我在改进bloom来避免第一种情况时,触发了第二种情况,导致帧率严重下降了。
后来发现stackoverflow上的一篇问题也遇到了跟我类似的情况,给出的解决方案也是避免动态纹理查找。我做了类似的修改,将zw拆成单独的vec2。最终用xcode看到的GPU time减少了3ms,demo渲染帧率从25fps提高到了29fps到30fps(满)。