原文链接: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的严重异常耗时却是被底层的图形栈拖累的

  1. 我们看到,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严重耗时中的耗时为什么要打引号,为什么要说是被图形层拖累了。

  2. 下图可以看到,刨去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的工况(第三列)来看,情况确实如此。

  3. 既然一口咬定是SF的锅,那就瞧瞧SF。先看SF的INVALIDATE,这没啥好看的,异常case和正常case都是~2.5ms。主要看refresh,正常case ~6.8ms,异常case ~18.8ms。refresh包含SF的合成四件套,包括rebuildLayerStackCalcuateWorkingSetPreparedoComposition

    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,能直接看到的线索是:

  1. 虚拟屏显著耗时,且合成工作通过GLES调用GPU完成
  2. 物理屏合成耗时很小,它通过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通过prepareFrameRenderSurface与Hardware Composer(即HWC)的HIDL服务通信,完成hwc layer的创建。但是,layer能够成功创建不意味着一定支持HWC合成。SurfaceFlinger通过getChangedCompositionTypes向HWC查询不支持HWC合成的Layer。该方法返回的layer如果被标记为CLIENT合成,那么这部分Layer无法由HWC进行合成,而只能通过GPU进行合成——case的VirtualDisplay就是这个情况。

  • 部分layer可能不能由HWC合成的原因(除功耗策略、其他软件策略外):
  1. 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有上限,因此在弹出多个弹窗、叠加过于复杂时,即使界面简单也有可能出现比较明显的卡顿。
  2. 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等三个工具是一系列重要参考。

经过测试,screenrecordscrcpy创建的VirtualDisplay支持HWC合成——这是优化目标。首先看看它们的实现。

编程上,虚拟辅助屏幕采用了与case一模一样的实现——通过创建VirtualDisplay让图形层额外合成一次屏幕到该虚拟屏幕中。虚拟屏幕本质上将画面发送给录屏功能实现,而非进行显示来完成录屏。

通读screenrecord源码,逻辑上,它与虚拟辅助屏、case录屏应用是相同的——VirtualDisplay录屏。但是编程上略有差异:

  1. screenrecord直接通过binder与SurfaceFlinger通信,获取了raw VirtualDisplay,而虚拟屏和case应用均通过SDK提供的MediaProjection包装后的VirtualDisplay。这一块经过源码阅读,确认了经过MediaProjetion包装的VirtualDisplay不会对HWC合成有影响。
  2. VirtualDisplay配置略有差异。case录屏工具自定了一个分辨率和dpi,与物理屏不一致,也和screenrecord不一致。
  3. 用于接收图像数据的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:

  1. 源码中MediaCodec.MediaFormat创建。重点是设置MIME为Video/AVC,并设置颜色空间为OMX_COLOR_FormatAndroidOpaque
format->setString(KEY_MIME, kMimeTypeAvc);
format->setInt32(KEY_COLOR_FORMAT, OMX_COLOR_FormatAndroidOpaque);
  1. 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_FormatSurfaceOMX_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后台录屏干的也不是前台应用干的,不知道这个系统里是不是有什么浮窗什么的干的(不是我没优化好)。

参考

  1. Android图形层垂直同步虚拟VSYNC机制
  2. 显示流水线VSYNC垂直同步

Android性能优化:定性和定位Android图形性能问题——以后台录屏进程为例相关推荐

  1. Android 系统性能优化(30)---Android性能全面分析与优化方案研究

    Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...

  2. linux性能优化实战 倪朋飞,Linux性能优化实战:系统的swap变高(09)

    一.实验环境 1.操作系统 root@openstack:~# lsb_release -a No LSB modules are available. Distributor ID:Ubuntu D ...

  3. C++性能优化(一)——应用程序性能优化简介

    一.程序性能优化简介 1.程序性能优化简介 在计算机发展的早期阶段,硬件资源相对而言是非常昂贵的,CPU运行时间与内存容量给程序开发人员设置了极大限制.因此,早期的程序对运行性能和内存空间占用的要求是 ...

  4. 前台性能和服务器性能是什么,前端性能优化指南[2]--什么是Web性能?

    一个大型网站架构模型如下图所示,对一个网站的性能进行优化,可以分为 Web 前端性能优化.应用服务器端性能优化.存储服务器端性能优化三层.网站的整体性能,需要所有开发者一同来维护. 大型网站架构模型 ...

  5. 前端性能优化:1.什么是前端性能优化

    1.1 性能的起因 人无我有,人有我优,人优我廉,人廉我专 1.2 性能的影响 1.2.1 用户的留存 根据Google营销平台提供的调研发现,如果网站页面加载时间超过3,就会有53%的移动网站的访问 ...

  6. 前端性能优化(二)01-页面性能优化之浏览器——浏览器的主要作用 浏览器的组成结构

    前端性能优化(二)01-页面性能优化之浏览器--浏览器的主要作用 & 浏览器的组成结构 页面性能优化 前端性能优化可以分为两大部分:浏览器部分.代码部分. 浏览器部分又可以分为: 网络层面 浏 ...

  7. Android 如何实现App在后台录屏

    在 Android 中实现 App 在后台录屏主要需要使用到 MediaProjection API. MediaProjection API 是 Android 5.0(API Level 21)引 ...

  8. Android 系统性能优化(34)---Android UI 性能优化

    Android官网 Slow rendering:个人觉得非常有价值,比如指出 对象分配.垃圾回收(GC).线程调度以及Binder调用 是Android系统中常见的卡顿原因,更重要的是给出了定位和解 ...

  9. Android 系统性能优化(52)---移动端性能监控方案Hertz

    移动端性能监控方案Hertz 性能问题是造成App用户流失的罪魁祸首之一.App的性能问题包括崩溃.网络请求错误或超时.响应速度慢.列表滚动卡顿.流量大.耗电等等.而导致App性能低下的原因有很多,除 ...

最新文章

  1. 干货+福利!MySQL常见的面试题+索引原理分析!
  2. 匹配替换指定文本为html标签
  3. session 原理
  4. .NET Framework4.5下载地址和安装
  5. 第五课 路由之初识路由
  6. 苏联当年有多少应该拿菲尔兹奖的数学家被黑了?
  7. 聚类分析(三)Mini Batch KMeans算法
  8. 在 NetBeans IDE 中调试 PHP 源代码
  9. java枚举变量带括号_java枚举类型 - 墨梅的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. JavaScript设计模式-工厂方法模式
  11. Confluence 6 为站点启用匿名用户访问
  12. 绑定事件和解绑事件的方法
  13. 用任何依赖中转服务器的远控软件,打造一款属于自己的远程控制软件(一)
  14. Eclipse配置反编译
  15. stm32 W25QXX系列驱动 W25Q80 W25Q16 W25Q32 W25Q64 W25Q128 W25Q256
  16. JDK 11 下载安装,配置idea
  17. FusionChartsFree的JSP标签开发
  18. Python之爬取《你好,李焕英》电影豆瓣短评
  19. 中台实践:数据中台建设五步法
  20. dataframe.append()合并多个列名相同的文件

热门文章

  1. 大年初一,给大家拜年了
  2. utf-8 GB2312 GBK三者之间的区别
  3. C#联合halcon开发框架源码
  4. 正则表达式学习(三)转义和反义
  5. Bluemsun第三周预习【CSS+选择器+盒模型】
  6. x264和aac编解码器下载地址
  7. 堆栈、队列--Leetcode(python)
  8. NORDIC蓝牙52系列芯片选型与参数对比-NRF52805,NRF52810,NRF52811,NRF52820,NRF52832,NRF52833,NRF52840
  9. Consider defining a bean of type问题解决
  10. K12在线教育行业现状与发展前景分析