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的视点,你能够了解的更深入一些。

系列文章目录

  1. Systrace 简介
  2. SR T s jystrace 基础知识 – Systrace 预备知识
  3. Sys– : M v b a 6 & ftrace 基础知识 – Why 60 fps ?
  4. Systrace 基础知识 – SystemServer 解读
  5. Systrace 基础知识 – SurfaceFlinger 解读
  6. Systrace 基础知识 – Input 解读
  7. Systrace 基础知识 – Vsync 解读
  8. SysC Z d N 7tra1 l 6 = @ 8 L $ce 基础知识 – Vsync4 g S % U O `-App :依据 Choreographer 的烘托机制详解c b M d 1 T | r
  9. Systrace 基础知识 – MainThread 和 RenderThread 解读
  10. Systrace 基础知识 – Binder 和锁竞赛解读
  11. Systrace 基础知识 – Triple Buffer 解读
  12. 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 拿去做组成,当然也或许没有

App 端掉帧
App 端掉帧

4 F b $ e以从 Systrace 的 App 端咱们是无法直接判断是否掉帧的,需求从 Systrace 里边的 SurfaceFlinger 端去看

SurfaceFlinger 端判断掉帧

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 oS 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文章食用:

  1. Android 中的卡顿丢帧原因概述 – 办法论
  2. AndK – { | # froid 中的卡顿丢帧原d B U [因概述 – 体系篇
  3. 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 在同一个进程里边

BufferQueue
Buffy c b s [erQueue

其作业逻辑如下

  1. 当出产者(Producer) 需求 Buffer 时,它经过调用 dequeueBuffer()并指定 Buffer 的= r k M 9 ~ 2 z宽度,高度,像素格局b E y w Q * X和运用标志,从 BufferQueue 请求开释 Buffer
  2. 出产者(ProducX w } Xer) 将填充缓冲区,并经过调用 queueBuffer()将缓冲区返回到行列。
  3. 顾客(Cons7 ) Eumer) 运用 acquireBuffer()获取 BuffeB 0 @ x Wr 并消费 Buffer 的内容
  4. 运用? 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 *个大概的了解

  1. Systrace 基础知识 – Vsync 解读
  2. 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),所以上面的流程就能够翻译为

  1. App 需求 Buffer 时,它经过调用[ [ ^ dequeueBuffer()并指定{ ; 3 3 M s z Q Buffer 的宽度,高度,像素格局和运用标志,从 BufferQueue 请求开释 BufT j b J A b = )fer
  2. App 能够用 cpu 进行烘托也能够调用用 gpu 来进行烘托,烘托完结后,经过调用 queueBuffer()将缓冲区返回到 App 对应的 BufferQueue(假如是 gpu 烘托的话,这儿还有个 gpu 处理的进程)
  3. SurfaceFlinger 在收到 Vsync 信号之后,开端预备组成,运用 acquireBuffer()获取 App 对应e d J .的 BufferQueue 中的 Buffer 并进行组成操作
  4. 组成结束后,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 既要用来做组成显现,又要被运用拿去做烘托

Single Buffer
Single Buffer

抱负状况下,单 Buffer 是能够完结任务的(有 Vsync-Offset 存在的状况下)

  1. App 收到 Vsync 信号,获取 Buffer 开端烘托
  2. 距离 Vsync8 0 t ! ;-Offset 时刻后,SurfaceFlingeD N I ur 收到 Vsync 信号,开端组成
  3. 屏幕改写,咱们看[ U H到组成后的画面
Single Buffer 抱负状况
Single Buffer 抱负状况

可是很不幸,抱负状况咱们也就想一想,这期间假如 App 烘托或h 9 X H S K g者 SurfaceFlinger 组成在屏幕显现改写之前还没有完结,那么屏幕改写的时分,拿到的 Buffer 便是不完整的,在用户看来,就有种撕裂的感觉

Single Buffer 非抱负状况
Sic e r N A ] m :ngle Buffer 非抱负状况

当然 Single Buffer 现已没有在运用,上面只是一个比方

Double Buffer

Double Buffer 相当于 BufferQueue 中有两个 Buffer 可供轮C = @ = E !转,顾客在消3 5 s p B费 Buffer 的一同,出产者也能够拿到备用的 Bu3 / N 7ffer 进行出产操作

Double Buffer
Double Buffer

下面咱们来看抱负状况下,Double Buffer 的作业O = yq B O G b b

DoubleBufferPipline_NoJank
DoubleBufferPipline_NoJank

可是 Double Buffer 也会存在功能上的问题,比方下面的状况,App 接连两帧出产都超越 Vsync 周期(准确的说是错失 SurfaceFS b M U | 8linger 的组成机遇) ,就会呈现掉帧状况

Double Buffer
Double Buffer

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)

Triple Buffer
Triple Buffer

下面便是引进 TrJ ` t v Piple Buffer 之后,处理了 Double Buffer 中遇到的由于 Buffer 不足引起的掉帧问题

处理第二个掉帧
处理第二个掉帧

这儿把两个图放到一同来看,方便大家做比照(一个是 Double Buffer 掉帧两次,一个是运用 Triple Buffer 只掉了一帧)

TripleBuffer_VS_DoubleBuffer
Triplej E ^ E i 5 1 rBuffer_VS_DoubleBuffer

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 之后,这种状况就基本上没有了

下降 GPU 和 SurfaceFlinger 瓶颈
下降 GPU 和 Sur v 7 W l k 4 ,rfaceFlinger 瓶颈
下降 GPU 和 SurfaceFlinger 瓶颈
下降 GPU 和 SurfaceFlinger 瓶颈

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

Dumpsys SurfaceFlinger
Dumpsys SurfaceFlinger

封闭 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

参阅

  1. source.android.google.cn/devices/gra…

附件

本文涉及到的附件也上传了,各位下载后解压,运用 Chrome 浏览器翻开即可
点此链接下载文章所涉及到的 Systrace 附件

关于我 && 博客

  1. 关于我 , 十分期望和大家一同交流 , 共同进步 .
  2. 博客内容导航
  3. 优秀9 @ B J | = ~博客文章记载 –a C B 8 Android 功能优化必知必R s ~
Android Performance
Android Performance

一个人能够走的更快 , A ) 9 2 ~ 一群人能够走的更远

本文运用 mdnice 排c K W K {版 , 域名存案中, 假如 com 无法拜访,能够修正成 cn 拜访