前言

学了一段时间的OpenGL,这次又重新看了一遍 iOS Core Animation 这本书,查看了许多资料。对这个iOS的drawing稍有顿悟=。=

这篇文章里面,我会针对下面几个方面说说我自己的见解:

  1. Core Animation,Core Graphic 和 OpenGL ES
  2. Rendering以及Animation的步骤
  3. iOS Drawing中的CPU,GPU
  4. drawRect方法的弊端
  5. 影响GPU与CPU绘制的因素

Core Animation,Core Graphics 和 OpenGL ES

Core Animation,Core Graphics 和 OpenGL ES 三者都是和iOS画图,呈现内容相关的iOS框架。总体来说,Core Animation和UIKit是一个层级的,而他们底层画图的框架就是Core Graphics 和 OpenGL ES。

  1. Core Animation 是iOS用来控制内容的显示和动画的。它的主要功能就是管理CALayer
  2. UIView 大家都很熟悉。那么UIView底层负责显示内容和动画的就是 CALayer 。UIView上和显示以及动画相关的接口,其实也是对CALayer的接口的包装
  3. CALayer的contents最终会显示在屏幕上。而Core GraphicsOpenGL ES就负责在CALayer上面作画,并将其内容显示到屏幕上
  4. Core Graphics是个2D图形库,一般用来画矢量图,比如UIBezierPath等等。它主要使用CPU来绘制。以绘制一条UIBezierPath为例:通过CPU的计算,生成包含该path路径的bitmap,然后将该bitmap传递给GPU,绘制出来。所以Core Graphics的主要计算压力在CPU上面,而且使用CPU来生成bitmap是很耗时的一个步骤
  5. OpenGL ES 讲道理是可以画任何的2D、3D图形的。以绘制一个三角形为例,只需要将3个顶点传递给GPU,然后GPU会计算生成包含该三角形的bitmap,最终使用GPU将该bitmap绘制出来。
  6. 所以我理解的Core Graphics和OpenGL ES的主要区别就是生成最终的 bitmap 的位置。Core Graphics由CPU来生成bitmap;OpenGL ES由GPU来生成bitmap。 最终这个bitmap都要交给GPU来渲染到屏幕上面。
  7. 由于CPU生成bitmap较为耗时(这也是Core Graphics性能差的原因),而且CPU还要执行其他的任务,所以不推荐使用Core Graphics来绘制。
  8. Core Animation对绘制过程进行了优化,大多数的UIKit里面的原件(UIView)的底层layer都是硬件加速的,也就是使用GPU来生成bitmap并绘制。

Rendering和Animation的步骤

参考iOS Core Animation 一书的第12章

  1. Layout: 设置view/layer树种每个layer的属性(frame, background color, border等等)
  2. Display: 生成layer的backing image,在这一步涉及到-drawRect:-drawLayer:inContext:的调用,也就是使用CPU生成bitmap,来填充backing image。也可以使用UIImage来填充backing image。
  3. Prepare: Core Animation准备好Animation的数据,比如动画的属性,终止值等等
  4. Commit: Core Animation将所有layer数据和动画数据打包将他们发送给render server 进行显示
  5. 将layer的属性计算,转化成OpenGL的顶点等属性(提供数据给GPU,计算bitmap,也就是OpenGL中的各个三角形)
  6. GPU组合各个layer的bitmap,将其渲染在屏幕上

iOS Drawing中的CPU和GPU

  1. Core Graphics使用CPU计算bitmap
  2. OpenGL ES使用GPU计算bitmap
  3. 使用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端

  1. 太多的重复绘制:一次绘制过程中,在同一个像素点,绘制多遍
  2. 离屏渲染:CPU和GPU都会出现离屏渲染的情况,这需要额外的内存来存放渲染的结果
  3. 太多的geometry:也就是OpenGL中,太多的三角形
  4. 渲染太大的图片:GPU支持的最大的渲染texture是有限的(通常2048*2048,或者retina的4096*4096),图片像素过大的话,要先在CPU端处理之后,才能交给GPU显示

CPU端

  1. Layout计算:也就是计算每个layer的frame等等属性,这个在使用autolayout的系统上面更加的费时
  2. Lazy View Loading:只有在view第一次出现在屏幕上时,才会去加载view相关的资源(图片等等),所以如果这个资源很大,或者延迟很大(网络获取)的时候,会延缓CPU的操作。这一步的操作包括Layout,Display等步骤
  3. Core Graphics drawing
  4. 图片解压:因为存放的基本都是压缩的图片,但是显示的时候需要全size的图片,所以需要CPU执行图片解压的工作

参考