屏幕渲染机制,UI性能优化,UI卡顿检测,硬件加速

Android性能优化 - 消除卡顿- https://blog.csdn.net/axi295309066/article/details/72675365

-- Android UI, GPU过度绘制一共有以下几种颜色:
原色:没有过度绘制
蓝色:1 次过度绘制
绿色:2 次过度绘制
粉色:3 次过度绘制
红色:4 次及以上过度绘制

--  GPU的ALU(算术逻辑单元)比CPU多的多,而图形处理和栅格化操作实际就是大量的数学计算,所以用GPU去渲染图形比CPU快的多。
 android 3.0引入了硬件加速,android4.0以后默认开启了硬件加速
 RenderThread是Android 5.0引入的功能。渲染工作的真正执行者是 GPU,而 GPU是不知道什么是动画的:执行动画的唯一途径便是将每一帧的不同绘制操作分发给 GPU,但该逻辑本身不能在 GPU 上执行。 而如果在 UI 线程执行该操作,任意的重操作都将阻塞新的绘制指令及时分发,动画就很容易出现延迟和卡顿。 添加一个RenderThread专门用来处理渲染的相关操作,UI线程只管计算生成一个DisplayList,剩下的渲染相关的事情就交给RenderThread,这样减轻了UI线程的负担,也提升了动画的流畅度。

-- 实现一个圆角矩形按钮通常有两种方案:

 使用PNG图片,解码PNG图片生成Bitmap,传到底层,由GPU渲染;
 使用代码(XML/Java)实现,直接将Shape信息传到底层,由GPU渲染。

-- Android 的两种常用绘图机制:Skia,GL
 1.Skia图形库:Skia官网,Skia是一个开源的二维图形库,提供各种常用的API,并可在多种软硬件平台上运行。在没开启硬件加速的时候用到 Canvas 的地方在底层都会调用到 Skia 库中去。在上面我们列举的方式里面:View、Drawable、xml、SV、Canvas 最终使用的都是 Skia 库。另外 Skia 最终是使用 CPU 来对图像进行最终绘制,所以效率比较低。
 2.GL:GL 是用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口。在开启硬件加速的时候用到 Canvas 的地方最终会调用到 GL ES 库中。没开启硬件加速的时候上面我们列举的方式中:GLSurfaceView、TV 最终使用的是 GL ES,另外 GL 使用的是 GPU 来对图像进行绘制,所以效率比较高。
 3.在后续的章节里我会从源代码上分析上面各种绘图方式的调用链。
 4.其实在 7.0 之后 Android 中添加了 Vulkan。Vulkan 是用于高性能 3D 图形的低开销、跨平台 API。与 GL ES 一样,Vulkan 提供多种用于在应用中创建高质量的实时图形的工具。不过目前很少用到

  View 的绘制根据是否硬件加速分为,软件绘制与硬件绘制两种。其实软件绘制与硬件绘制的区别就在于是使用 CPU 进行绘制计算还是使用 GPU 进行绘制计算。

> UI渲染, 屏幕刷新, UI绘制

-- UI渲染流程线:
UI对象—->CPU处理为多维图形,纹理 —–通过OpeGL ES接口调用GPU—-> GPU对图进行光栅化(Frame Rate ) —->硬件时钟(Refresh Rate)—-垂直同步—->投射到屏幕
-- Android UI性能优化 检测应用中的UI卡顿- https://blog.csdn.net/lmj623565791/article/details/58626355
Android硬件加速原理与实现简介- https://tech.meituan.com/hardware-accelerate.html

-- Android 屏幕刷新机制- https://www.cnblogs.com/dasusu/p/8311324.html
Android UI渲染机制与卡顿原理- https://blog.csdn.net/u013263323/article/details/53462724
  Android系统每隔16ms就会重新绘制一次Activity View。因为Android系统每隔16ms就会发出VSYNC信号,触发对UI进行渲染,VSYNC是Vertical Synchronization(垂直同步)的缩写,可以简单的把它认为是一种定时中断。在Android 4.1中开始引入VSYNC机制。
  渲染操作一般依赖两个核心的系统组件:CPU和GPU。 CPU主要负责Measure,Layout,Record,Execute等计算操作,GPU负责Rasterization(栅格化)操作。所谓的栅格化,就是绘制那些Button,Shape,Path,Bitmap等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示,通俗点说,就是解决哪些复杂的XML布局文件和标记语言,使之转换成用户能看懂的图像,但是这不是直接转换,XML布局文件需要在CPU中首先转换为多边形或者纹理,然后再传递给GPU进行栅格化,对于栅格化,跟Opengl有关,栅格化是一个特别费时的操作。
  CPU把控件进行多边形化和纹理化,然后交给GPU进行栅格化渲染。16ms的时间主要被两件事情占用,第一件:将UI对象转换为一系列多边形和纹理;第二件:CPU传递处理数据到GPU进行栅格化,因此要缩短这两部分的时间,需要尽力减少对象转换的次数以及可能发声的问题和解决方案。
  CPU方法,最常见的性能问题是不必要的布局和失效,这些内容必须在识图层次结构中进行测量,清除并重新创建,引发这种问题通常的原因通常有两个,一个是重建显示列表的次数太多,而是花费太多时间作废视图层次并进行不必要的重绘,这两个原因在更新显示列表或者其他缓存GPU资源时资源时导致CPU工作过度。
  而GPU方法,最常见的问题就是过度绘制,通常实在像素着色过程中,通过其他工具进行后期着色时浪费了GPU处理时间。因此,对UI优化时,可以重点从以下两个方向进行:
 1.CPU产生的问题:不必要的布局和失效
 2.GPU产生的问题:过度绘制(overdraw)

-- GPU绘制
深红: 意味着overdraw 4倍。像素绘制了5次或者更多,这是错误的,需要修复;
淡红: 意味着overdraw 3倍。像素绘制了4次,小范围可以接受。
绿色: 意味着overdraw 2倍。像素绘制了3次。中等大小的绿色区域是可以接受的,但是还是应该优化,减少他们。
蓝色: 意味着overdraw 1倍。像素绘制了2次。大片的蓝色还是可以接受的。
优化的目标就是减少红色的Overdraw,看到更多的蓝色区域。

去掉window的背景可以在onCreate()中setContentView()之后调用getWindow().setBackgroundDrawable(null);或者在theme中添加android:windowbackground=”null”;
-- GUI 显示系统
  在一个典型的显示系统中,一般包括CPU、GPU、display三个部分, CPU负责计算数据,把计算好数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer里存起来,然后display(有的文章也叫屏幕或者显示器)负责把buffer里的数据呈现到屏幕上。
  显示过程,简单的说就是CPU/GPU准备好数据,存入buffer,display每隔一段时间去buffer里取数据,然后显示出来。display读取的频率是固定的,比如每个16ms读一次,但是CPU/GPU写数据是完全无规律的。

    View 的测量、布局、绘制三大流程都是交由 ViewRootImpl 发起,而且还都是在 performTraversals() 方法中发起的,所以这个方法的逻辑很复杂,因为每次都需要根据相应状态判断是否需要三个流程都走,有时可能只需要执行 performDraw() 绘制流程,有时可能只执行 performMeasure() 测量和 performLayout() 布局流程(一般测量和布局流程是一起执行的)。不管哪个流程都会遍历一次 View 树,所以其实界面的绘制是需要遍历很多次的,如果页面层次太过复杂,每一帧需要刷新的 View 又很多时,耗时就会长一点。

-- Android系统显示原理 GUI
  Android显示过程可以简单概括为:Android应用程序把经过测量、布局、绘制后的Surface缓存数据,通过SurfaceFlinger把数据渲染到显示屏幕上, 通过Android的刷新机制来刷新数据。也就是说应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过刷新机制把数据更新到屏幕上。
  我们都知道在Android的每个View绘制中有三个核心步骤:Measure、Layout、Draw。具体实现是从 ViewRootImp类的performTraversals() 方法开始执行,Measure和Layout都是通过递归来获取View的大小和位置,并且以深度作为优先级,可以看出层级越深、元素越多、耗时也就越长。
  真正把需要显示的数据渲染到屏幕上,是通过系统级进程中的SurfaceFlinger服务来实现的,那么这个SurfaceFlinger服务主要做了哪些工作呢?如下:
 1.响应客户端事件,创建Layer与客户端的Surface建立连接。
 2.接收客户端数据及属性,修改Layer属性,如尺寸、颜色、透明度等。
 3.将创建的Layer内容刷新到屏幕上。
 4.维持Layer的序列,并对Layer最终输出做出裁剪计算。

  既然是两个不同的进程,那么肯定是需要一个跨进程的通信机制来实现数据传递,在Android显示系统中,使用了Android的匿名共享内存:SharedClient,每一个应用和SurfaceFlinger之间都会创建一个SharedClient ,然后在每个SharedClient中,最多可以创建31个 SharedBufferStack,每个Surface都对应一个SharedBufferStack,也就是一个Window。
  一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含多个窗口,即Surface。也就是说SharedClient包含的是SharedBufferStack的集合,其中在显示刷新机制中用到了双缓冲和三重缓冲技术。最后总结起来显示整体流程分为三个模块:应用层绘制到缓存区,SurfaceFlinger把缓存区数据渲染到屏幕,由于是不同的进程,所以使用Android的匿名共享内存SharedClient缓存需要显示的数据来达到目的。
  除此之外,我们还需要一个名词:FPS。FPS表示每秒传递的帧数。在理想情况下,60FPS就感觉不到卡,这意味着每个绘制时长应该在16ms以内。但是 Android系统很有可能无法及时完成那些复杂的页面渲染操作。Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的60FPS。如果某个操作花费的时间是24ms ,系统在得到VSYNC信号时就无法正常进行正常渲染,这样就发生了丢帧现象。那么用户在32ms内看到的会是同一帧画面,这种现象在执行动画或滑动列表比较常见,还有可能是你的Layout太过复杂,层叠太多的绘制单元,无法在16ms完成渲染,最终引起刷新不及时。
-- UI绘制:
 1.我们知道一个 View 发起刷新的操作时,最终是走到了 ViewRootImpl 的 scheduleTraversals() 里去,然后这个方法会将遍历绘制 View 树的操作 performTraversals() 封装到 Runnable 里,传给 Chorerographer,以当前的时间戳放进一个 mCallbackQueue 队列里,然后调用了 native 层的方法向底层注册监听下一个屏幕刷新信号事件。
 2.当下一个屏幕刷新信号发出的时候,如果我们 app 有对这个事件进行监听,那么底层它就会回调我们 app 层的 onVsync() 方法来通知。当 onVsync() 被回调时,会发一个 Message 到主线程,将后续的工作切到主线程来执行。
 3.切到主线程的工作就是去 mCallbackQueue 队列里根据时间戳将之前放进去的 Runnable 取出来执行,而这些 Runnable 有一个就是遍历绘制 View 树的操作 performTraversals()。在这次的遍历操作中,就会去绘制那些需要刷新的 View。
 4.所以说,当我们调用了 invalidate(),requestLayout(),等之类刷新界面的操作时,并不是马上就会执行这些刷新的操作,而是通过 ViewRootImpl 的 scheduleTraversals() 先向底层注册监听下一个屏幕刷新信号事件,然后等下一个屏幕刷新信号来的时候,才会去通过 performTraversals() 遍历绘制 View 树来执行这些刷新操作。

> 硬件加速
-- 硬件加速的主要原理,就是通过底层软件代码,将CPU不擅长的图形计算转换成GPU专用指令,由GPU完成。
在Android中,大多数应用的界面都是利用常规的View来构建的(除了游戏、视频、图像等应用可能直接使用OpenGL ES)。
--DisplayList:
 DisplayList是一个基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应Canvas的drawXxx()方法(如下图)。
 信息传递流程:Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU。
 在Android 4.1及以上版本,DisplayList支持属性,如果View的一些属性发生变化(比如Scale、Alpha、Translate),只需把属性更新给GPU,不需要生成新的DisplayList。
-- RenderNode:一个RenderNode包含若干个DisplayList,通常一个RenderNode对应一个View,包含View自身及其子View的所有DisplayList。

-- 硬件加速:
 CPU更擅长复杂逻辑控制,而GPU得益于大量ALU和并行结构设计,更擅长数学运算。
 页面由各种基础元素(DisplayList)构成,渲染时需要进行大量浮点运算。
 硬件加速条件下,CPU用于控制复杂绘制逻辑、构建或更新DisplayList;GPU用于完成图形计算、渲染DisplayList。
 硬件加速条件下,刷新界面尤其是播放动画时,CPU只重建或更新必要的DisplayList,进一步提高渲染效率。

 实现同样效果,应尽量使用更简单的DisplayList,从而达到更好的性能(Shape代替Bitmap等)。

你可能感兴趣的:(屏幕渲染机制,UI性能优化,UI卡顿检测,硬件加速)