Skip to content

卡顿、掉帧怎么查 ​

阴阳师、星铁、终末地都是这么查出来的。开发者的机器一般配置都很高,加核、加内存解决不了的才是要查的。

1. 先确认测的是什么 ​

  • 确认真进了要测的画面。菜单、读条、战斗、大世界要分开测。
  • 战斗要按自动化脚本的日志切出每一场来算。30 秒一个窗口的话,里面混着菜单和读条,算出来的不是战斗帧率。
  • 记下同机开着几个别的模拟器:开多了会全核降频,帧率整体掉,结论要连同这个数一起看。
  • 对比时用同一场景、同一画质、同一实例配置。

2. 测帧率 ​

  • 客体 logcat 里 EGL_emulation 每秒打一行 app_time_stats: avg=… min=… max=… count=…。
  • 只取游戏主进程的行:父进程是 zygote64、名字是包名的那个。
  • count 加起来除以秒数就是帧率,max 是这一秒里最慢的一帧。

3. 看瓶颈在客体还是宿主 ​

客体:shell top -H -p <游戏 pid> 看各线程的 CPU,重点看主线程(Unity 游戏叫 UnityMain)和渲染线程。

  • 一条线程吃满一个核、其余核大多空闲:单线程瓶颈。加核、加内存都没用。
  • 主线程很忙,帧率却不随 CPU 频率变化:它多半在等(锁、访存、宿主回包),不是在算。

宿主:用任务管理器或 Process Explorer,看 qemu 进程里虚拟 CPU 线程和渲染线程各占多少,再看显卡 3D 占用。渲染线程满了,瓶颈在图形这边。

4. 往下挖 ​

  • 主线程在干什么、在等什么:在客体里 simpleperf record -g -t <线程 id> 录二三十秒,看热点和调用链(例如大量 futex 等待)。
  • 卡住或画面冻住:shell su 0 debuggerd -b <pid> 取所有线程的栈,看谁在等谁。
  • 图形往返:渲染线程的时间如果大多耗在客体和宿主之间的同步往返上,就看能不能批量化,或者换掉同步调用。
  • 加载和刚进战斗时最卡:看客体内存换出(swap)和磁盘读写,主线程这时多半在等,不在算。

5. 记结论 ​

写清哪个场景、多少帧、哪条线程满了、改了什么开关,前后对比的数字各是多少。对比要交替多轮跑,看组间差别是不是大过组内波动,只跑一次比一次不算数。

以前查出来的例子 ​

游戏现象查出来的原因结果
阴阳师庭院 44–48 fps;菜单约 44 fps,进出战斗读条卡 1 秒左右庭院:GL 传输走 pipe。菜单和读条:游戏主线程用户态满载,跑的是转译后的 arm64 代码;客体其余 CPU 七成空闲,宿主渲染线程只有两三成GL 传输换成 asg 以后庭院 60 fps。菜单和读条是转译开销,加核没用
星铁战斗 40–48 fps;第一次进战斗那 30 秒只有 7–16 fps战斗:主线程和渲染线程各吃满一个核,受单核速度限制,加到 8 核、加内存都不提高。首战:主线程只有 31%,在等加载和内存换出战斗帧率只能靠转译效率、单核频率或降画质;首战那段要减少换出
终末地跑任务时 12.8 fps;标题画面 14.6 fps跑任务:渲染线程 79% 的时间耗在客体和宿主之间的同步往返上,每帧约 159 次描述符分配。标题:宿主可见的显存(ReBAR)让上传走了慢路径打开 Vulkan 批量描述符更新后 30.5 fps;设 ANDROID_EMU_VK_HIDE_REBAR=1 后标题 60.7 fps

Android 是 Google LLC 的商标。