离屏渲染&图形渲染的流程

图形渲染的流程

iOS图形渲染的技术栈

请添加图片描述

APP通过Core Graphics ,Core Animation ,Core Image 等框架进行可视化的绘制,这些框架相互依赖,并通过OpenGL ES /Metal 调度GPU进行视图的渲染最终显示在屏幕上。

UIKit
1.UIKitk框架提供一系列的Class(类)来建立和管理iPhone OS应用程序的用户界面( UI )接口、应用程序对象、事件控制、绘图模型、窗口、视图和用于控制触摸屏等的接口。

2.开发者一般直接使用UIKit进行视图的布局和对用户事件的响应,事件响应的传递是通过视图树逐级遍历实现的。

Core Animation

1.CoreAnimation 源自于 Layer Kit ,是苹果提供的一套基于绘图的动画框架

2.Core Animation是一个复合引擎,它的职责就是尽可能快地组合屏幕上不同的可 视内容,这个内容是被分解成独立的图层,存储在一个叫做图层树的体系之中。于 是这个树形成了UIKit以及在iOS应用程序当中你所能在屏幕上看见的一切的基础。

Core Graphics

1.Core Graphics是Quartz 2D的一个高级绘图引擎,常用与iOS,tvOS,macOS的图形绘制应用开发。Core Graphics是对底层C语言的一个简单封装,其中提供大量的低层次,轻量级的2D渲染API。

2.Core GraphicsAPI简单易于使用,并且提供了强大的功能访问,如透明层,基于路径的涂层绘制,屏幕外呈现,高级色彩管理,抗锯齿已经PDF文档的生成解析操作

3.Core Graphics是创建运行时图形,可以对图片进行解压。

Core Image

1.Core Image是IOS5中新加入的一个框架,里面提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。还提供了很多强大的滤镜,可以实现你想要的效果。

  1. 相对于Core Graphics,Core Image是处理运行前的创建的图像。Core Image会在GPU中完成工作,如果GPU忙,会使用CPU进行处理。

UIView和CALayer 千丝万缕的关系。

1.iOS屏幕显示的所有视图都是依赖CALayer 的。每个UIKit控件下都有一个关联的CALayer,即backing layer。视图层级拥有 视图树 的树形结构,对应 CALayer 层级也拥有 图层树 的树形结构。视图树中添加或者删除视图,相对应的图层树也会添加或者删除图层保证视图树和图层树一一对应。

请添加图片描述

2.视图树和图层树是一一对应,我们为什么要保留两个这样的树呢?其原因是 职责分离,在 iOS 和 Mac OS X 两个平台上,事件和用户交互有很多地方的不同,基于多点触控的用户界面和基于鼠标键盘的交互有着本质的区别,这就是为什么 iOS 有 UIKit 和 UIView,对应 Mac OS X 有 AppKit 和 NSView 的原因。它们在功能上很相似,但是在实现上有着显著的区别。

3.视图树 和 图层树,还有 呈现树渲染树,每一个都扮演着不同的角色。

CALayer

1.CALayer是一个对象,该对象管理基于图像的内容,并允许您对该内容执行动画。

  1. CALayer 中包含一个 contents 属性指向一块缓存区,称为 backing store,可以存放位图(Bitmap)。iOS 中将该缓存区保存的图片称为 寄宿图。

3.在图形渲染中有两种方式一是顶点进行绘制,二是通过纹理。在绘制图形中也有两种方式,一是手动绘制(对应图形渲染中的顶点绘制),二是使用图片(图形渲染中的通过纹理)

请添加图片描述

Contents Image

1.Contents Image 是指通过 CALayer 的 contents 属性来配置图片。然而,contents 属性的类型为 id。在这种情况下,可以给 contents 属性赋予任何值,app 仍可以编译通过。但是在实践中,如果 content 的值不是 CGImage ,得到的图层将是空白的。因为在 Mac OS 系统中,该属性对 CGImage 和 NSImage 类型的值都起作用,而在 iOS 系统中,该属性只对 CGImage 起作用。

2.CALayer 的contents 属性指向的一块缓存区域,称为 backing store,可以存放 bitmap 数据

Custom Drawing

1.Custom Drawing是使用Core Graphics 直接绘制寄宿图。在开发中我们一般是通过实现UIView的- (void)drawRect:完成绘制的

2.-drawRect: 是一个 UIView 方法,但事实上都是底层的 CALayer 完成了重绘工作并保存了产生的图片。下图所示为 -drawRect: 绘制定义寄宿图的基本原理

请添加图片描述

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

Core Animation 流水线

请添加图片描述

1.app 本身并不负责渲染,渲染则是由一个独立的进程负责,即 Render Server 进程.App 通过 IPC 将渲染任务及相关数据提交给 Render Server。Render Server 处理完数据后,再传递至 GPU。最后由 GPU 调用 iOS 的图像设备进行显示。

2.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 刷新率的支持,需要将这些步骤进行分解,通过流水线的方式进行并行执行,如下图所示。

请添加图片描述

Commit Transaction

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

Layout

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

Display

Display 阶段主要进行视图绘制,这里仅仅是设置最要成像的图元数据。重载视图的 drawRect: 方法可以自定义 UIView 的显示,其原理是在 drawRect: 方法内部绘制寄宿图,该过程使用 CPU 和内存。

Prepare

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

Commit

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

关于iOS原生渲染的整体流程,我也画了一张图:

请添加图片描述

什么是离屏渲染

UIView和CALayer关系

UIView继承自UIResponder,可以处理系统传递过来的事件,如:UIApplication、UIViewController、UIView,以及所有从UIView派生出来的UIKit类。每个UIView内部都有一个CALayer提供内容的绘制和显示,并且作为内部RootLayer的代理视图。

CALayer继承自NSObject类,负责显示UIView提供的内容contents。CALayer有三个视觉元素:背景色、内容和边框,其中,内容的本质是一个CGImage。

下图为CALayer的结构图:

请添加图片描述

界面渲染过程

RunLoop有一个60fps的回调,即每16.7ms绘制一次屏幕,所以view的绘制必须在这个时间内完成,view内容的绘制是CPU的工作,然后把绘制的内容交给GPU渲染,包括多个View的拼接(Compositing)、纹理的渲染(Texture)等等,最后显示在屏幕上。但是,如果无法是16.7ms内完成绘制,就会出现丢帧的问题,一般情况下,如果帧率保证在30fps以上,界面卡顿效果不明显,那么就需要在33.4ms内完成View的绘制,而低于这个帧率,就会产生卡顿的效果,影响体验。

渲染的过程如下:

  • UIView的layer层有一个content,指向一块缓存,即backing store
  • UIView绘制时,会调用drawRect方法,通过context将数据写入backing store
  • 在backing store写完后,通过render server交给GPU去渲染,将backing
    store中的bitmap数据显示在屏幕上

请添加图片描述

在使用圆角、阴影和遮罩等视图功能的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所有就需要在屏幕外的上下文中渲染,即离屏渲染。

我们知道iOS的系统中,非离屏渲染图层的显示,是经过CPU计算处理,通过GPU渲染,然后将渲染结果存放在帧缓存区中,然后才会被加载显示到屏幕上,大致流程如下:

请添加图片描述

但是,如果我们要显示的效果是,需要多图层叠加、裁剪、合并得到的最终效果时,也就是GPU需要将多次数据结合处理才可以得到一个最终要显示的效果,由于我们的帧缓存区的数据是放如一帧就会读取一帧显示,所以我们只能在最终要显示的结果放入帧缓存区,如果在此之前,需要多次处理渲染结果的叠加处理,我们就需要用到离屏渲染,也就是先将中间的渲染图层,存放在离屏缓存区,最后结合得到最终显示效果,再放入帧缓存区,其流程如下:

请添加图片描述

圆角未触发离屏渲染

view 和 imageView 都是一个图层就能完成的效果,也就是GPU只需要一次就能渲染完成 没有触发离屏渲染。

backgroundColor + 中间的contents + 边框 合并裁剪得到的结果, 它们都操作了会触发离屏渲染

离屏渲染卡顿原因

  • 开辟临时缓存空间
  • 上下文切换,上下文对象比较大,切换操作会带来一定的性能消耗
  • 内存拷贝

设置了以下属性时,就会触发离屏绘制:

  • shouldRasterize(光栅化)
  • masks(遮罩)
  • shadows(阴影)
  • edge antialiasing(抗锯齿)
  • group opacity(不透明)
  • 复杂形状设置圆角等
  • 渐变

屏幕渲染类型

CPU计算好显示内容提交到GPU,GPU渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。

具体链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值