VR 性能模式介绍
更新日期:
VR 为何需要性能模式
由于 VR 这个应用场景的特殊性,它需要极低的显示延迟,否则就会出现眩晕感(VR 这套原理网上有,当然也可看看我写的 headtracking 相关文章:VR HeadTracking 分析)。所以用 android 平台跑 VR 业务,是不能使用 android 原来那套显示框架的。那套 Vysnc + 三重缓冲 的框架,显示延迟至少一个 Vysnc 周期(原因可以看我这篇文章的相关分析:Activity 启动速度分析方法(启动流程分析)#Vsync简介 )。所以就需要 Single Buffer + Direct Render。这个就要求在一个 Vsync 周期里完成 应用渲染 + ATW 反畸变,反色差后处理送帧。VR 应用 和 普通 android 应用 的区别:
- VR 应用都是 OpenGL 3D 渲染的(我们用的引擎是 Unity引擎 或是 oculus ovr 那套),而且还是双目渲染,GPU loading 是比普通的 andrid 应用高不少。
- 普通 android 平台,应用渲染完 SurfaceFlinger 直接送显就行了,VR 还需要反畸变、反色差后处理(这个原因网上也有,简单的说是由于 VR 的光学镜片造成的畸变,需要图像处理进行矫正)。业界基本上是拿 GPU 做的,这让 GPU loading 进一步提高(我之前开发的平台这块是硬件处理的)。
- 我开发 VR 的时候是 2017年 那会,VR 主流分辨率是 2560x1440@70Hz,基本手机的刷新率是 60Hz。这个显示规格明显高了手机一截。不像现在(2019年)手机上有 90Hz、120Hz 了,但是即便手机上有 90Hz 和 120Hz 也不是一直都是这个刷新率(大部分时候还是 60Hz),而且 VR 是一直要保持 70Hz 这个刷新率的(VR 需要高刷新率是为了更低的延迟)。分辨率和刷新率的提升,也加重了系统的负载。
- 普通 android 场景偶尔有几次丢帧关系不大,但是在 VR 里面就会对用户造强烈的不舒适。VR 要求不能丢帧。
所以 VR 场景对系统的性能要求极高。而我开发 VR 的平台是一个 28nm 的 4核 1.8G 的 A53,GPU 也只有 Mali 760 mp2,而我们对标的竞品是 高通骁龙820,简直是青铜对线王者。要让应用达到稳定渲染稳定 60fps 不丢帧(应用渲染 60fps,ATW 插帧插到 70fps),需要极限压榨硬件性能。所以才有了 VR 性能模式这个说法,这个其实是下面几个措施的合称。因为涉及到的代码都是 vendor 的相关实现,所以这里不说具体代码实现,只是介绍一些措施思路而已。
场景模式
android 有一个 power hal,在里面可以定义一些 场景模式,不同的场景模式可以通过写硬件的文件节点控制频率,开关核等操作。对应到 PowerManager 接口就是 PowerHit:
|
|
hintId 是 场景编号,这个接口不对外公开,只在 SystemService 里调用。框架如下:
可以在应用使能 VrMode (Vr Mode 是 Android 7.1 的时候加入的)的时候,通过这个接口切换到对应的 场景模式。例如我开发的 VR 平台为了保证性能,VR 的场景模式将 CPU 的最低频率锁定到一个比较高的值;关闭 cpu idle,强制 4核 全部开启;GPU 最低频率也锁定到一个比较高的值。那为什么不直接锁最高频率,因为 soc 的最高频率一般都只是一个理论值(低端 soc 基本适用),最高频率只要保持一段时间,基本上就要温控强制 降频+关核 了。
这里再多介绍一下,为什么手动锁最低频率,而不是依靠调频策略根据当前负载动态调频。一般调频策略都会有个延后性,一般来说 cpu(gpu)要提频,都要负载高于某个阀值保持一段时间才会提。这个就涉及到调频算法的优劣了,我开发 VR 平台那个算法,额, 就是简单粗错达到了某个点就升,低于那个点了就降。在 VR 场景一会负载高一点,一会又稍微低一点,频率忽高忽低很不稳定,导致应用帧率波动很大。 而我们要求的是稳定 60fps,所以这里就需要手动把最低频率锁定在某个合适的频率。还留有一定的调节空间,兼顾负载峰值和功耗。
设置渲染线程优先级
android 是 linux 内核的,是一个多任务系统。在 VR 应用前台渲染的时候,还有很多后台任务在跑。所以可以提升渲染线程的优先级(设置线程的 nice 值),让渲染线程更优先得到 cpu 资源。可以拉一个接口,让应用把自己的渲染线程的 tid 传过来,在系统里面进行配置。
cpu 绑定
我开发的 VR 平台是 4核 的,我发现大部分任务都默认被分配到了 0,1 核心(4个核心的编号是 cpu0,cpu1,cpu2、cpu3),包括了应用渲染线程。这就造成了渲染线程需要和其他任务强 cpu 资源。但是 2、3 核的任务就比较轻。于是我们就可以设计一下:让渲染线程跑到 2、3 核心。让其有足够的 cpu 资源,充分发挥4核 cpu 的优势(我们的 cpu 是 SMP 架构的,不是现在流行的大小核的,所以4个核的算力是一样的)。
绑定渲染线程 cpu
linux 有一个叫 cgroup 的概念。就是可以创建一个组别(cpu group),配置这个组别的 cpu 运行资源,其中就可以指定这个组别的任务跑到哪几个 cpu 上面。我们就可以创建一个 VR组别,配置这个组别的任务跑到 2,3 核上。再拉个接口,让 VR 应用把自己的 uid 传过来就行,把 VR 应用加入到 VR组 就能让 VR 应用渲染线程跑到 2、3 核了,不去和其他线程抢 cpu 资源。
在 开启VR场景模式 + 给渲染线程设置高优先级 + 绑定空闲 cpu 核心 后,VR应用渲染线程的 cpu运行时间 比以前有了很大的改善,帧率稳定了很多。systrace 能看出来(上面是没开启前,下面是开启后的,可以看到 systrace 基本都是绿的 Running 状态,基本没有等待状态了):
绑定显示中断 cpu
有了上面的措施后,在某些场景还是会卡断掉帧。分析过后发现是显示中断受了影响(Vysnc 就是显示中断产生的)。例如 mtp copy 创建,短时间内 mtp 中断频繁发生,抢占了显示中断的 cpu 资源(用户线程是抢占不过中断的,只有中断之间能抢占)。有了上面绑定 cpu 的经验,我们就把显示中断绑到 cpu3 上去,减少其他中断对显示中断的影响。