一,图像显示原理 从显示器屏幕刷新原理说起 手机显示屏一般是液晶或者OLED,无论什么屏幕,现代显示屏都是由一个一个很小的像素点构成,比如液晶显示屏是由红绿蓝三色液晶组成的像素点构成,液晶本身不发光,需要加个LED灯做背光。OLED是目前比较先进的柔性显示屏,有机发光二极管,顾名思义,自己本身就是可以发光的二极管,也是红绿蓝三色组成。只要控制三原色像素点的明暗就可以控制屏幕显示不同的颜色。以下是显示器三原色像素常见的排列
显示器是通过逐行扫描,一行行扫描完,直到扫描完整个屏幕成后显示器就呈现一帧画面,随后电子枪回到初始位置继续下一次扫描。为了把显示器的显示过程和系统的视频控制器进行同步,显示器(或者其他硬件)会用硬件时钟产生一系列的定时信号。
HSync:当一行扫描结束开始新的一行,准备进行扫描时,显示器会发出一个水平同步信号,简称 HSync(horizonal synchronization)。
VSync:当一帧画面绘制完成后,开始从头扫描时,准备画下一帧前,显示器会发出一个垂直同步信号,简称VSync(vertical synchronization)。
显示器通常以“固定频率“进行刷新,这个刷新率就是VSync信号产生的频率。 iOS设备的屏幕刷新频率是每秒60帧,平均每16.67ms发出一个VSync。 二、CPU,GPU以及显示器的协作方式
从图形渲染流程一文中可知,CPU、GPU、显示器是以下图这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,视频控制器收到VSync信号后逐行读取帧缓冲区的数据,再经过一定的数模转换传递给显示器显示。
如果这个过程中只有一个缓冲区且没有引入VSync,不仅在帧缓冲区的读取和刷新上有比较大的效率问题,还会在屏幕画面呈现上出问题。先来介绍两个概念:
屏幕刷新频率:Refresh Rate或Scanning Frequency,单位hz,是指设备刷新屏幕的频率。
帧率:Frame Rate,单位 fps,是指 GPU 生成帧的速率。
单缓存,GPU 向缓存区中写入数据,视频控制器从缓存区中取图像数据后显示,理想的情况是帧率和屏幕刷新频率相等,每绘制一帧,屏幕就显示一帧。而实际情况是,如果没有同步机制,当帧率大于屏幕刷新频率时,视屏控制器刚逐行读取完第2帧的上半部分时,GPU 已经完成第3帧的渲染并提交到缓存区中,视屏控制器会继续读取第三帧的下半部,这样会造成画面撕裂。
三、iOS屏幕刷新机制
为了解决单缓存区的问题,iOS设备在这个过程中采取了如下图所示的双缓存区+VSync机制:
GPU 会预先渲染好一帧放入一个缓存区内(前帧缓存)。
在显示器发出VSync后,视频控制器的指针会指向前帧缓存区并开始读取,GPU开始渲染下一帧,并将渲染结果放入另一个缓存区(后帧缓存)。
在显示器发出新的VSync后,视频控制器的指针会指向后帧缓存区并开始读取,GPU开始渲染下一帧,并将渲染结果放入前帧缓存区。
双缓存和VSync造成的问题
由上文可知,每一帧画面要先经过CPU计算,再经过GPU渲染,最后存入缓存区供视频控制器读取。由于垂直同步的机制,如果在一个VSync时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,而这时显示屏会保留之前的内容不变,也就造成界面卡顿。如下图所示,A、B代表两个缓存区。
1.CPU:输出位图 2.GPU :图层渲染,纹理合成 3.把结果放到帧缓冲区(frame buffer)中 4.再由视频控制器根据 vsync 信号在指定时间之前去提取帧缓冲区的屏幕显示内容 5.显示到屏幕上 CPU 工作 1.Layout: UI 布局,文本计算 2.Display: 绘制 3.Prepare: 图片解码 4.Commit:提交位图 GPU 渲染管线(OpenGL) 顶点着色,图元装配,光栅化,片段着色,片段处理 四、UI 卡顿掉帧原因 iOS 设备的硬件时钟会发出 Vsync(垂直同步信号),然后App的 CPU 会去计算屏幕要显示的内容,之后将计算好的内容提交到GPU去渲染。随后,GPU 将渲染结果提交到帧缓冲区,等到下一个 VSync 到来时将缓冲区的帧显示到屏幕上。也就是说,一帧的显示是由 CPU 和 GPU 共同决定的。 一般来说,页面滑动流畅是 60fps,也就是 1s 有 60 帧更新,即每隔 16.7ms 就要产生一帧画面,而如果 CPU 和GPU加起来的处理时间超过了 16.7ms,就会造成掉帧甚至卡顿。 五、滑动优化方案 CPU: 把以下操作放在子线程中 1.对象创建、调整、销毁 2.预排版(布局计算、文本计算、缓存高度等等) 3.预渲染(文本等异步绘制,图片解码等) GPU: 纹理渲染,视图混合 一般遇到性能问题时,考虑以下问题: 1.是否受到CPU 或者GPU 的限制? 2.是否有不必要的CPU 渲染? 3.是否有太多的离屏渲染操作? 4.是否有太多的图层混合操作? 5.是否有奇怪的图片格式或者尺寸? 6.是否涉及到昂贵的view 或者效果? 7.view 的层次结构是否合理? 六、UI 绘制原理
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群里会免费提供一些收藏的免费学习书籍资料以及整理好的面试题和答案文档!)
异步绘制: [self.layer.delegate displayLayer: ] 代理负责生成对应的 bitmap 设置该bitmap 作为该layer.contents 属性的值 七、离屏渲染 On-Screen Rendering:当前屏幕渲染,指的是GPU 的渲染操作是在当前用于显示的屏幕缓冲区中进行 Off-Screen Rendering:离屏渲染,分为CPU 离屏渲染和GPU 离屏渲染两种形式。GPU 离屏渲染指的是 GPU在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作应当尽量避免的则是GPU 离屏渲染 GPU 离屏渲染何时会触发呢? 圆角(当和maskToBounds 一起使用时)、图层蒙版、阴影,设置
为什么要避免 GPU 离屏渲染? GPU 需要做额外的渲染操作。通常GPU在做渲染的时候是很快的,但是涉及到 offscreen-render 的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程需要做 onscreen 跟 offscreen 上下文之间的切换,这个过程的消耗会比较昂贵,涉及到 OpenGL 的 pipeline 跟barrier,而且 offscreen-render 在每一帧都会涉及到,因此处理不当肯定会对性能产生一定的影响。另 外由于离屏渲染会增加 GPU 的工作量,可能会导致 CPU+GPU 的处理时间超出 16.7ms,导致掉帧卡顿。所以可以的话应尽量减少 offscreen-render 的图层
|