关于iOS Drawing
前言
学了一段时间的OpenGL,这次又重新看了一遍 iOS Core Animation 这本书,查看了许多资料。对这个iOS的drawing稍有顿悟=。=
这篇文章里面,我会针对下面几个方面说说我自己的见解:
- Core Animation,Core Graphic 和 OpenGL ES
- Rendering以及Animation的步骤
- iOS Drawing中的CPU,GPU
- drawRect方法的弊端
- 影响GPU与CPU绘制的因素
Core Animation,Core Graphics 和 OpenGL ES
Core Animation,Core Graphics 和 OpenGL ES 三者都是和iOS画图,呈现内容相关的iOS框架。总体来说,Core Animation和UIKit是一个层级的,而他们底层画图的框架就是Core Graphics 和 OpenGL ES。
- Core Animation 是iOS用来控制内容的显示和动画的。它的主要功能就是管理CALayer。
- UIView 大家都很熟悉。那么UIView底层负责显示内容和动画的就是 CALayer 。UIView上和显示以及动画相关的接口,其实也是对CALayer的接口的包装
- CALayer的contents最终会显示在屏幕上。而Core Graphics 和 OpenGL ES就负责在CALayer上面作画,并将其内容显示到屏幕上
- Core Graphics是个2D图形库,一般用来画矢量图,比如UIBezierPath等等。它主要使用CPU来绘制。以绘制一条UIBezierPath为例:通过CPU的计算,生成包含该path路径的bitmap,然后将该bitmap传递给GPU,绘制出来。所以Core Graphics的主要计算压力在CPU上面,而且使用CPU来生成bitmap是很耗时的一个步骤
- OpenGL ES 讲道理是可以画任何的2D、3D图形的。以绘制一个三角形为例,只需要将3个顶点传递给GPU,然后GPU会计算生成包含该三角形的bitmap,最终使用GPU将该bitmap绘制出来。
- 所以我理解的Core Graphics和OpenGL ES的主要区别就是生成最终的 bitmap 的位置。Core Graphics由CPU来生成bitmap;OpenGL ES由GPU来生成bitmap。 最终这个bitmap都要交给GPU来渲染到屏幕上面。
- 由于CPU生成bitmap较为耗时(这也是Core Graphics性能差的原因),而且CPU还要执行其他的任务,所以不推荐使用Core Graphics来绘制。
- Core Animation对绘制过程进行了优化,大多数的UIKit里面的原件(UIView)的底层layer都是硬件加速的,也就是使用GPU来生成bitmap并绘制。
Rendering和Animation的步骤
参考iOS Core Animation 一书的第12章
- Layout: 设置view/layer树种每个layer的属性(frame, background color, border等等)
- Display: 生成layer的backing image,在这一步涉及到
-drawRect:
和-drawLayer:inContext:
的调用,也就是使用CPU生成bitmap,来填充backing image。也可以使用UIImage来填充backing image。 - Prepare: Core Animation准备好Animation的数据,比如动画的属性,终止值等等
- Commit: Core Animation将所有layer数据和动画数据打包将他们发送给render server 进行显示
- 将layer的属性计算,转化成OpenGL的顶点等属性(提供数据给GPU,计算bitmap,也就是OpenGL中的各个三角形)
- GPU组合各个layer的bitmap,将其渲染在屏幕上
iOS Drawing中的CPU和GPU
- Core Graphics使用CPU计算bitmap
- OpenGL ES使用GPU计算bitmap
- 使用GPU将所有的layer的bitmap组合在一起
drawRect方法的弊端
drawRect方法是对drawLayer:inContext:的包装。两个方法如果有实现的话,在重绘layer的时候会调用。他们适合自定义layer内容。但是会影响性能
在每次调用这两个方法的时候,都会新建一个与当前的layer的大小相等的空的context(bitmap),然后使用Core Graphics或者直接UIImage等等将该context填充。这相当与离屏渲染。然后将生成的context再赋给layer的content,再交给GPU渲染出来。
注意:CALayer会cache自己的bitmap,那么每次渲染的时候就不需要重新生成CALayer的bitmap。只需要将bitmap交给GPU,让GPU执行组合工作就可以了。一般来说,生成bitmap(无论在CPU端还是GPU端)都是比组合bitmap耗时的
影响GPU与CPU绘制的因素
GPU端
- 太多的重复绘制:一次绘制过程中,在同一个像素点,绘制多遍
- 离屏渲染:CPU和GPU都会出现离屏渲染的情况,这需要额外的内存来存放渲染的结果
- 太多的geometry:也就是OpenGL中,太多的三角形
- 渲染太大的图片:GPU支持的最大的渲染texture是有限的(通常2048*2048,或者retina的4096*4096),图片像素过大的话,要先在CPU端处理之后,才能交给GPU显示
CPU端
- Layout计算:也就是计算每个layer的frame等等属性,这个在使用autolayout的系统上面更加的费时
- Lazy View Loading:只有在view第一次出现在屏幕上时,才会去加载view相关的资源(图片等等),所以如果这个资源很大,或者延迟很大(网络获取)的时候,会延缓CPU的操作。这一步的操作包括Layout,Display等步骤
- Core Graphics drawing
- 图片解压:因为存放的基本都是压缩的图片,但是显示的时候需要全size的图片,所以需要CPU执行图片解压的工作
参考
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.