GPU负载分析
GPU负载抓取
参考CPU/GPU负载—抓取—分析文档 ,用snapdragon profiler进行抓取
GPU负载分析
分析工具
snapdragon profiler
两个浅紫色flush Markers之间就为一次flush的结果,大多数情况下,我们可以粗略的认为两次flush之间为一帧,这里的surface对应duration就是这一帧抓取进程process的GPU耗时,一般可以用surface的耗时来衡量本次GPU性能。

两个flush markers间,可能存在多个surface

Renderdoc
下载&编译
💡 下载地址 官方版本: https://github.com/baldurk/renderdoc 小米版本: https://git.n.xiaomi.com/dongjunjun1/renderdocproject 建议使用小米版本,官方版本无法抓取systemui、home等系统APP,并且不支持external texture,小米版本找@董军军开通下载权限
💡 编译方式:参考官方renderdoc编译方式:https://github.com/baldurk/renderdoc/blob/v1.x/docs/CONTRIBUTING/Compiling.md windows编译: Windows下编译小米版本RenderDoc ubuntu&mac可直接参考这篇: https://blog.csdn.net/goodnight1994/article/details/124843115
💡 原理介绍: renderdoc流程分析 renderdoc 相关
使用方式
可参考:https://blog.csdn.net/chqj_163/article/details/114361661
准备工作
编译后打开renderdoc,主界面如下:

💡 要抓取的应用需要是debugable的,或者需要root权限
💡 目前仅支持gles抓取,因此需要切换成gles: adb shell setprop persist.sys.vk_mode_enabled false adb shell setprop debug.hwui.renderer skiagl
连接应用
- 点击左下角选择连接的手机,会在手机上自动安装RenderDoc的Android App,保持手机常亮。

- 成功连接后会是如图显示

- 选择Launch Application窗口,选择Executable Path,在弹出的窗口中选择你要连接的包名,ok之后点击Launch按钮
❗ 注意!如果是特殊系统应用,如systemui、home、个人助理,Executable Path无法显示包名,直接输入包名即可(需要用小米的renderdoc)



Launch成功后显示如图

截帧
- 首先准备好需要抓帧的场景
- capture x Sequential Frame(s)用来选择抓帧数,直接修改数字即可,目前单次上限是可连续抓100帧,点击Capture Frame 截帧

- 点击后,开始在手机上操作场景,成功后双击截出的帧,等待加载完数据,就可以开始分析了。当然你也可以右键保存到本地,这样方便下次分析。

分析帧
窗口简介
RenderDoc中全部窗口如下:

Event Browser

主要用来查看以及选择DrawCall调用,我们分析的单位也是从DrawCall分析。选中单一的DrawCall,其他窗口也会有对应的改变显示,因此在glDrawElements之间跳动,可以很方便的查看到这次Drawcall具体作用。同时点击1处的时钟图标,会在2处显示调用的消耗时间。
Texture Viewer

主要用来查看纹理显示,我们可以用它来查看在Event Browaser中选中的DrawCall或者Pass的输入和输出纹理。 2中是输入纹理,3中是输出纹理,在其中选中一个纹理,可以在4中放大显示。点击1按钮可以保存纹理。

在纹理小窗口中右键弹出一些选项,可以看到这个纹理在哪些DrawCall中使用,点击可以Event Browaser中选中对应的DrawCall。 例如我在一次截帧中顺序选中两个DrawCall。


对比发现第二个DrawCall画的是妙播的左按钮。
Pipeline State
主要用来查看渲染管线过程,是一个使用很频繁的窗口。(下面以控制中心的圆角背景举例)
- VTX(Vertex Input)

VTX窗口主要用来查看输入的顶点(包括格式,大小等等)。
- VS(Vertex Shader) 这个就是顶点着色器

因为基本的上不在顶点着色器中做采样操作,所以这里的纹理和采样器是空的,但是因为后面的PS(像素着色器)和这里基本上是一样的显示和操作,我就点击Show Disabled Items(下图中1处按钮)强行显示出来这个ShaderProgram使用到的纹理和采样器(一般情况下不需要开这个),PS处就不再细介绍了。

2处显示的是ShaderProgramma,可以看到Shader的名字,点击会调到Resource界面,可以看到更详细的内容,后面再介绍。点击View按钮可以看到具体的shader代码,如图。

分别是VShader的代码和VS的输入和输出。在Mesh View 能看到更详细的Mesh数据,这个后面再介绍。
3处是VS用到的纹理,可以看到纹理类型,大小,格式等。点击箭头可以在Texture View看到纹理显示,点击资源按钮可以在Resource Inspector中查看。一般来说箭头按钮表示有更详细的信息,资源按钮表示在Resource Inspector有更多的信息。4处是采样器,可以看到采样纹理的环绕方式(Addressing),过滤方式(filter)等。5处是VS用到的一些Uniform变量,点击箭头可以看到具体的变量和内容。

- FS(Fragment Shader)(Pixel Shader) 像素着色器
操作显示和VS差不多

- FB(FrameBuffer)

这个窗口主要关注Alpha混合(是否开启,以及混合模式等),Z-Test(深度测试是否开启,测试函数等等),Stencil Test(模板测试)
Mesh Viewer
这个窗口主要是用来查看VertexShader中输入输出的顶点数据,一般关注输出的就行。

2中的就是输入的顶点数据,3中是输出的顶点数据,4是你输入的Mesh预览,在2中选中任意一个顶点在4中都会以蓝色高亮显示,并且以红色高亮显示当前顶点所在的三角形。- 在
2中选中任意以后,点击1出按钮,可以导出Mesh数据,一般导出成CSV格式,可以根据顶点数据自己生成模型去做测试。
API Inspector
主要用来查看该条drawcall一些切换渲染状态的API调用。

展开后,能看到具体的资源,点击可以跳转到Resource Inspector查看。
Timeline
查看这一帧的时间轴

小旗子表示当前选中的DrawCall。 当我们在TextureViewer中选中一张纹理,时间轴下面会有一些不同颜色的小三角形。
表示这张纹理在这一次DrawCall被读,写,读写,或者清除。

Resource Inspector
查看这一帧中所有的资源。

- 在
1中可以查看到所有引用到这一个资源的的EID,点击可以跳转到Event Brower中选中。 2中是初始化这个资源调用的一些OpenGL API,以及调用API传递的参数。(注意这里只是初始化参数,后面改了的话这里也不会改,例如Shader传了不同的Uniform参数,这里看到的也只是第一次的,所以要看真正的值还是要在在PIpeline State中查看。)3中是所有的资源列表。
分析方法
overdraw过度绘制
💡 参阅资料: https://developer.android.com/topic/performance/rendering/inspect-gpu-rendering https://developer.android.com/topic/performance/rendering/overdraw
开发者选项选择调试GPU 过度绘制
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
- 真彩色:没有过度绘制
- 蓝色:过度绘制 1 次

- 绿色:过度绘制 2 次

- 粉色:过度绘制 3 次

- 红色:过度绘制 4 次或更多次

💡 通过该功能,已经可以辨别出布局中何处出现了过度绘制,可以采取以下措施来减少甚至消除过度绘制:
- 移除布局中不必要的背景。
- 使视图层次结构扁平化。
- 减少透明度渲染。 部分过度绘制是不可避免的,在优化应用的界面时,应尝试达到大部分显示0-2次过度绘制(蓝色)的视觉效果。
- case1 --- 小部件中心

如上,该界面存在较多的过度绘制,首先可以通过Layout Inspector查看布局是否存在不必要的背景

更进一步,用renderdoc抓帧小部件,查看overdraw来源


可以看到,每个控件绘制了两次阴影,导致了严重overdraw。
💡 小tips: 类似阴影、透明度等不能直观查看其绘制区域的效果,可以在renderdoc里修改shader,比如调成红色来查看其区域,判断会不会相交 方式如下:

找到main函数,修改输出如:


这时候阴影就变成红色了,可以看到其边界
绘制负载优化
- case2 --- 通控切换
renderdoc抓帧,发现通控切换时会多一个renderpass

原因是通知栏条目的容器alpha发生变化,alpha小于1,创建了一个离屏

通过trace也可以看出:

在通控切换时,每帧都会重新渲染该view,因此这种情况下,离屏的开销比正常渲染的开销要大
💡 将视图设置为半透明时,该视图会在屏幕外缓冲区渲染,导致所需的填充率翻倍。 关于Alpha透明度的绘制是比较耗时的,一个主要的原因是Alpha图层绘制需要进行两次绘制:
- 第一次是创建硬件缓冲区,然后绘制不透明的画面
- 第二次是对alpha通道的颜色进行混合,丢弃硬件缓冲区的是不透明画面 参考视频:https://www.youtube.com/watch?v=wIy8g8yNhNk
原因如下图所示,左侧是Android混合优化的效果,右侧是叠加的效果,当存在叠加时,如果不做离屏,多层view叠加渲染会导致非透明度叠加,进而导致靠近底层的绘制越来越模糊,为了解决此类问题,Android需要对视图进行混合,而不是简单的叠加。

如何优化?
- 减少透明度渲染的次数,或减少透明度渲染区域
如最近任务退出时,给全屏的view设置了alpha,用于做退出的透明渐变动画,而如果将alpha改为只对按钮做动画,背景用argb设置颜色,透明导致的成本会大大降低

- 复写hasOverlappingRendering
class MyView extends View {
@Override
public boolean hasOverlappingRendering() {
return false;
}
}对于明确不需要剔除叠加效果的View,即便是存在半透明效果的情况下,直接让其返回false,通知渲染器不需要叠渲染,因此也不会进行混合渲染。这种方式直接告诉渲染器不进行混合,因此不会创建硬件缓冲区。
适用于两种情况:
- View不存在叠加,只有一层View存在color或者drawable的情况
- View存在叠加,但不准确的alpha叠加效果可以接受
绘制性能对比:

- 善用hardwareLayer
上文也提到了,alpha产生的离屏,在通控切换场景下开销比不离屏要大,那什么情况下应该使用离屏?
以demo举例,点击按钮,做一个位移动画


通过renderdoc抓取,每帧都需要对这三个矩形及其阴影进行重绘

使用缓冲进行优化:

通过renderdoc抓帧可以看到,单帧多了一个renderpass,但只会绘制一次,之后的动画流程只需要贴图即可,除非内容重新创建或修改


❗ 1. 别忘记,在动画结束后,通过setLayerType(View.LAYER_TYPE_NONE, null); 关闭优化 2. 假如view设置了clipchildren/clippadding,需要注意设置离屏的区域是否会裁切如阴影等效果,如会裁切需要调整区域,或不能使用 3. 使用hardware layer对内存有一定影响,需要考虑内存增长是否在可接受的预期范围
GPU抢占
测试提供的GPU负载数据中有sdp文件,用snapdragon打开后,观察surface负载高占比,是否为抢占导致

如何分析抢占?
正常app创建时,优先级是默认的,而surfaceflinger和systemui有重新设置优先级,按优先级顺序为:
surfaceflinger>systemui>普通app
- 通过trace查看抢占来源
因此,在遇到抢占时,可以通过trace,查看这几个进程的 GPU Completion ,是否存在叠加态,来粗略判断是谁进行的抢占操作,如:

判断为surfaceflinger gpu合成抢占
遇到抢占导致的负载高问题,需要抢占方和app本身优化负载
- 通过GPU分析工具
