骁龙 8 Gen 2 掌机跑 Switch 模拟器: 内存布局越大越不稳, 以及显存优化实测

我的 AYN Thor 掌机用的是骁龙 8 Gen 2, 平时靠 Eden 玩 Switch。有一段时间《王国之泪》总是"退后台"或者干脆闪退, 稳定版和 nightly 两个构建都一样。我第一反应是内存不够——掌机有 15GB 内存, 那 Eden 的内存布局就该往大了给, 于是我把它从默认调到最高的 8GB。结果问题变得更频繁了。

这篇就是从这个反直觉的结果往回查的过程。

退后台和闪退, 是两个独立的问题

一开始我把两个现象当成一件事, 后来看日志才发现根因完全不同。判断办法很简单: 看 Eden 进程还在不在。退后台的时候进程活着, 只是它的 EmulationActivity 被停掉了; 闪退的时候进程直接没了, 留下 tombstone。

分开之后, 每个问题都有了明确的证据链。

退后台: lmkd 杀掉的是桌面, 不是游戏

logcat 里反复出现同一组行:

lowmemorykiller: Kill 'com.iisulauncher' (15071) ... reason: critical pressure and device is low on memory
lowmemorykiller: Kill 'com.iisulauncher' (16254) ... reason: critical pressure
lowmemorykiller: Kill 'com.iisulauncher' (16271) ... reason: critical pressure

被杀的是前端桌面 iiSU, 而且是一串不断变化的新 pid。iiSU 的 SecondaryHomeActivity 被系统反复重新拉起, 每次拉起就把 Eden 的 EmulationActivity 停掉(wm_stop_activity ... STOP_ACTIVITY_ITEM)。表面看是"游戏退后台、桌面回到前台", 实际上 Eden 进程一直没死。这也解释了为什么换 nightly 也没用——它和前端抢内存的关系没变。

真正要解决的不是游戏, 是内存压力, 而压力的来源正是我把内存布局调到了 8GB。

为什么内存布局越大反而越不稳

Eden 的 memory_layout_mode 有三个档: 0 是 4GB, 1 是 6GB, 2 是 8GB, 对应的模拟器给"客机"(被模拟的 Switch)分配的内存上限。Switch 原生就是 4GB, 所以直觉上给得越多越宽裕。问题在于这台机器的内存不只给 Eden: Eden 进程实测 PSS 约 9.2GB, 其中 GPU 的共享内存(GL mtrack, 也是显存)常占 3.3GB, 再加大约 3.4GB 的 Other mmap(客机 4GB 映射)和 1.9GB 的 Native heap。8GB 布局下, 客机内存 + 显存 + Android 系统本身一起把整机推到临界。

关键认知是 lmkd 击杀的判断依据: 它看的是内存压力(PSI / thrashing), 不是"还剩多少可用内存"。所以即使面板上显示还占着 11–12GB"看着够用", 只要系统进入 critical pressure, lmkd 照样杀。把布局从 8GB 降到 6GB, 问题立刻缓解; 最后统一设回 4GB(Switch 原生容量), 再没出现桌面被杀。这个结论很反直觉: 多给的那 4GB 没有让游戏更流畅, 反而挤出了系统维持前台所需要的余量。

闪退: 着色器缓存是驱动专属的

另一个问题才是真的崩溃。tombstone 两次都停在同一个点:

SIGSEGV  vulkan.adreno.so  qglinternal::vkCreateGraphicsPipelines(...)
   ← Vulkan::vk::Device::CreateGraphicsPipeline ← GraphicsPipeline::MakePipeline
另一次:  libllvm-qgl.so CreateQGLCShader(...) ← vulkan.adreno.so

同时崩溃会话里报告的驱动是 Qualcomm Technologies Inc. Adreno Vulkan Driver 512.676.53, 而正常会话用的是 PurpleVK public driver 26.0.99, 也就是 Turnip。根因是 Eden 的 files/cache/shader/<titleid>/ 下的管线缓存是驱动专属的, 切换或回退驱动之后, 旧缓存和新驱动不匹配, 就会在创建图形管线时崩掉。《王国之泪》的这份缓存有 53MB, 旁边的 vulkan.bin 有 117MB, 都不小。删掉 cache/shader/0100f2c0115b6000/ 让缓存重建, 并确认 GPU 驱动选回 Turnip, 闪退就停了。

顺带还发现一个隐蔽点: 配置文件里 [GpuDriver] driver_path 明明指向 T30, 实际却加载了 Adreno——也就是自定义驱动没真正生效。光看设置项会以为已经用了 Turnip, 得从崩溃日志里报告的驱动名去反查。

把王国之泪跑稳: 驱动与显存的取舍

清掉缓存、驱动切回 Turnip 之后, 崩溃消失, 但《王国之泪》在这颗 SoC 上本来就吃力, 实测帧率大约 25–29 FPS, 瓶颈在 GPU: 8GB 布局时 GPU 被锁在 680MHz、占用 80% 左右震荡, 而 CPU 只有约 50% 不饱和。设备性能模式也有讲究——“性能模式"会锁频到 680MHz 再触发发热降频, 帧率反而抖; 切成"正常/均衡”(performance_mode=0)让频率按需升降, 帧 pacing 更稳。

显存是更紧的资源。给《王国之泪》单独开 ASTC 重压缩(config/custom/<TitleID>.ini 里 astc_recompression\use_global=false 加 astc_recompression=1)之后, GL mtrack 从 3.32GB 降到 2.07GB, Eden 的 PSS 从约 9.2GB 降到 7.82GB, 系统可用内存从 3.6GB 回到 5.0GB, 帧率维持在带插帧显示的约 60FPS(内部真实约 30, 抖动标准差 4–5ms)。代价是解压时机换了位置, 换来的是显存余量。顺带一提, 王国之泪从 1.2.1 升到 1.4.2 后显存占用比 1.2.1 更大, 所以升级后必须配合 4GB 内存布局才稳。

不开 UI 也能拿到性能数据

排这些问题的过程中, 大部分数据我都是用 adb 直接读的, 不依赖模拟器的悬浮显示:

# 帧率: 找到游戏 SurfaceView 层名后用 --latency 算 actualPresent 时间戳
adb shell "dumpsys SurfaceFlinger --list | grep 'SurfaceView.*EmulationActivity'"
adb shell "dumpsys SurfaceFlinger --latency '<该层名>'"

# GPU: 占用率与当前/最大频率
adb shell "cat /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage"
adb shell "cat /sys/class/kgsl/kgsl-3d0/gpuclk /sys/class/kgsl/kgsl-3d0/max_gpuclk"

# CPU 各核当前频率
adb shell "for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq; do cat $c; done"

# 内存: 看 GL mtrack(显存)与 Native Heap
adb shell "dumpsys meminfo <eden_pid>"

# 设备性能模式(0=正常/均衡)
adb shell "settings get system performance_mode"

有了这些, “帧率不稳到底卡在哪"就不再是猜: GPU 占用接近满、CPU 空着, 就是 GPU 瓶颈; 帧时间标准差大, 往往是降频或内存压力, 而不是 CPU 算不动。

两个更隐蔽的坑

查内存时还顺手确认了两个坑。一个是游戏打不开: 某些游戏专属配置里 speed_limit=0, 而内核计时的 speed_scale = 100.f / SpeedLimit() 会在这里除以零, 直接把游戏打崩, 把该项改成 100 就好(grep -l '^speed_limit=0$' config/custom/*.ini 能一次性找出来)。另一个是异步着色器, 它能减少新特效的顿卡, 但曾在 vulkan.adreno.so ← Vulkan::Scheduler::WorkerThread 的调用栈上崩过, 属于稳定性换流畅度的取舍, 是否开启要看具体游戏。

回头看, 这台掌机跑 Switch 的瓶颈几乎都不在"算力够不够”, 而在内存和显存怎么分配。把客机内存从 8GB 降回 4GB 反而更稳, 是我这次最想留下的一条结论。

文章链接:

https://time-friend.com/zh/archive/switch-emulation-snapdragon-tuning/

# 相关文章推荐