You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ffmpeg_develop_doc/iOS资料/iOS下的渲染框架.md

9.4 KiB

iOS下的渲染框架

1.图形渲染框架

iOS APP图形渲染框架,APP在显示可视化的图形时,使用到了Core Animation、Core Graphics、Core Image等框架,这些框架在渲染图形时,都需要通过OpenGL ES / Metal来驱动GPU进行渲染与绘制。

img

  • UIKit

UIKit是iOS开发最常用的框架,可以通过设置UIKit组件的布局以及相关属性来绘制界面。 事实上,UIKit自身并不具备在屏幕成像的能力,其主要负责对用户操作事件的响应(UIView继承自UIResponder),事件响应的传递大体是经过逐层的视图树遍历实现的。

  • Core Animation

Core Animation源自于Layer Kit,动画只是Core Animation的冰山一角。 Core Animation是一个复合引擎,其职责是尽可能快地组合屏幕上不同的可视内容,这些可视内容可被分解成独立的图层(即CALayer),这些图层会被存储在一个叫做图层树的体系之中。从本质上而言,CALayer是用户所能在屏幕上看见的一切的基础。

  • Core Graphics

Core Graphics是基于Quartz 的高级绘图引擎,主要用于运行时绘制图像。开发者可以使用此框架来处理基于路径的绘图,转换,颜色管理,离屏渲染,图案,渐变和阴影,图像数据管理,图像创建和图像遮罩以及PDF文档创建,显示和分析。

  • Core Image

Core Image与Core Graphics恰恰相反,Core Graphics用于在运行时创建图像,而Core Image用于处理运行前创建的图像。Core Image框架拥有一系列现成的图像过滤器,能对一寸照的图像进行高效的处理。大部分情况下,Core Image会在GPU中完成工作,如果GPU忙,会使用CPU进行处理。

2.UIView与CALayer的关系

CALayer事实上是用户所能在屏幕上看见的一切的基础。为什么UIKit中的视图能够呈现可视化内容,就是因为UIKit中的每一个UI视图控件其实内部都有一个关联的CALayer,即backing layer。

由于这种一一对应的关系,视图层级有用视图树的树形结构,对应CALayer层级也拥有图层树的树形结构。

其中,视图的职责是创建并管理图层,以确保当子视图在层级关系中添加或被移除时,其关联的图层在图层树中也有相同的操作,即保证视图树和图层树在结构上的一致性。

为什么iOS要基于UIView和CALayer提供两个平行的层级关系呢?

其原因在于要做职责分离,这样也能避免很多重复代码。在iOS和Mac OSX两个平台上,事件和用户交互有很多地方的不同,基于多点触控的用户界面和基于鼠标键盘的交互有着本质的区别,这就是为什么iOS有UIKit和UIView,对应Mac OSX有AppKit和NSView的原因。它们在功能上很相似,但是在实现上有着显著的区别。实际上,这里并不是两个层级关系,而是四个。每一个都扮演着不同的角色。除了视图树图层树,还有呈现树渲染树

那么为什么CALayer可以呈现可视化内容呢?因为CALayer基本等同于一个纹理。纹理是GPU进行图像渲染的重要依据。

图形渲染原理中提到纹理本质上就是一张图片,因此CALayer也包含一个contents属性指向一块缓存区,称为backing store,可以存放位图(Bitmap)。iOS中将该缓存区保存的图片称为寄宿图

img

图形渲染流水线支持从顶点开始进行绘制(在流水线中,顶点会被处理生成纹理),也支持直接使用纹理(图片)进行渲染。相应地,在实际开发中,绘制界面也有两种方式: 一种是手动绘制;另一种是使用图片

对此,iOS中也有两种相应的实现方式:

  • 使用图片:contents image
  • 手动绘制:custom drawing

Contents Image Contents Image是指通过CALayer的contents属性来配置图片。然而,contents属性的类型为id,在这种情况下,可以给contents属性赋予任何值,app仍可以编译通过。但是在实践中,如果contents的值不是CGImage,得到的图层将是空白的。 既然如此,为什么要将contents的属性类型定义为id而非CGImage。因为在Mac OS系统中,该属性对CGImage和NSImage类型的值都起作用,而在iOS系统中,该属性只对CGImage起作用。 本质上,contents属性指向的一块缓存区域,称为backing store,可以存放bitmap数据。 Custom Drawing Custom Drawing是指使用Core Graphics直接绘制寄宿图。实际开发中,一般通过继承UIView并实现-drawRect:方法来自定义绘制。 虽然-drawRect:是一个UIView方法,但事实上都是底层的CALayer完成了重绘工作并保存了产生的图片。 下图所示为drawRect:绘制定义寄宿图的基本原理

img

  • UIView有一个关联图层,即CALayer。
  • CALayer有一个可选的delegate属性,实现了CALayerDelegate协议。UIView作为CALayer的代理实现了CALayerDelegate协议。
  • 当需要重绘时,即调用-drawRect:,CALayer请求其代理给予一个寄宿图来显示。
  • CALayer首先会尝试调用-displayLayer:方法,此时代理可以直接设置contents属性。
- (void)displayLayer:(CALayer *)layer;
  • 如果代理没有实现-displayLayer:方法,CALayer则会尝试调用-drawLayer:inContext:方法。在调用该方法前,CALayer会创建一个空的寄宿图(尺寸由bounds和contentScale决定)和一个Core Graphics的绘制上下文,为绘制寄宿图做准备,作为ctx参数传入。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
  • 最后,有Core Graphics绘制生成的寄宿图会存入backing store。

三个框架间的依赖关系

Core Animation、Core Graphics、Core Image这个三个框架间也存在着依赖关系。

上面提到CALayer是用户所能在屏幕上看到一切的基础。所以Core Graphics、Core Image是需要依赖于CALayer来显示界面的。由于CALayer又是Core Animation框架提供的,所以说Core Graphics、Core Image是依赖于``Core Animation ```的。

上文还提到每一个 UIView 内部都关联一个CALayer图层,即backing layer,每一个CALayer都包含一个content属性指向一块缓存区,即backing store, 里面存放位图(Bitmap)。iOS中将该缓存区保存的图片称为寄宿图。

这个寄宿图有两个设置方式:

直接向content设置CGImage图片,这需要依赖Core Image来提供图片。

通过实现UIView的drawRect方法自定义绘图,这需要借助Core Graphics来绘制图形,再由CALayer生成图片。

img

3.Core Animation 流水线

CALayer是如何调用 GPU 并显示可视化内容的呢?下面我们就需要介绍一下 Core Animation 流水线的工作原理。

img

事实上,app 本身并不负责渲染,渲染则是由一个独立的进程负责,即Render Server进程。

App 通过 IPC 将渲染任务及相关数据提交给Render Server。Render Server处理完数据后,再传递至 GPU。最后由 GPU 调用 iOS 的图像设备进行显示。

Core Animation 流水线的详细过程如下:

首先,由 app 处理事件(Handle Events),如:用户的点击操作,在此过程中 app 可能需要更新视图树,相应地,图层树也会被更新。

其次,app 通过 CPU 完成对显示内容的计算,如:视图的创建、布局计算、图片解码、文本绘制等。在完成对显示内容的计算之后,app 对图层进行打包,并在下一次 RunLoop 时将其发送至Render Server,即完成了一次Commit Transaction操作。

Render Server主要执行 Open GL、Core Graphics 相关程序,并调用 GPU

GPU 则在物理层上完成了对图像的渲染。

最终,GPU 通过 Frame Buffer、视频控制器等相关部件,将图像显示在屏幕上。

对上述步骤进行串联,它们执行所消耗的时间远远超过 16.67 ms,因此为了满足对屏幕的 60 FPS 刷新率的支持,需要将这些步骤进行分解,通过流水线的方式进行并行执行,如下图所示。

img

Commit Transaction

在 Core Animation 流水线中,app 调用Render Server前的最后一步 Commit Transaction 其实可以细分为 4 个步骤:

Layout:主要进行视图构建,包括:LayoutSubviews方法的重载,addSubview:方法填充子视图等。

Display:视图绘制,这里仅仅是绘制寄宿图,该过程使用CPU和内存

Prepare:阶段属于附加步骤,一般处理图像的解码和转换等操作

Commit:主要将图层进行打包,并将它们发送至 Render Server。该过程会递归执行,因为图层和视图都是以树形结构存在。

原文https://zhuanlan.zhihu.com/p/157556221