Android Vsync

Android Vsync 原理浅析

Preface

Android中,Client测量和计算布局,SurfaceFlienger(server)用来渲染绘制界面,client和server的是通过匿名共享内存(SharedClient)通信。

每个应用和SurfaceFlienger之间都会创建一个SharedClient,一个SharedClient最多可以创建31个SharedBufferStack,每个surface对应一个SharedBufferStack,也就是一个Window。也就意味着,每个应用最多可以创建31个窗口。

Android 4.1 之后,AndroidOS 团队对Android Display进行了不断地进化和改变。引入了三个核心元素:Vsync,Triple Butter,Choreographer。

首先来理解一下,图形界面的绘制,大概是有CPU准备数据,然后通过驱动层把数据交给GPU来进行绘制。图形API不允许CPU和GPU直接通信,所以就有了图形驱动(Graphics Driver)来进行联系。Graphics Driver维护了一个序列(Display List),CPU不断把需要显示的数据放进去,GPU不断取出来进行显示。

其中Choreographer起调度的作用。统一绘制图像到Vsync的某个时间点。

Choreographer在收到Vsync信号时,调用用户设置的回调函数。函数的先后顺序如下:

CALLBACK_INPUT:与输入事件有关
CALLBACK_ANIMATION:与动画有关
CALLBACK_TRAVERSAL:与UI绘制有关

Vsync是什么呢?首先来说一下什么是FPS,FPS就是Frame Per Second(每秒的帧数)的缩写,我们知道,FPS>=60时,我们就不会觉得动画卡顿。当FPS=60时是个什么概念呢?1000/60≈16.6,也就是说在大概16ms中,我们要进行一次屏幕的刷新绘制。Vsync是垂直同步的缩写。这里我们可以简单的理解成,这就是一个时间中断。例如,每16ms会有一个Vsync信号,那么系统在每次拿到Vsync信号时刷新屏幕,我们就不会觉得卡顿了。

但实现起来还是有点困难的。

多重缓冲是什么技术呢?我们先来说双重缓冲。在Linux上,通常使用FrameBuffer来做显示输出。双重缓冲会创建一个FrontBuffer和一个BackBuffer,顾名思义,FrontBuffer是当前显示的页面,BackBuffer是下一个要显示的画面。然后滚动电梯式显示数据。为什么呢?这样好在哪里呢?首先他并不是不卡了,他还是会卡。但是如果是单重缓冲,页面可能会有这种情况:A面数据需要显示,然后是B面数据显示,B面数据显示需要耗费一定时间,但是这个时间里,C面数据也请求了展示,我们可能会看到,在展示C面数据的时候,还有B面数据的残影…

下面分情况来具体说明一下(Vsync每16秒一次)。

1.没有使用Vsync的情况
在这里插入图片描述
可以看出,在第一个16ms之内,一切正常。然而在第二个16ms之内,几乎是在时间段的最后CPU才计算出了数据,交给了Graphics Driver,导致GPU也是在第二段的末尾时间才进行了绘制,整个动作延后到了第三段内。从而影响了下一个画面的绘制。这时会出现Jank(闪烁,可以理解为卡顿或者停顿)。那么在第二个16ms前半段的时间CPU和GPU干什么了?哦,他们可能忙别的事情了。这就是卡顿出现的原因和情况。CPU和GPU很随意,爱什么时候刷新什么时候刷新,很随意。

2.有Vsync的情况
在这里插入图片描述
如果,按照之前的前提来说,Vsync每16ms一次,那么在每次发出Vsync命令时,CPU都会进行刷新的操作。也就是在每个16ms的第一时间,CPU就会想赢Vsync的命令,来进行数据刷新的动作。CPU和GPU的刷新时间,和Display的FPS是一致的。因为只有到发出Vsync命令的时候,CPU和GPU才会进行刷新或显示的动作。图中是正常情况。那么不正常情况是怎么个情况?我们先来说一下双重缓冲,然后再说。

3.双重缓冲
在这里插入图片描述
逻辑就是和之前一样。多重缓冲页面在Back Buffer,然后根据需求来显示不同数据。但是会有什么问题呢(这就是2中提到的问题)?

首先我们看Display行,A页面需要了两个时间单位,为什么?因为B Buffer在处理的时候太耗时了。然后导致了,在第一个Vsync发出的时候,还在GPU还在绘制B Buffer。那么,刚好,第一个Vsync发出之后很短的时间,A页面展示完了,B Buffer的也在一开始的时候就不进行计算了。那么接下来的时间呢?屏幕还是展示着B Buffer,这时候就会造成Jank现象。他不会动,因为他在等下一个Vsync过来的时候,才会显示下一个数据。

那么,如图,在A Buffer过来的时候,展示B页面的数据。这个时候!重复了上一个情况,也是太耗时了,然后又覆盖了下一个Vysnc发
出的时间,再次造成卡顿!依次类推,会造成多次卡顿。这个时候就有了三重缓冲的概念。

4.三重缓冲
在这里插入图片描述
首先看图。我们看到,B Buffer依旧很耗时,同样覆盖了第一个Vsync发出的时间点。但是,在第一个Vsync发出的时候,C Buffer站了出来,说,我来展示这个页面,你去缓冲A后面需要缓冲的页面吧!然后会发生什么?然后就是出现了一个Jank…,但是这个Jank只在这一个时间单位出现,是可以忽略不计的。因为之后的逻辑都是顺畅的了。依次类推,除了A和B 两个图层在交替显示,还有个“第三者”在不断帮他们两个可能需要展示的数据进行缓冲。但是注意了:

只有在需要时,才会进行三重缓冲。正常情况下,只使用二级缓冲!

另外,缓冲区不是越多越好。上图,C页面在第四个时间段才展示出来,就是因为中间多了一个Buffer(C Buffer)来进行缓冲。

但是,虽然谷歌给了你这么牛逼的前提逻辑,实际开发中你写的APP还是会卡,为什么呢?原因大概有两点:

1.界面太复杂。

2.主线程(UI线程)太忙。他可能还在处理用户交互或者其他事情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值