>>分享Android开发相关的技术 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 20099 个阅读者 刷新本主题
 * 贴子主题:  Android 性能优化之渲染原理 回复文章 点赞(0)  收藏  
作者:mary    发表时间:2020-03-08 21:42:10     消息  查看  搜索  好友  邮件  复制  引用

                                                                                                

Android 性能优化之渲染原理

先来看几个概念

     fps:每秒传输的帧数,FPS”也可以理解为我们常说的“刷新率(单位为Hz)”。

    hz:代表屏幕在一秒内刷新的次数,Android手机一般为60HZ,也就是一秒刷新60帧,大约16.67ms刷新一次  

    丢帧:本来一秒中画面需要更新 60 帧,但是如果这期间只更新了 55 帧 , 那么在用户看来就是丢帧了 , 主观感觉就是卡了

    tearing: 一个屏幕内的数据来自2个不同的帧,画面会出现撕裂感

    jank: 绘图速度过慢,一个帧在屏幕上连续出现2次

    lag:从用户体验来说,就是点击下去到呈现效果之间存在延迟

    撕裂(screen tearing):当cpu/gpu将数据准备写入buffer(缓冲区)中,但display还没来的及显示,这时cpu/gpu把下一帧数据往buffer写,还没写完,Display开始读去buffer显示,(也就是绘图数度大于显示速度),这是就会出现显示的上半部分为下一帧数据,下半部分为上一帧数据,如图所示,这就是撕裂。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     tearing发生的原因是display读buffer同时,buffer被修改,那么多一个buffer是不是能解决问题,这个时候双缓冲就诞生了。

双缓冲机制

从性能角度出发,如果只对一块缓冲区进行读写无疑效率比较低下:一方面屏幕要从该区域去读,另一方面显卡要等待去写.因此在实际中,其实帧缓冲区实则被划分为两部分:

     前缓冲区: 用来缓存要显示到屏幕的帧数据

后缓冲区: 用来缓存显卡生成的帧数据.

屏幕只能前缓冲区读取数据用于显示,显卡只能往后缓冲区写入新生成的帧数据.需要注意的是两块缓冲区并不发生实际上的数据拷贝操作,即将后缓冲区的帧数据拷贝到前缓冲区,而是在前缓冲区的帧数据已经推到屏幕上,且新的帧数据被写入到后台缓冲区后,进行指针交换操作,将原来的后缓冲区变为前缓冲区

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     但还是有问题,假如是backbuffer准备完成一帧数据以后就进行,那么如果此时屏幕还没有完整显示上一帧内容的话,肯定是会出问题的。看来只能是等到屏幕处理完一帧数据后,才可以执行这一操作了.

当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI)。大家应该能想到了,这个时间点就是我们进行缓冲区交换的最佳时间。因为此时屏幕没有在刷新,也就避免了交换过程中出现 screentearing的状况。VSync(垂直同步)是VerticalSynchronization的简写,它利用VBI时期出现的vertical sync pulse来保证双缓冲在最佳时间点才进行交换。

所以说V-sync这个概念并不是Google首创的,它在早些年前的PC机领域就已经出现了。不过Android 4.1给它赋予了新的功用,稍后就可以看到。

Android显示原理

在一个典型的显示系统中分为GPU,CPU,Display(有些言论称为显示器)三部分,Android中LayoutInflater将布局中的xml标签转换为对象,cpu经过计算将它转化为多边形(Polygons)或Texture(纹理)。gpu对多边形或者纹理进行栅格化(Rasterization)操作,栅格化后的数据写入帧缓冲区中等待显示器显示。如图:

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

安卓在4.1做的优化(Project Butter 黄油计划)

在Android 4.1之前,界面卡顿是Android中最受诟病的一点.为了解决界面卡顿问题,Google为Android引入了Project Butter计划,即常说的黄油计划.在该项目中,Google对Android显示系统进行重构,并引入了三个至关重要的改进:

         VSync增强:VSync不仅仅用于避免画面撕裂现象,现在它还会通知GPU在渲染下一帧之前要等待屏幕完成            逐行绘制.

    Triple Buffer: 三缓冲机制  

    Choreographer: 用于协同nimations,input和drawing一起工作

         双缓冲意味着要使用两个缓冲区(SharedBufferStack中),其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中,其中Display处理前Front Buffer,CPU、GPU处理Back Buffer

     需要注意的是Android中一直存在VSync机制(有待考证),只不过早期VSync只是为了避免画面撕裂(screen tearing)现象.为了更好的了解增强过的VSync和Triple Buffer,我们先来看一下早期图像显示的过程,即VSync只用来避免画面撕裂的情况:

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     这个图中有三个元素,Display是显示屏幕,GPU和CPU负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。VSync用于指导双缓冲区的交换。以时间的顺序来看下将会发生的异常:
  1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成。
  2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧。
  3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理
  4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。
  5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。
其实总结上面的这个情况之所以发生,首先的原因就在于第二帧没有及时的绘制。那么如何使得第二帧及时被绘制呢?这就是我们在Graphic系统中引入VSYNC的原因,

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

CPU/GPU根据VSYNC信号同步处理数据,可以让CPU/GPU有完整的16ms时间来处理数据,减少了jank。假如CPU/GPU的FPS(FramesPer Second)高于这个值,那么这个方案是完美的,显示效果将很好。

总结,vync同步使得CPU/GPU充分利用了16ms时间,减少jank

Triple Buffer

在双缓存的基础上,android又引入了三缓存技术。

首先来看传统的双缓冲机制,理想情况下,其工作状态如下:

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

在上图中,以GPU一行为例,长方形A和B分别代表两块缓冲区域,分别代表前台缓冲区和后台缓冲区.开始时A作为前台缓冲区,此时GPU会想后台缓冲区B写入帧数据;当B缓冲区准备就绪后,A,B交换,B变成前台缓冲区,A变成后台缓冲区,一切都很流程,CPU/GPU充分利用每个16.6ms,在前一个VSync到来时开始准备数据,并在后一个VSync到来时准备好数据.但问题时,我们无法确保CPU/GPU都能在16.6ms内能够准备好数据(l可能设备较差,或者界面你叫复杂,cpu/gpu无法在16秒内处理好数据导致交换延迟)

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     当CPU/GPU的处理时间超过16ms时,第一个VSync到来时,缓冲区B中的数据还没有准备好,于是只能继续显示之前A缓冲区中的内容。而B完成后,又因为缺乏VSync pulse信号,它只能等待下一个signal的来临。于是在这一过程中,有一大段时间是被浪费的。当下一个VSync出现时,CPU/GPU马上执行操作,此时它可操作的buffer是A,相应的显示屏对应的就是B。这时看起来就是正常的。只不过由于执行时间仍然超过16ms,导致下一次应该执行的缓冲区交换又被推迟了——如此循环反复,便出现了越来越多的“Jank”。

     很显然,第一次的Jank看起来是没有办法的,除非升级硬件配置来加快FPS。我们关注的重点是被CPU/GPU浪费的时间段,怎么才能充分利用起来呢?Google为了解决该问题,引入的三缓冲机制,即在原来双缓冲的机制上加入了第三块缓冲区.

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     我们来逐步分析下这个是否有效。首先和预料中的一致,第一次“Jank”无可厚非。不过让人欣慰的是,当第一次VSync发生后,CPU不用再等待了,它会使用第三个buffer C来进行下一帧数据的准备工作。虽然对缓冲区C的处理所需时间同样超过了16ms,但这并不影响显示屏——第2次VSync到来后,它选择buffer B进行显示;而第3次VSync时,它会接着采用C,而不是像double buffering中所看到的情况一样只能再显示一遍B了。这样子就有效地降低了jank。但是带来了lag的问题,如上图所示,A这一帧在第4个vsync来的时候才显示,如果是双缓冲,那在第三个vynsc就可以显示了。

     三缓冲作用:

简单的说在2个缓存区被GPU和display占据的时候,开辟一个缓冲区给CPU用,一般来说都是用双缓冲,需要的时候会开启3缓冲,三缓冲的好处就是使得动画更为流程,但是会导致lag,从用户体验来说,就是点击下去到呈现效果会有延迟。所以默认不开三缓冲,只有在需要的时候自动开启

一句话总结三缓冲有效利用了等待vysnc的时间,减少了jank,但是带来了lag

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     总结:

VSync(解决画面撕裂)

三缓冲(提升CPU/GPU利用率)

     参考:

    http://lionoggo.com/2017/09/12/%E4%BB%8E%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E7%B3%BB%E7%BB%9F%E5%88%B0Android%E6%98%BE%E7%A4%BA%E7%B3%BB%E7%BB%9F/#VSync%E6%80%BB%E7%BB%93

    https://www.jianshu.com/p/0cb06877cf2a#cd582b20-7596-518f-a6a8-d79cd29a17ea

    https://blog.csdn.net/litefish/article/details/53939882#commentBox

                                                                                                                    
----------------------------
原文链接:https://blog.csdn.net/jingjc/article/details/102644795

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-03-10 12:29:38 重新编辑]
  Java面向对象编程-->输入与输出(上)
  JavaWeb开发-->自定义JSP标签(Ⅱ)
  JSP与Hibernate开发-->JPA API的高级用法
  Java网络编程-->ServerSocket用法详解
  精通Spring-->Vue Router路由管理器
  Vue3开发-->Vue指令
  Android ListView高度问题
  Android 自定义Menu
  Android ExpandableListView 使用范例
  android异步更新UI
  Android平台概述
  Android ListView滑动加载
  总是听到有人说AndroidX,到底什么是AndroidX?
  Android性能优化:Android UI渲染机制
  Android性能优化:App启动原理分析及启动时间优化
  自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)
  Android 应用程序组件
  Android 开发环境搭建
  MVVM 架构与数据绑定库
  RecyclerView顶部阴影透明度渐变效果
  Android中NDK的含义和作用
  更多...
 IPIP: 已设置保密
树形列表:   
1页 1条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。