图像显示与优化离屏渲染

iOS图形显示原理、界面保持流畅的技巧

2018-06-21  本文已影响51人  蔚尼

写在前面:
这篇文章并非原创,是对iOS 保持界面流畅的技巧的学习总结。
讲述图像显示的原理;界面卡顿的原因;从CPU和GPU方面如何解决卡顿(让界面保持流畅)。

一.屏幕显示图像的原理

1. HSync、VSync

CRT电子枪从上到下一行行扫描,扫描完成后回到初始位置继续下一次扫描。每扫描完一行,显示器会发出一个水平同步信号HSync,让显示器的显示过程和视屏控制器进行同步。

绘制完一帧后,CRT电子枪回复到原位准备下一帧前,显示器会发出垂直同步信号VSync。显示器也按照VSync信号产生的频率进行刷新。

图像显示原理.png

2. 图像显示原理

图像显示原理

1)CPU计算好内容,提交给GPU进行渲染,2)GPU把渲染的结果放入帧缓冲区(FrameBuffer);
3)视频控制器收到VSync信号后逐行读取FrameBuffer的数据,数据转换后传递给显示器

2.1 CPU的工作

CPU的工作.png

2.1 GPU的工作

GPU的渲染管线.png

二.卡顿、掉帧产生的原因:

卡顿、掉帧

每秒会有60帧的画面更新,所以60分之一秒就要产生一个画面,即60ps是流畅画面;相当于16.7ms产生一个画面;

16.7ms内会场上一个VSync信号。

总结:
出现卡顿和掉帧的原因:在VSync信号到来之前(16.7ms),CPU和GPU没有完成下一帧画面的合成,就会造成卡顿。

三.卡顿尝试的原因和解决方案

CPU方面:

CPU优化方案

1.对象创建、调整、销毁

1.1对象创建
1.2对象调整

CAlayer内部没有属性,当调整UIView 的关于显示相关的属性(比如 frame/bounds/transform)的时候,resolveInstanceMethod临时创建一个方法,把修改的属性值放到字典里面,创建动画等,非常消耗属性。
所以修改UIView的frame/bounds/transform属性消耗资源大于一般的属性;

1.3对象销毁

eg:把对象捕获到 block 中,然后扔到后台队列去随便发送个消息以避免编译器警告,就可以让对象在后台线程销毁了。

NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue, ^{
    [tmp class];
});

2.预排版(布局计算、文本计算)

2.1布局计算

把布局放到后台计算,并进行缓存;

例如tableview的cell高度,可以在获取到数据之后,在后台线程里面进行计算好,避免cell里面和height获取的时候再次计算;

2.2文本计算

一个界面中包含大量文本(比如微博微信朋友圈等),文本的宽高计算会占用很大一部分资源,并且不可避免。

3.预渲染(文本等异步绘制、图片编解码、图像的绘制等)

3.1文本渲染

常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。

3.2图片的解码

因为用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,只能按照上面的图片解码方法。

3.3图片的绘制

Quartz 2D绘制路径、文字Quartz 2D绘制路径实例里面,在[UIView drawRect:] 用CG开头的方法进行绘制,就是最简单的绘制方法。

GPU方面:

GPU方面

纹理渲染

较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。

视图混合

当多个视图(或者说 CALayer)重叠在一起显示时,GPU 会首先把他们混合到一起,混合的过程也会消耗很多 GPU 资源。所以需要试图混合。

图形的生成

CALayer 的 border、圆角、阴影、遮罩(mask)---》产生离屏渲染(GPU方面的);
列表有大量圆角的时候,快速滚动列表,GPU资源基本占满,CPU资源消耗少;


补充:
上述实践:微博Demo性能优化技巧

使用第三方# AsyncDisplayKit
,可把控件房贷线程创建和修改、图层预合成、异步并发操作、runloop任务分发、滑动列表预加载;


下面对上面进行总结:

卡顿优化 - CPU

卡顿优化 - GPU

面试题
你在项目中是怎么优化内存的?

优化你是从哪几方面着手?

列表卡顿的原因可能有哪些?你平时是怎么优化的?

遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?

卡顿检测

可以使用第三方LXDAppFluecyMonitor-master辅助

耗电优化

1. 耗电主要来源

2.耗电优化

  1. 尽可能降低CPU、GPU功耗
  2. 少用定时器
  3. 优化I/O操作
  1. 网络优化
  1. 定位优化
  1. 硬件检测优化
    用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

启动优化

1.APP的启动

  1. APP的启动可以分为2种
  1. 检测启动时间:

2. APP冷启动三阶段

包含以下三个阶段:

冷启动三阶段

2.1. dyld阶段

1.什么是 dyld(dynamic link editor):Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)

  1. 启动APP时,dyld所做的事情有

2.2. runtime阶段

启动APP时,runtime所做的事情有:

  1. 调用map_images进行可执行文件内容的解析和处理
  2. 在load_images中调用call_load_methods,调用所有Class和Category的+load方法
  3. 进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)
  4. 调用C++静态初始化器和attribute((constructor))修饰的函数

到此为止,可执行文件和动态库中所有的符号(Class,Protocol,Selector,IMP,…)都已经按格式成功加载到内存中,被runtime 所管理

2.3.main阶段

总结一下

  1. APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库
  2. 并由runtime负责加载成objc定义的结构
  3. 所有初始化工作结束后,dyld就会调用main函数
  4. 接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法

APP的启动优化

按照不同的阶段

  1. 减少动态库、合并一些动态库(定期清理不必要的动态库)
  2. 减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)
  3. 减少C++虚函数数量
  4. Swift尽量使用struct

安装包瘦身

  1. 资源(图片、音频、视频等)
  1. 可执行文件瘦身
  1. 生成LinkMap文件,可以查看可执行文件的具体组成
linkmap

可借助第三方工具解析LinkMap文件: https://github.com/huanxsd/LinkMap


还需要学习的地方:


1.简单的 FPS 指示器:FPSLabel
2.查看自己app的fps
3.使用CADisplayLink显示FPS


1.如何异步绘制?
这篇博客异步绘制通过VVeboTableViewDemo分享了如何进行一步绘制;

并讲述以下部分:
提前计算并缓存好高度;
滑动时按需加载,在大量图片展示时提高滑动速度。

2.如何使用CoreText 绘制文本:(图文混排的绘制)
4.如何使用[NSAttributedString drawWithRect:options:context:] 绘制文本;


1.在后台线程操作对图片削圆,然后放到ImageCache缓存中/YYKit把头像渲染为圆形后放到缓存,如何使用这个功能?
2.YYKit的学习:YYLayout、YYDispatchQueuePool
、YYKit里面对图片的加载和解码的使用


上一篇下一篇

猜你喜欢

热点阅读