与iOS9(OGLES2.0)相比,iOS10/11下的海量屏幕外渲染性能下降
背景:我们的开发中游戏在我们更新至iOS 10或11的设备上进行了性能潜水。两款iPhone 6s的运行时间为10.3 .3只能达到20-30fps,而iPhone 5s仍然以每秒60fps的速度顺利通过8.0。与iOS9(OGLES2.0)相比,iOS10/11下的海量屏幕外渲染性能下降
最近我更新了iPod6从ios 9到ios 11,它也从60下降到20-30 fps,运行完全相同的游戏构建。
注意:最初GPU分析器让我相信这是一个着色器相关问题,但那是一个错误的线索。感谢所有在此基础上评论的人。
下面是我已经收窄的问题了下来:
运行正常,我们的游戏生成以下屏幕外的纹理每一帧:
- 十选手的阴影,在256×256(无alpha混合参与)
- 十128×256 256×256到动画“电视屏幕”纹理
- 一个256×512个“全球思考”的质感结合场景模型的一小部分。
在iPhone5s上,所有这些都以平稳的60fps进行。在iPod6和iPhone6s上,由于更新到iOS10/11,它努力达到30fps。
作为测试,我将所有离屏渲染重定向到主帧缓冲区,禁用了深度检查并启用了对所有内容的Alpha混合,以确保任何内容都不会被tile渲染器优化。
结果是,游戏*渲染的像素数量是以前的十倍(因为填充256x256纹理的渲染现在全部填充640x1136屏幕),所有的alpha混合(在很多之前它没有混合),它在iPod6上以60fps的速度高兴地做到了这一点。
我知道我仍然可以对离屏渲染进行优化(我目前没有标记用于丢弃的阴影纹理上的深度缓冲区),但这不是真正的重点:5s是处理未经优化的渲染效果还不错,而iPod6也曾经使用过,所以在iOS 10/11下改变了什么?
重现步骤:
- 生成20小(256×256)的纹理和为它们分配帧缓存。
- 每一帧,为每个纹理渲染几个精灵,然后将纹理渲染到屏幕上。
- OpenGL剖析这个设置。
- 将所有精灵渲染重定向到屏幕(但保留'渲染纹理到屏幕'步骤)
- 剖析此设置。在我的测试中,尽管精灵渲染必须覆盖更多像素,但第二个设置的速度要快10ms。
根据我的测试,似乎某些iOS OGLES实现(特别是10.3.3和11)在优化交错渲染到屏幕和离屏纹理方面的工作严重不足。
理想(1),从基于区块的硬件在一帧期间渲染到多个目的地的角度来看,一个OpenGL实现将推迟尽可能长时的主帧缓冲区执行渲染命令,与效果解交错该要求序列:
- 绘制到主帧缓冲区
- 绘制纹理1
- 绘制纹理1主帧缓冲区
- 绘制到纹理2
- ...
- 绘制纹理n至主帧缓冲区
到该视觉等效执行序列:
- 绘制到纹理1
- 绘制纹理2
- ...
- 绘制纹理Ñ
- 绘制纹理1到主帧缓冲区
- 绘制纹理2到主帧缓冲区
- ...
- 绘制纹理n至主帧缓冲区
后者序列是远效率更高,因为它避免了重复逻辑加载的需要,并且存储主帧缓冲器的内容(2)。
但是,从一个简单的基准测试中可以看出,在更新版本的iOS中执行此优化的实施比以往更严重。
我的基准测试分配了10个256x256纹理和10个512x512纹理,并为它们分配了一个帧缓冲区。然后每个框架都会为每个纹理绘制大量的alpha混合精灵,然后将这些纹理渲染到屏幕(也是alpha混合)。我将它设置为默认情况下以交错顺序进行,然后在触摸屏幕时进行解交错处理。
这里是不变/触摸(差)在所测试的范围内的设备的结果:
- iPhone 5S(iOS的8.0):14毫秒/ 11.5ms(为2.5ms)
- 的iPod 6gen(iOS11) :19ms/11.8ms(7.2ms)
- iPhone6s(iOS10.3.3):17MS/8.2ms(8.8ms)
正如你可以看到,所有的设备从手动校正呈现顺序中受益。然而,不这样做的惩罚从iOS8下的2.5ms上升到类似硬件上的iOS11下高达7.2ms(在iPod上安装iOS11之前,它顺利运行我们的游戏,所以我觉得得出这个推论是合理的)。无疑,由于屏幕分辨率更高,iPhone6的罚款更高。
惩罚可能部分归因于逻辑加载和存储,部分原因是由渲染到纹理之前的依赖关系引入的停顿,然后才会被拖动到屏幕上。再说一遍:在一个将命令缓存到主帧缓冲区的实现中,那些停顿不会发生。
拒绝替代
逻辑加载和存储有自己在成本大幅上升。
这将解释观察到的放缓,但不影响iPod和iPhone5s在手动解交错渲染顺序时的性能相似性。纠正后的订单仍然需要大量的逻辑存储(10mb),并且没有明显额外成本的证据。
参考文献:
你在谈论的OpenGL-ES,对不对?您定位的是哪个版本? – BDL
OGLES2.0 - 现在添加到主帖。 – Peeling
几件事情要尝试:1.用两个独立的vec2替换vec4 texcoord,可以避免依赖于纹理的读取(尽管它们在新硬件上并不那么糟糕)。 2.使用临时vec4而不是多次写入/修改gl_FragColor。 – Columbo