Android性能优化:定性和定位Android图形性能问题——以后台录屏进程为例
原文链接:https://juejin.cn/post/7096288511053004830
作者github:https://github.com/NasdaqGodzilla
- 简介
- 发现、定性与定位
- FPS
- 初步定位问题
- 定性问题
- 跟不上旋律节奏的VSYNC
- 严重异常耗时的dequeueBuffer
- VirtualDisplay合成耗时
- 结论
- 定位问题
- 总结
- 成果展示
- 参考
简介
本文记录一次Android图形性能问题的分析过程——发现、定性和定位图形性能问题,以及探讨的性能优化方案。
环境:Android Q + MTK + ARM Mali-G72。
所分析的性能问题(下称case):打开录屏应用并启动后台录屏,滑动前台应用(滑屏)。性能表现差:CPU、GPU负载显著升高、掉帧、用户明显卡顿感,帧率不足30帧,帧渲染、合成耗时急剧飙升(渲染耗时平均为29ms左右)。
经过优化后,相同环境和条件下,渲染帧率稳定在60帧(提升一倍),渲染耗时平均为8.44ms左右(为优化前的不到三分之一的消耗)。
关键词 Keywords: Screen Recording; Frame rate; FPS; GPU utilization; Jank; MediaProjection; VirtualDisplay; MediaCodec; Perfetto; Inferno; Surface; SurfaceTexture; VSYNC; SurfaceFlinger; HWC; Hardware composer; GPU; OpenGL;
发现、定性与定位
FPS
- 计算FPS的方法和工具
Android框架层通过hwui配合底层完成渲染。该框架本身提供了逐帧渲染分段耗时记录。通过dumpsys gfxinfo可以获取。
io.microshow.screenrecorder/io.microshow.screenrecorder.activity.MainActivity/android.view.ViewRootImpl@6b9b8a9 (visibility=0)Draw Prepare Process Execute3.80 0.48 13.88 1.641.08 0.50 14.29 1.591.12 0.51 17.49 1.741.74 0.17 15.56 1.39
...1.38 0.45 17.64 1.381.56 0.44 10.85 1.383.09 0.24 18.03 1.731.25 0.30 12.43 1.38
使用工具统计帧率与平均耗时(同时打印GPU负载),在开启后台录屏的情况下滑动屏幕,平均渲染耗时高达~29ms,超出16.67ms一倍,导致帧率仅31帧,显著低于60帧。
Average elapsed 28.96 ms
FPS: 31 │ 9.03 0.73 16.76 2.44# GPU负载 LOADING BLOCKING IDLE
70 0 30# case的对比——未开启后台录屏
Average elapsed 9.00 ms
FPS: 60 │ 1.56 0.65 5.62 1.17
- 通过gfx柱状图直观感受性能数据
直观地感受图形渲染性能,除了帧率感受、触控延时外,还可以通过将gfxinfo的分段耗时通过柱状图展示在屏幕上。
这是case性能问题的gfxinfo柱状图,可以看到红柱和绿柱都非常高,远远超越了流畅标准。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-deQKnRrp-1652412137675)(https://cdn.jsdelivr.net/gh/NasdaqGodzilla/PeacePicture/img/优化前(开启录屏)的绘制情况.png)]
其中,绿柱异常放大表明两个Vsync之间耗时显著增长,红柱异常放大表明应用层应用加速使用的DisplayLists大量增长、或图形层使用GLES调用GPU耗时显著增多导致的GPU执行绘制指令耗时变长。
初步定位问题
本节记录初步的分析思路和定位过程。首先我们完成实验(启停后台录屏并滑动屏幕触发渲染)、观测以及记录,拿到了后台录屏启停情况下的FPS、分阶段耗时以及GPU负载(相关数据位于FPS小节)。
开发的工具输出的统计数据计算结果非常直观,一眼可见,后台录屏为Draw阶段带来额外的8倍或8ms耗时,给Process阶段带来额外的2倍或11ms耗时。帧率从60帧坠落到~30帧。
- 耗时分析
可以看到,主要的额外耗时来自Draw和Process。接下来重点围绕着两part定位问题问题。
Stage | Description | Comp |
---|---|---|
Draw | 创建DisplayLists的耗时。Android的View如果支持硬件加速,绘制工作均通过DisplayLists由GPU绘制,可以处理为onDraw的耗时 | 额外8ms或8倍 |
Prepare | 准备 | 没有额外耗时 |
Process | DisplayLists执行耗时。即硬件加速机制下提交给GPU绘制的工作耗时 | 额外11ms或2倍 |
Execute | Framebuffer前后缓冲区flip动作的耗时,上屏耗时 | 额外不到~1ms |
60Hz下,上述4个步骤合计耗时小于16.67ms为正常情况。case为~29ms。主要增量来自Draw和Process。
经过上述初步分析、观测后,接下来的分析可以围绕Draw和Process开展。由于Android Draw部分涉及较广,包含App 渲染线程(DisplayLists)、UI线程(onDraw方法创建DisplayLists),以及图形栈耗时如SurfaceFlinger、RenderEngine等都可能增加Draw耗时。
这里一个技巧可以初步判断耗时来自App进程(渲染线程和UI线程)还是来自图形栈。如果能判断耗时来自App或图形栈,那么可以缩小分析范围、减少分析工作量。上述四大阶段的耗时统计分类比较宽,实际上还有更详细的分阶段耗时,它呈现在前文描述过的gfx统计信息柱状图上。gfx柱状图会以蓝色(RGB(66,122,249))呈现onDraw方法创建和更新DisplayLists的耗时。如果case与正常情况对比后,这部分耗时(蓝柱大小对比)差异很小,即可说明额外的Draw耗时不是来自App的,极可能来自图形栈。Besides,结合过度绘制分析,判断case与正常情况下是否有更多的额外绘制次数可以协同判断。
——根据上述指导思想,排查出了case的额外Draw耗时与App onDraw无关,多出来的DisplayLists来自App以外的进程,可能是图形栈如SurfaceFlinger。
定性问题
本小节介绍问题追踪过程,通过一些方法定位到各阶段的耗时原因,并定性地得出case性能问题的性质。从本小节开始,围绕Perfetto进行分析。这里贴出perfetto的总览,我将关键的信息排序到顶部。前四行分别为SF负责图形的线程、提交到GPU等待完成的工作、Vsync-App、Vsync-sf,最后两行为case中出现卡顿掉帧的App的主线程(UI)和渲染线程(RenderThread)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D2F3G84l-1652412137677)(https://cdn.jsdelivr.net/gh/NasdaqGodzilla/PeacePicture/img/screen_recording_perfetto_sumary.png)]
跟不上旋律节奏的VSYNC
容易看到,Vsync-sf非常不规律。Vsync-sf是触发SurfaceFlinger一次合成工作的基于Hardware VSYNC虚拟出来的一个信号。它相对于真实硬件信号(HW_VSYNC)一个规律的偏移(在case设备上,Vsync-app与Vsync-sf都被配置为8.3ms,即硬件VSYNC到达后,虚拟的Vsync-app和Vsync-sf延时8.3ms后发出,分别触发App绘制、SurfaceFlinger合成。
而case的Vsync-sf交错、残次、不齐、无规律,显然工况不佳。它将导致SurfaceFlinger不能按照预期的时间间隔将合成的帧提交到Framebuffer(经过Flip后,被提交的Framebuffer将上屏成为显示器的下一帧图像),出现掉帧/丢帧。
As we can see,case的VSYNC-sf出现严重的漂移(见图,第二行的VSYNC-sf残次不齐、跟不上规律、难看且混乱),这导致了丢帧。(但VSYNC-sf的失控仅表示与丢帧的相关性,并不直接表明因果性。)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Jl6NxMo-1652412137678)(https://cdn.jsdelivr.net/gh/NasdaqGodzilla/PeacePicture/img/掉帧问题:SurfaceFlinger性能问题导致的VSYNC信号错乱.png)]
- VSYNC-sf为什么会出现偏差?
出于功耗的考虑,VSYNC-sf合VSYNC-app并不是一定会触发的。如果app或sf并没有更新画面的需求,那么死板固定地调度它们进行绘制和合成是不必的。编程上,负责触发VSYNC-sf和VSYNC-app的两个EventThread
会在requestNextVsync
调用后才会将下一个VSYNC-sf或VSYNC-app发出。因此,当(各自EventThread的)requestNextVsync
没有调用时,VSYNC-app和VSYNC-sf也就出现漂移。BufferQueueLayer::onFrameAvailable
会在应用提交后调用,该方法通过调用SF的signalLayerUpdate
触发产生下一个VSYNC-sf
。
换而言之,出于功耗,或别的什么原因(比如耗时导致的延期,人家是线程实现的消息队列),SurfaceFlinger的SFEventThread有可能不调用requestNextVsync
,这将导致Vsync-sf在窗口期内短暂消失——但是也不会出现参差不齐的情况。结合case的VSYNC信号报告来看,VSYNC-sf信号异常切实地提示了性能问题——它的不规律现象表明前后Vsync之间有异常耗时,而非低功耗机制被激活或无屏幕刷新(case性能问题复现时一直在滑前台应用的屏,它每16ms都有画面更新的需求)。
VSYNC-sf虽然出现了偏差,但是它与卡顿问题仅有相关性(或者说它是性能问题的结果),并非因果关系。猜测是其他卡顿问题导致了SF延缓了对VSYNC的request,导致其信号出现漂移。VSYNC-sf信号偏差实质上指导意义重大,因为它能提示我们,问题发生在比App更底层的地方(前文分析的结论),且比SurfaceFlinger提交到Framebuffer更上层的位置(VSYNC-sf用于触发合成,合成完成后提交到屏幕双缓冲区)。
这样,将case性能问题的上下界都确定了,问题分析范围从原先的整个图形栈,有效的缩小到了SurfaceFlinger渲染和合成阶段了。
严重异常耗时的dequeueBuffer
通读Perfetto,可以看到,出了难看的Vsync-sf以外,还可以看到刺眼的超长耗时的draw(App UI线程)以及耗时变态长的dequeueBuffer(App 渲染线程)调用。相对于正常情况,perfetto报告提示的case的draw方法成倍增长的耗时非常容易被误认为耗时“居然来自一开始就排除掉的App进程",这与前文提出的”问题范围“是不能自洽的——它们是相反的结论,肯定哪里不对。仔细分析才能发现,draw方法确实是消耗了更多【墙上】时间(但是不意味着消耗了更多CPU时间,因为等待过程是sleep的),但是draw方法是因为等待渲染线程的dequeueBuffer
造成的耗时,而dequeueBuffer
的严重异常耗时却是被底层的图形栈拖累的。
我们看到,draw严重耗时,渲染线程
dequeueBuffer
消耗掉~20ms的时间。As we all known,Android的Graphics buffer是生产者消费者模型,当作为消费者的SF来不及处理buffer并释放,渲染线程也就需要额外耗时等待buffer就绪。上面还有一段"Waiting GPU Completion"的trace没有贴上来(下图),这段耗时比不开启后台录屏的case下高得多(3ms对比15ms),说明了一定的GPU性能问题或SF的性能问题,甚至有可能是Display有问题(HWC release耗时过长也会导致SF释放buf、生产者渲染线程dequeueBuffer额外等待)。这里的机制比较复杂,不熟悉底层Graphics buffer的流水线模型就不好理解。In one world,
dequeueBuffer
申请的buffer不是凭空new出来的,而是在App-SurfaceFlinger-Framebuffer这一流水线中循环使用的。流水线中的buffer不是无限的,而是有穷的几个。当底层的伙计,如SF和HWC,使用了buffer但是没有来得及释放时(它们的工作没做完之前不会释放buffer),流水线(可以理解成头尾相接的单向队列(ring buffer))没有可用的buffer,此时dequeueBuffer
就不得不进入等待,出现【耗时看上去很长】的问题。实际上,dequeueBuffer
耗时的唯一原因几乎仅仅只有一个:底层消费太慢了,流水线没有剩余buffer,因此需要等待。这个模型抽象理解非常简单。下图,右边消费者是底层图形栈——它每消费完一个buffer就会释放掉,每释放一个buffer应用层能用的buffer就加1。左边生产者是App渲染线程——它调用
dequeueBuffer
申请一个buffer以将它的画面绘制到这个buffer上。buffer送入BufferQueue后由右边的消费者(图形栈)进行消费(合成、上屏显示),然后释放buffer。当图形栈来不及release buffer时,dequeueBuffer
的调用者(App渲染线程)将由于无可用buffer,就必须挂起等待了,在perfetto上就留下长长的一段”耗时“(实际上是墙上时间,大部分都没有占用CPU)。以上,这就是为什么说App渲染线程
dequeueBuffer
严重耗时中的耗时为什么要打引号,为什么要说是被图形层拖累了。下图可以看到,刨去
dequeueBuffer
的严重异常耗时,执行渲染的部分耗时相对于正常的case几乎没有差异,这可以断言渲染线程的惨烈耗时主要就是被dequeueBuffer浪费了。从
GPU Completion
来看,此时GPU正在为SF工作,因为在图中看到(不好意思没有截全,下图你是看不出来的),dequeueBuffer
总是在SF的GPU Completion
结束之后结束的,这就表明SF正在通过GPU消费buffer(调用GPU进行合成后提交,然后标记buffer允许被渲染线程dequeue)。dequeueBuffer
获取到就绪的buffer此时此刻取决于SF的消费能力——因为case中它是短板。(当然图形层的buffer可用不止SurfaceFlinger需要释放,因为SF释放后buffer实质上流转到更底层的HWC,等它将Buffer提交到屏幕后才会释放,这里释放后才能给App再次使用(上面哪个模型图把SF和HWC合并为流水线的图形层buffer消费者)。从perfetto报告看HWC release非常及时、余量充足,SF的GPU Completion则较紧密地接着dequeueBuffer返回,基本断言是SF太慢了——排除HWC的责任。(下图看不出来,当时没有截图到HWC的release情况。)
到这里,除了再次确认排除了前台App的问题外,还可以断言问题来自SurfaceFlinger过分耗时。此外将问题范围的下界从整个SF合成流程(上文的Vsync-sf)缩小到了排除HWC的范围。
结论:渲染耗时一切正常,问题出现在SF消费buffer(合成图形)失速了,导致没有可用的buffer供渲染线程使用。从下图的SF的工况(第三列)来看,情况确实如此。
既然一口咬定是SF的锅,那就瞧瞧SF。先看SF的INVALIDATE,这没啥好看的,异常case和正常case都是~2.5ms。主要看refresh,正常case ~6.8ms,异常case ~18.8ms。refresh包含SF的合成四件套,包括
rebuildLayerStack
、CalcuateWorkingSet
、Prepare
、doComposition
。
Perfetto报告直接表明,case的后台录屏导致的额外一次合成和配套工作是主要的耗时增量。之所以会执行两次合成,是因为后台录屏工具编程上通过Android SDK提供的
MediaProjection
配合VirtualDisplay
实现一个虚拟的镜像的屏幕。SurfaceFlinger会将画面输送一份到这个虚拟的Display以实现屏幕图像传送到录屏工具,虚拟的屏幕要求额外的一次合成。从上图可以直接得出结论,case带来的额外工作消耗就是对该录屏用的VirtualDisplay
的合成工作(doComposition)带来的。
VirtualDisplay合成耗时
由于问题范围已经缩小到了很小的一个范围,在SurfaceFlinger的Refresh过程中,case相对正常应用有巨大的差异耗时,几乎完全来自于对VirtualDisplay
的合成耗时(doComposition)。同时也可以看到,两次合成(一次是设备的物理屏幕,一次是case的后台录屏工具创建的虚拟屏幕)中,虚拟屏幕的耗时远远高于物理屏幕(4倍以上)。
通过查看ATRACE的tag(上图,Perfetto中SurfaceFlinger中主线程的各个trace point都是用ATRACE打的tag),结合dumpsys SurfaceFlinger,能直接看到的线索是:
- 虚拟屏显著耗时,且合成工作通过GLES调用GPU完成
- 物理屏合成耗时很小,它通过HWC合成
结合图中提示的trace tag、耗时,可以得出结论,使用GPU合成的虚拟屏中因GPU合成耗时很长,导致它显著高于物理屏HWC合成耗时。如果GPU合成能够和HWC合成一样快,或者干脆让虚拟屏也使用HWC合成,那么可以预期SurfaceFlinger的合成工作的消耗将显著降低。
结论
本小节综合上述三个小节的分析,对节”定性问题“下一个结论。
耗时的本质已经被看透,录屏工具申请创建的VirtualDisplay
没有通过HWC进行合成,而是通过GPU进行合成,它耗时很长导致界面卡顿。In one word,case使用的VirtualDisplay
的合成方式不够高效。
HWC是Hardward Composer。它接收图形数据,类似于往桌面(真的桌面,不是电脑和手机的桌面)上面叠放照片和纸张——即合成过程。这个工作能将界面上几个窗口叠加在一起后送到屏幕上显示。通过GLES调动GPU也能干这活,不过HWC执行合成的动作是纯硬件的——它很快,比GPU快几倍。
定位问题
前面虽然定性了问题原因是合成方式不够高效,但是没有得出其中的原理——为什么虚拟屏不使用高效的HWC进行合成。本节通过介绍HWC的原理、SurfaceFlinger控制合成方式、虚拟屏Surface特性等来介绍图形栈中合成方式的处理模式。掌握了相关管理后,探讨一些尽量通用的共性的解决方案实现性能优化。最后着重介绍多套优化方案中的一种直面根本原因的解决方法——MediaCodec.MediaFormat创建的支持HWC合成的Surface方案。
- SurfaceFlinger如何决定使用HWC还是GPU合成?
SurfaceFlinger合成主要可以依靠两条路径。其中之一是”纯硬“的HWC合成(在dumpsys SurfaceFlinger中可以看到Composition type为DEVICE),另一个是通过OpenGL让GPU进行合成(Composition type为CLIENT)。
除非是功耗上的设计,否则SurfaceFlinger总是会优先检查本次合成是否支持使用HWC。编程上,在合成阶段之一的prepare过程中,SurfaceFlinger通过prepareFrame
在RenderSurface
与Hardware Composer(即HWC)的HIDL服务通信,完成hwc layer的创建。但是,layer能够成功创建不意味着一定支持HWC合成。SurfaceFlinger通过getChangedCompositionTypes
向HWC查询不支持HWC合成的Layer。该方法返回的layer如果被标记为CLIENT合成,那么这部分Layer无法由HWC进行合成,而只能通过GPU进行合成——case的VirtualDisplay
就是这个情况。
- 部分layer可能不能由HWC合成的原因(除功耗策略、其他软件策略外):
- HWC layer达到上限
Hardware Composer支持的layer数量是有限的。查阅公开资料可知,HWC合成动作属于硬件提供的能力,它们的合成能力受到硬件本身的限制。Google官方资料对Android设备的要求是,HWC最少应该支持4个Layer,分别用于一个常规页面上最常见的4个层:壁纸、状态栏、导航栏和应用窗口。
在case设备中,经过测试,该平台的HWC最多支持7个能进行HWC合成的layer,从第8个layer开始,完完全全只能使用CLIENT合成亦即SurfaceFlinger调用RenderEngine通过OpenGL调动GPU进行合成。
正是由于HWC合成layer有上限,因此在弹出多个弹窗、叠加过于复杂时,即使界面简单也有可能出现比较明显的卡顿。 - VirtualDisplay的Surface格式不受HWC支持
HWC的硬件合成能力对buffer(Surface封装)内保存的图像的格式有要求。比如,HWC不能处理缩放,仅支持一部分的格式,大多数都还有其他因素会导致不支持,如旋转、部分Alpha等等。In one word,图像格式的数量是远远多于HWC支持的类型数的。当HWC碰到不支持合成的Surface时,就会在前文提过的getChangedCompositionTypes
中通知SurfaceFlinger,由SurfaceFlinger转为使用GPU合成。
结合上述几种情况,设计实验验证。其中通过在物理屏上弹窗来增加Layer以获取HWC Layer上限。确认case无法使用HWC合成不是Layer上限导致的问题后,通过对比来验证Surface格式问题。Surface是对native层的buffer的封装,其类型广泛、实现复杂,一个一个试是不现实的。通过对比性能强劲的类似实现可以一探究竟。Android adb提供一个出厂自带的录屏命令screenrecord
、用于测试双屏显示功能的虚拟辅助屏幕(开发者模式-模拟辅助屏)、著名远程窥屏工具scrcpy
等三个工具是一系列重要参考。
经过测试,
screenrecord
和scrcpy
创建的VirtualDisplay
支持HWC合成——这是优化目标。首先看看它们的实现。
编程上,虚拟辅助屏幕采用了与case一模一样的实现——通过创建VirtualDisplay
让图形层额外合成一次屏幕到该虚拟屏幕中。虚拟屏幕本质上将画面发送给录屏功能实现,而非进行显示来完成录屏。
通读screenrecord
源码,逻辑上,它与虚拟辅助屏、case录屏应用是相同的——VirtualDisplay
录屏。但是编程上略有差异:
screenrecord
直接通过binder与SurfaceFlinger通信,获取了rawVirtualDisplay
,而虚拟屏和case应用均通过SDK提供的MediaProjection
包装后的VirtualDisplay
。这一块经过源码阅读,确认了经过MediaProjetion
包装的VirtualDisplay
不会对HWC合成有影响。VirtualDisplay
配置略有差异。case录屏工具自定了一个分辨率和dpi,与物理屏不一致,也和screenrecord
不一致。- 用于接收图像数据的
Surface
不一致。screenrecord
通过MediaCodec
创建了指定格式的Surface,类型是OMX库中定义的OMX_COLOR_FormatAndroidOpaque
。
可能影响HWC合成的最大可能来自第3点的Surface
格式。做出这个假设的逻辑是,HWC是否有合成能力是由Layer的格式来决定的(Layer是图形层对Graphics Buffer的抽象,Surface是应用层对Layer的再一次抽象,在分析问题时,可以将它们的底层视为一致的,即都是在各自的层指代Graphics Buffer)。
因此,编写demo,模仿screenrecord
创建Surface的方式screenrecord::prepareEncoder:
- 源码中
MediaCodec.MediaFormat
创建。重点是设置MIME为Video/AVC
,并设置颜色空间为OMX_COLOR_FormatAndroidOpaque
。
format->setString(KEY_MIME, kMimeTypeAvc);
format->setInt32(KEY_COLOR_FORMAT, OMX_COLOR_FormatAndroidOpaque);
MediaCodec
创建后通过getInputSurface
获取创建的Surface
,传递到VirtualDisplay
即可接收到VirtualDisplay
上显示的内容——即获取到录屏内容。这一段是常规操作,起HWC合成的决定性作用的是第1步中对MIME类型的设置、对颜色空间的设置。
我们的case是应用层App,因此用Java代码简单表示一下:
final MediaFormat f = MediaFormat.createVideoFormat("Video/AVC", SCREEN_WIDTH, SCREEN_HEIGHT);
f.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
// 其他的MediaCodec的常规设置,如I帧、码率帧率就不再赘述
final MediaCodec c = MediaCodec.createEncoderByType("Video/AVC");
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
// 录屏功能基础操作,这个surface的格式就是前面设置的COLOR_FormatSurface,它作为MediaCodec的输入流进行编码,稍后,它作为VirtualDisplay的参数,传递到VirtualDisplay接收显示数据
final Surface sf = codec.createInputSurface();
codec.start();
mScreenRecorder.setupVirtualDisplay(sf, SCREEN_WIDTH, SCREEN_HEIGHT);
Java层使用
COLOR_FormatSurface
,它等同于OMX的OMX_COLOR_FormatAndroidOpaque
。
demo如上。简单说明下,核心操作是前两行设置格式。其他的如创建Codec、传递Surface到VirtualDisplay均为常规录屏应用的”死“代码(任谁来写都是这么写的,也是case应用的本来就实现的代码)。case应用之前通过SurfaceTexture
创建Surface
,它仅支持GPU合成(SurfaceTexture
的特性)。
运行该demo,通过dumpsys SurfaceFlinger查看Virtual Display的各个Layer的合成类型,可以看到从原来的CLIENT类型变成了DEVICE类型。用相同的环境、手法测试,发现帧率按照预期从30帧恢复到了60帧! 表明我们的方案取得了彻底的成功——通过这个解决方案,不仅验证了HWC合成受到Surface的格式(底层的Graphics Buffer)的限制,还验证了DEVICE合成的性能优化可行性。
总结
SurfaceFlinger
合成中没有利用上HWC可能导致卡顿掉帧。而HWC合成条件比较严格(跟硬件平台本身有关、系统底层实现有关)。应用层尽量使用契合HWC条件的Surface能够尽可能保证使用HWC进行合成——将Surface设置为COLOR_FormatSurface
(OMX_COLOR_FormatAndroidOpaque
)。
成果展示
- 优化后的效果
Average elapsed 8.60 ms
1.45 0.56 5.13 1.46
- 优化后看看Perfetto
验证是HWC合成
通过dumpsys SurfaceFlinger可以看到,case创建的虚拟屏从CLIENT合成(GPU合成)转成了DEVICE合成(HWC合成)。通过gfx柱状图非常直观,对比文章开头的一幅图,优化效果极其显著。
注意,屏幕最右边有几个高耸如云的柱子,那个不是case后台录屏干的也不是前台应用干的,不知道这个系统里是不是有什么浮窗什么的干的(不是我没优化好)。
参考
- Android图形层垂直同步虚拟VSYNC机制
- 显示流水线VSYNC垂直同步
Android性能优化:定性和定位Android图形性能问题——以后台录屏进程为例相关推荐
- Android 系统性能优化(30)---Android性能全面分析与优化方案研究
Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...
- linux性能优化实战 倪朋飞,Linux性能优化实战:系统的swap变高(09)
一.实验环境 1.操作系统 root@openstack:~# lsb_release -a No LSB modules are available. Distributor ID:Ubuntu D ...
- C++性能优化(一)——应用程序性能优化简介
一.程序性能优化简介 1.程序性能优化简介 在计算机发展的早期阶段,硬件资源相对而言是非常昂贵的,CPU运行时间与内存容量给程序开发人员设置了极大限制.因此,早期的程序对运行性能和内存空间占用的要求是 ...
- 前台性能和服务器性能是什么,前端性能优化指南[2]--什么是Web性能?
一个大型网站架构模型如下图所示,对一个网站的性能进行优化,可以分为 Web 前端性能优化.应用服务器端性能优化.存储服务器端性能优化三层.网站的整体性能,需要所有开发者一同来维护. 大型网站架构模型 ...
- 前端性能优化:1.什么是前端性能优化
1.1 性能的起因 人无我有,人有我优,人优我廉,人廉我专 1.2 性能的影响 1.2.1 用户的留存 根据Google营销平台提供的调研发现,如果网站页面加载时间超过3,就会有53%的移动网站的访问 ...
- 前端性能优化(二)01-页面性能优化之浏览器——浏览器的主要作用 浏览器的组成结构
前端性能优化(二)01-页面性能优化之浏览器--浏览器的主要作用 & 浏览器的组成结构 页面性能优化 前端性能优化可以分为两大部分:浏览器部分.代码部分. 浏览器部分又可以分为: 网络层面 浏 ...
- Android 如何实现App在后台录屏
在 Android 中实现 App 在后台录屏主要需要使用到 MediaProjection API. MediaProjection API 是 Android 5.0(API Level 21)引 ...
- Android 系统性能优化(34)---Android UI 性能优化
Android官网 Slow rendering:个人觉得非常有价值,比如指出 对象分配.垃圾回收(GC).线程调度以及Binder调用 是Android系统中常见的卡顿原因,更重要的是给出了定位和解 ...
- Android 系统性能优化(52)---移动端性能监控方案Hertz
移动端性能监控方案Hertz 性能问题是造成App用户流失的罪魁祸首之一.App的性能问题包括崩溃.网络请求错误或超时.响应速度慢.列表滚动卡顿.流量大.耗电等等.而导致App性能低下的原因有很多,除 ...
最新文章
- 干货+福利!MySQL常见的面试题+索引原理分析!
- 匹配替换指定文本为html标签
- session 原理
- .NET Framework4.5下载地址和安装
- 第五课 路由之初识路由
- 苏联当年有多少应该拿菲尔兹奖的数学家被黑了?
- 聚类分析(三)Mini Batch KMeans算法
- 在 NetBeans IDE 中调试 PHP 源代码
- java枚举变量带括号_java枚举类型 - 墨梅的个人空间 - OSCHINA - 中文开源技术交流社区...
- JavaScript设计模式-工厂方法模式
- Confluence 6 为站点启用匿名用户访问
- 绑定事件和解绑事件的方法
- 用任何依赖中转服务器的远控软件,打造一款属于自己的远程控制软件(一)
- Eclipse配置反编译
- stm32 W25QXX系列驱动 W25Q80 W25Q16 W25Q32 W25Q64 W25Q128 W25Q256
- JDK 11 下载安装,配置idea
- FusionChartsFree的JSP标签开发
- Python之爬取《你好,李焕英》电影豆瓣短评
- 中台实践:数据中台建设五步法
- dataframe.append()合并多个列名相同的文件
热门文章
- 大年初一,给大家拜年了
- utf-8 GB2312 GBK三者之间的区别
- C#联合halcon开发框架源码
- 正则表达式学习(三)转义和反义
- Bluemsun第三周预习【CSS+选择器+盒模型】
- x264和aac编解码器下载地址
- 堆栈、队列--Leetcode(python)
- NORDIC蓝牙52系列芯片选型与参数对比-NRF52805,NRF52810,NRF52811,NRF52820,NRF52832,NRF52833,NRF52840
- Consider defining a bean of type问题解决
- K12在线教育行业现状与发展前景分析