获取显存值
安卓上debug显存,除了游戏引擎自带的profiler外,还有更底层的统计方式,用adb获取内存大小,命令为:
adb shell dumpsys meminfo <应用包名>
这可以获取到类似下列的列表:
Applications Memory Usage (in Kilobytes):
Uptime: 1307163772 Realtime: 1442234389
** MEMINFO in pid 26060 [games.guanyou.ca.ymr] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 30104 30024 12 105 30888 48356 45145 3212
Dalvik Heap 1815 1676 36 74 2616 11550 2887 8663
Dalvik Other 1668 1556 56 0 2172
Stack 1892 1892 0 0 1900
Ashmem 136 0 0 0 1020
Gfx dev 392 392 0 0 396
Other dev 19 4 8 0 544
.so mmap 77560 11596 61188 8 111328
.jar mmap 4411 0 2012 0 44044
.apk mmap 441 0 12 0 1812
.ttf mmap 2213 0 384 0 5584
.dex mmap 7177 52 7072 0 7936
.oat mmap 31 0 0 0 2004
.art mmap 7700 5656 1060 43 16472
Other mmap 10389 8 10220 0 11420
EGL mtrack 37000 37000 0 0 37000
GL mtrack 764 764 0 0 764
Unknown 16209 16068 92 1 16596
TOTAL 200152 106688 82152 231 294496 59906 48032 11875
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 8392 19088
Native Heap: 30024 30888
Code: 82316 172756
Stack: 1892 1900
Graphics: 38156 38160
Private Other: 28060
System: 11312
Unknown: 31704
TOTAL PSS: 200152 TOTAL RSS: 294496 TOTAL SWAP PSS: 231
和图形相关的有如下信息:
Gfx dev 392 392 0 0 396
EGL mtrack 37000 37000 0 0 37000
GL mtrack 764 764 0 0 764
Graphics: 38156 38160
上述是在adreno芯片的手机上获取到的信息,而如果是在mali gpu的手机获取,只会获取到如下信息:
GL mtrack 111392 111392 0 0 111392\
Graphics: 111392 111392
观察上述信息,我们能简单得到一些结论: adreno芯片获取的信息中,Gfx dev + EGL mtrack + GL mtrack的结果,恰好是Graphics中的值。
多次在手机运行demo程序中,我发现多次用adb获取上述信息时,adreno手机上,Gfx dev时刻在小范围波动,每次获取都不同;EGL mtrack不会轻易发生变化,GL mtrack和纹理相关,在demo程序中添加纹理,大小会反应在GL mtrack中,且纹理的大小几乎就是GL mtrack的增量(有极小的差异,可能和其他渲染用资源有关)。
复制一个特效,再将其删除,理论上只有shader不会被卸载,其他资源都应该被卸载,反应在上述结果中,GL mtrack再复制前和删除后几乎没有变化,而Gfx dev有很大变化,说明Gfx dev内包含Shader Program的内存。
mali芯片只有GL mtrack并和Graphics值相等,怀疑mali芯片获取的GL mtrack中,包含了Gfx dev + EGL mtrack,不过与时刻变化的adreno芯片Gfx dev不同,mali芯片GL mtrack不会时刻变化,而是当demo重新启动时,每次重启app,这里值都有微小差异。
发现的问题与调查问题
在mali芯片的gpu中,我们播放的一个粒子特效后,内存值显著高于adreno芯片。
在Android Studio的Profiler界面(View->Tool Windows->Profiler)可以查看内存,其中包含Other、Code、Stack、Graphics、Native、Java等项:
对比上文中的adb命令其中的数值,可以判定这些值是用同一个方法拿到的(KB转MB需要除以1024):
。
将检查部分改成Arrange by callstack展开内容,用remaining size排序,首先是看起来是有0.5MB的纹理创建导致的内存未清理,后面还有glBindFramebuffer等申请的内存未释放,这些内存是在libGLES.mali.so,也就是芯片厂商提供的动态库内部申请的。
合理怀疑可不可能是Unity有些申请的纹理为释放,因此用高通设备走同样的流程对比,发现高通的remaining size也就不到400KB,并且也没有纹理相关的堆栈,说明这与Unity无关,是芯片厂dll内部申请的内存未释放。
将同一特效反复加载播放然后删除,mali和adreno设备的曲线有明显区别:
因此几乎可以判定,mali芯片的gles动态库底层有一个池子。
堆栈内存问题
偶然中发现,在mali设备上,Graphics峰值增长100MB时,堆栈才显示分配了不到20MB,而高通设备上,分配大小显著高于Graphics峰值,怀疑mali设备的统计上,至少有一个有问题。
所以我将11张ASTC4x4纹理(10个2048每个5.3MB,1个1024 1.3MB,合计54.3MB)打为一个Bundle,在运行时加载bundle并显示渲染包含这些纹理的Cube,查看堆栈的增长情况:
发现mali设备的堆栈申请大小才不到20MB,而高通上接近80MB,可以断定,mali设备查这里的堆栈是有问题的。
同时分别查看加载和卸载这50MB纹理的情况,在高通设备上,加载和卸载纹理时,Graphics几乎没有变化,而在adreno设备上,Graphics比加载纹理前多25MB。
其他情况
同样的示例打了Vulkan包试了试,Graphics释放不全的问题,在mali vk上同样适用,且对vulkan api的调度,会调度到libvulkan.so,然后再调用到底层的libGLES_mali.so,怀疑mali上vk是封装了层gl?
在adreno上测试,实际api调用是vulkan.adreno.so;结果50多MB的纹理创建,Graphics才增长32MB,且卸载Bundle销毁纹理时,Graphics没有一点下降,不是统计有问题,就是底层有什么机制。