Android Systrace 基础知识(11) – Triple Buffer 解读
本文是 Systrace 系列文章的第十一篇,主要是对 Systrace 中的 Triple Buffer 进行简略介绍,简略介绍了如安在 Systrace 中判断卡顿状况的发作,进行初步的定位@ m u + C c和剖析,以及介绍 Triple Buffer 的引进对功能的影响
本系列的目的是经过 Systrace 这个东西,从别的一个视点来看待 An| o G # X ^ / 9 udroid 体系全体的作业,一同也从别的一个视点来对 Framework 进行学习。或许你看了许多讲 F_ L . b * G R xramework 的文章,可是总是记T t ^ s W不住代码,或者不清楚其作业的流程,或许从 Systrace 这个图形化/ z F的视点,你能够了解的更深入一些。
系列文章目录
-
Systrace 简介 -
SR T s jystrace 基础知识 – Systrace 预备知识 -
Sys– : M v b a 6 & ftrace 基础知识 – Why 60 fps ? -
Systrace 基础知识 – SystemServer 解读 -
Systrace 基础知识 – SurfaceFlinger 解读 -
Systrace 基础知识 – Input 解读 -
Systrace 基础知识 – Vsync 解读 -
SysC Z d N 7tra1 l 6 = @ 8 L $ce 基础知识 – Vsync4 g S % U O `-App :依据 Choreographer 的烘托机制详解c b M d 1 T | r -
Systrace 基础知识 – MainThread 和 RenderThread 解读 -
Systrace 基础知识 – Binder 和锁竞赛解读 -
Systrace 基础知识 – Triple Buffer 解读 -
Systrace 基础知识 – CPU Inn ^ l 9 5 | T x qfo 解读
怎样界说掉帧?
Systrace 中能够看到运用的掉帧状况,咱们经常看到说主线程超越 16.6 msU 1 e e 就会掉帧,其实不然,这和咱们这一篇文章讲到的 Triple Buffer 和必定的联络,一般来说,Systrace 中咱们从 App 端和 SurfaceFlinger 端一同来@ e # 1 u判断掉帧状况
App 端判断掉帧
假如之前没有看过 Systrace 的话,仅仅从理论上来说,下面这个 Trace 中的运R _ w @ q _ i用是掉帧了,其主线程的绘制时刻超越了 16.6ms ,但其实n z ` K a U不必定,由于 BufferQueue 和 TripleB] j . p 8 ;uffer 的存在,此刻 BufferQueue 中或许还有上一帧或者上上一帧预备好的 Buffer,能够直接被 SurfaceFlinger 拿去做组成,当然也或许没有
「所4 F b $ e以从 Systrace 的 App 端咱们是无法直接判断是否掉帧的,需求从 Systrace 里边的 SurfaceFlinger 端去看」
SurfaceFlinger 端判断掉帧
SurfaceFlinger 端能够看到 Surfacg { j w eFlinger 主线程和组成状况和运用对应的 BufferQueue 中 Buffer 的状况。如上图,便是一个掉帧的比方。App 没有及时烘托完结,且此刻 Buffc V rerQueue 中也 s 4 z % ; )没有前几帧的 Bd R a Duffer,所以这一帧 SurfaceFlinger 没有组成对应 App 的 Layer,在用户看来这儿就掉了一帧
而在第一张图中咱们说从 App 端无法看出是否掉帧,那张图对应的 SurfaceFlinger 的 TrX A t 2 l ? {ace 如下, 能够看到由于有 Trip# ) & h a V y Gle Buffer 的存F y ! R d % z 4 O在, SF 这儿有之前 App 的 Buffer,所以虽然 App 测一帧超越了 16.6 ms, 可是 SF 这儿依然有可用来组成的 Buffer, 所以没有掉帧
逻# % ; C d辑掉帧
上面的掉帧咱们是从烘托 u r q J 7 d这边来看的,这种掉帧在 Systrace 中能够很简单就k U o发S 9 @ @ T .现;还存在一种掉帧状况叫「逻辑掉帧」
「逻辑掉帧」指的是由于运用自己的代码逻辑问题,导致画面更新的时分,不是以均匀或者物理R ) k * $ D曲线的方式,而是呈现跳动更新的Z % 2 z状况,这种掉帧一般在 Sysy m K ^ ? J v ] Rtrace 上没法看出来,可是用户在运用的时分能够显着感觉到
举一个简略的比方,比方说列表滑动的时分,假如咱们滑动松手后列表的每一帧前进步长是一个均匀改变的曲线,最终趋近于 0,这样便是完美的q ] I;可是假如呈现这一帧比较上一帧走了 20,下一帧比较这一帧走了2 * A 1 n x S k 10,下下一帧比较下一帧走了 30,这W } Z P Z 4 H S种便是跳动更新,在 Syb k P bstrace 上每一帧都是及时烘托且 SurfaceFlinger 都及时组成的,可是用户用起来便是觉得会卡. 不过我列举的这个比方中,Android 现已针对这种状况做了优化,感兴趣的b V M @ L ( E m能够去看一下 android/view/animation/Aw q x D M 0nimationUtils.java 这个类,要点看下面三个办法的运用
public static void lockAnimationClock(c t ^ ] xlong vsyncMillis)
public static void unlockAnimationClock()
public static long currentAnimationTimeMillis()
Android 体系的动画一般不会有这个问题,可是运用开发者就保不齐会写这种代码,比方做动画的时分依据**当时的时刻(而不是 Vsync 到来的时刻)**来计算动画特点改变的状况,这种状况下,一旦呈现掉帧,动画的改变就会变得不^ A U E o –均匀,感兴趣的能够自己考虑一下这一块
别的 Android 呈现掉帧状况的原因十分多,各位能够参阅下面三篇: : P文章食用:
-
Android 中的卡顿丢帧原因概述 – 办法论 -
AndK – { | # froid 中的卡顿丢帧原d B U [因概述 – 体系篇 -
Android 中的卡顿丢帧原因概述 – 运m s | v + c u *用篇
BufferQueue 和 Triple Buffer
Bv ? = T V 3ufferQueue
首要看) . w一下 BufferQueue,BufferQueue 是一个出产者(Producer– i h d a O g |)-顾客(Consumer)模型中的数据结构,一般来说,顾客(CL = L / bonsumer) 创建 BufferQueue,而出产者(Producer) 一般不好 BufferQueue 在同一个进程里边
其作业逻辑如下
-
当出产者(Producer) 需求 Buffer 时,它经过调用 dequeueBuffer()并指定 Buffer 的= r k M 9 ~ 2 z宽度,高度,像素格局b E y w Q * X和运用标志,从 BufferQueue 请求开释 Buffer -
出产者(ProducX w } Xer) 将填充缓冲区,并经过调用 queueBuffer()将缓冲区返回到行列。 -
顾客(Cons7 ) Eumer) 运用 acquireBuffer()获取 BuffeB 0 @ x Wr 并消费 Buffer 的内容 -
运用? w K U完结后,顾客(Consumer)将经过调用 relg 4 k beaseBuffer()将 Buff3 m f K g 8 ~er 返回到行列
Android 经过 Vsyno J V , R ( Ac 机制来控制 Buffer 在 BufferQueue 中的流动机遇,假如对 Vsync 机制不了解,能够参阅下面这两篇文章,看完后你会有T T } W q K P *个大概的了解
-
Systrace 基础知识 – Vsync 解读 -
Android 依据 Choreograph{ v ) B w u der 的烘托机制详解
上面的流程比较抽象,这儿举* F p Z 6一个具体的比方,方便大家了解上面那张图,对后续了解 Systrace 中的 BufferQueue 也会有协助。
在 Android App 的烘托流程里边,App 便a 3 f N f ) t是个` t M出产者(Producer) ,而 SurfaceFlinger 是一个顾n = x d ! f n n客(ConsO { V P – * k ^umer),所以上面的流程就能够翻译为
-
当 「App」 需求 Buffer 时,它经过调用[ [ ^ dequeueBuffer()并指定{ ; 3 3 M s z Q Buffer 的宽度,高度,像素格局和运用标志,从 BufferQueue 请求开释 BufT j b J A b = )fer -
「App」 能够用 cpu 进行烘托也能够调用用 gpu 来进行烘托,烘托完结后,经过调用 queueBuffer()将缓冲区返回到 App 对应的 BufferQueue(假如是 gpu 烘托的话,这儿还有个 gpu 处理的进程) -
「SurfaceFlinger」 在收到 Vsync 信号之后,开端预备组成,运用 acquireBuffer()获取 App 对应e d J .的 BufferQueue 中的 Buffer 并进行组成操作 -
组成结束后,「Surfacei p D oFlinger」 将经过调用 releaseBuffer()将y t R w O ^ 7 q Buffer 返回到 App 对应的 BufferQueue
了解了 BufferQueue 的作用后,接下来来解说一下 Buffer4 s g ) d E pQueue 中的 Buffer
从上面的图能够看到` $ K ~ 8 b ),BufferQueue 中的出产者和顾客经过 dequeueBuffer、queueBuffer、acquireBuffer、E K ; L preleaseBuffer 来申请或者开释 Buffer,那么 BufferQueue 中需求几个 Buffer 来进行作业呢?下面从单. h a 3 7 Buffer,双 Buffer 和 TH _ u B b u .riple Buffer 的视点剖析(留意这儿只是从 Buffer 的视点来做剖析的, 比方 App 测涉及到 Buffer 的是 Rende Z O 1erThread , 不过由于 RenderThread 与 MainThread 有必定的联络, 比方 unBv Q 2 S D hlockUiThread 履行的机遇, MainThread 也会由于 Render0 * SThread 履行慢而被 Block 住)
Single Buffer
单 Buffer 的状况下,由于只有一个 Buffer 可用,那么这个 Buffer 既要用来做组成显现,又要被运用拿去做烘托
抱负状况下,单 Buffer 是能够完结任务的(有 Vsync-Offset 存在的状况下)
-
App 收到 Vsync 信号,获取 Buffer 开端烘托 -
距离 Vsync8 0 t ! ;-Offset 时刻后,SurfaceFlingeD N I ur 收到 Vsync 信号,开端组成 -
屏幕改写,咱们看[ U H到组成后的画面
可是很不幸,抱负状况咱们也就想一想,这期间假如 App 烘托或h 9 X H S K g者 SurfaceFlinger 组成在屏幕显现改写之前还没有完结,那么屏幕改写的时分,拿到的 Buffer 便是不完整的,在用户看来,就有种撕裂的感觉
当然 Single Buffer 现已没有在运用,上面只是一个比方
Double Buffer
Double Buffer 相当于 BufferQueue 中有两个 Buffer 可供轮C = @ = E !转,顾客在消3 5 s p B费 Buffer 的一同,出产者也能够拿到备用的 Bu3 / N 7ffer 进行出产操作
下面咱们来看抱负状况下,Double Buffer 的作业O = y流q B O G b b程
可是 Double Buffer 也会存在功能上的问题,比方下面的状况,App 接连两帧出产都超越 Vsync 周期(准确的说是错失 SurfaceFS b M U | 8linger 的组成机遇) ,就会呈现掉帧状况
Trw ~ # Z K Iiple Buffer
Triple Buffer 中,咱们又加入了一个 BackBuffer ,这样的话 B1 j E { – #ufferQueue 里边就有三个 Buffer 能够轮转了,当 FrontBuffer 在被运用的时分,App 有两个闲暇的 Buffer 能够拿去出产,就算出产进程中x ? J ] $ p有 GPU 超时,CPU 任然能够拿到新的 Buffer 进行出产(「即 Surf5 Y 1 uaceFling 消费 FrontBuffer,GPU 运用一个 BackBuffer,CPU 运用一个 BackBuffer」)
下面便是引进 TrJ ` t v Piple Buffer 之后,处理了 Double Buffer 中遇到的由于 Buffer 不足引起的掉帧问题
这儿把两个图放到一同来看,方便大家做比照(一个是 Double Buffer 掉帧两次,一个是运用 Triple Buffer 只掉了一帧)
Triple Buffer 的作用
缓解掉帧
从上一节 Do4 & } j ! n n & `uble Buffer 和 Tr8 : E ^ / `iple Buffer 的比照图能够看到,在这种状况下(呈现接连主线程超时)H g ~,三个 Buffer 的轮转有助于缓解掉帧呈现的次数(从掉帧两次 -> 只掉帧一次)
所以从第一节怎么界说掉帧这儿咱们就知道,App 主线程超时不必定会导致掉帧,由于 Triple Buffer 的存在,部分 App 端的掉帧(主要} Y R q = @ + k |是由于 GPU 导致), o Q 1 j到 Surd e c vfaceFlinger 这儿k Y K 2 7 8 :未必是掉} 6 帧,这是看 Systrace 的时分需求留意的一个点
削减主线程和烘托线程等待时刻
「双 Buffer 的轮转」, App 主线程有时分必须要等待 SurfaceFlinger(顾客)开释 Buffer 后,才能获取 Buffer 进行出产,这时分就有个问题,现在大部分手机 Surf} Y ) _ ; B 4 O uaceFlingerB & 0 e p 和 App 一同收到 Vsync 信号,假如呈现 Ap[ X Gp 主线程等待j 8 m z SurfaceFlinger(顾客)开释 Buffer ,那么势必会让 App 主线程的履行时刻拖延,比方下面这张图z , c L o D Y,能够/ w )显着看到:「Buffer B 并不是在 Vsync 信号来的时分开端被消费(由于还在[ 4 V x # G运用),而是等 Buffer A 被消费后,Buffer B 被开释,App 才能拿到 Buffer B 进行出产,这期间就有必定的推迟,会让主线程可用的时刻变短」
咱们来看一下在 Systrace 中的上面这种状况发作的时分的体现
而 三个 Buffer 轮转的状况下,则基本不会有这种状s K H { + k ! V 况的发作,烘托线程` * z一般在 dequeueB– I Z v r = ? $uffe$ Y V k S T sr 的时分,t e b .都能够顺畅拿T D , 到可用的 Buffer (当然假如 dequeueBuffer 本身耗时那就不是这儿的讨论范围了)
下降 GPU 和 SurfaceFlinger 瓶颈
这个比较好了解,双 Buffer 的| % 7 | H时分,App 出产的 Buffer 必须要及时拿去让 GPU 进行烘托,然后 SurfaceFlinger 才能进行组成,一旦 GPU 超时,就很简单呈现 SurfaceFlm 2 7 _ o o Q ] 1inger 无法及时组成而导致掉帧
在三个 Buffer 轮转的时分,App 出产的 Buffer 能够及早进入 BufferQueue,让 GPU 去进行烘托(由于不需求等待,就算这儿积累了 2 个 Buffer,下下一帧才去组成,这儿n b P I Z N p也会提早进行,而不是在真实运用之前去E & O匆忙让 GPU 去烘托),别的 SurfaceFlinger 本身的负载假如比较大,三个 Buffer 轮转也会有效下降 dequeueBuffer 的等待时刻
比方下面两张图,便是对应的 SurfaceFlinger 和 App 的「双 Buffer 掉帧」状况,由于 SurfaceFlinger 本身就比较耗时(特定场景),而 App 的 dequeueBuffer 得不到及时的响应,导致发作了比较严重的掉帧状况。在换成 Tripl= E E Ze Buffer 之后,这种状况就基本上没有了
Debug Triple Buffer
Dumpsys SS / n B 4urfaceFlinger
dumpsys SurfaceFlinger 能够检查 Suj @ 6 _ u nrfacH c b B 4eFlinger 输出的很多当时的状况,比方一些功能指标、Buffer 状况、图层信息等,后续有篇幅的话能够独自拿4 J 3 j ; z %出来讲,下面是截取的 Double BuffeI _ / B i E G %r 状况下和 Triple Buffer 状况下的各个 App 的 Buffer 运用状况,能够看到不同的 App,在负载不一样的状况下,u 9 a对 Triple Buffer 的运用率是不一样的;Double Buffer 则彻底运用的是双 Buffer
封闭 Triple Buffer
不同 Android 版别特点设置不一样(这是 Google 的一个逻辑 Bug,Android 10 上面现已修复了)
Android 版别 <= Androz } Aid P
//控制代码
property_get("ro.sf.disable_triple_buffer", value, "1");
mLayerTriple2 D - L T Y QBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripl8 L u n V deBufferingDisabled, "Disabling Triple Buffering");
「修正对应的特点值,然后重启 Framework」
//按次序履行下面的句子(需求 Root 权限)
adb root
adb shell setprJ % o 9 n C E &op ro.sf.disable_triple_buffer 0
adb shell stop && adb shell start
Android 版别 > Android P
//控制代码
property_get("ro.sf.disable_triple_buffe 5 k p c ,erJ q G 4", value, "0");
mLayerTripleBufferingDisabled = aq B ; h P - ( otoi(value);
ALOGI_IF(mLayeO ? _ z ErTripL ` r - @ SleBufferingDisabled, "Disabling Triple Buffering");
「修正对应的特点值,然后重启 Framew0 N # Z S Jork」
//按次序履行下面的句子(需求 Root 权限)
adb root
adb shell setprop ro.sfK + 0 t z.disable_triple_buffer 1
adb shelS S 7 ) Z ) Sl stop && adbu G Q p o D h shell start
参阅
-
source.android.google.cn/devices/gra…
附件
本文涉及到的附件也上传了,各位下载后解压,运用 「Chrome」 浏览器翻开即可
点此链接下载文章所涉及到的 Systrace 附件
关于我 && 博客
-
关于我 , 十分期望和大家一同交流 , 共同进步 . -
博客内容导航 -
优秀9 @ B J | = ~博客文章记载 –a C B 8 Android 功能优化必知必R s ~会
「一个人能够走的更快 , A ) 9 2 ~ 一群人能够走的更远」
本文运用 mdnice 排c K W K {版 , 域名存案中, 假如 com 无法拜访,能够修正成 cn 拜访