Texture的异步渲染和布局引擎

探讨的几个点

  • Texture的简介 (What)
  • 为什么要使用Texture (Why)
  • Texture的作者 (Who)
  • Node的异步绘制如何实现 (How)
  • Node的异步渲染(Runloop任务分发)如何实现 (How)
  • Texture的布局引擎 (How)
  • Texture的使用能带来什么收益 (How Much)

texture简介:

Texture(原名AsyncDisplayKit)是FaceBook开源的一款能够保持界面流畅的框架。建立在UIKit之上,可以保持最复杂的用户界面的流畅和响应。(smooth and responsive)


1.x版本

2.x版本

Texture整体架构

Texture

Node:对UIView和CALayer的抽象
Node Containers:node容器,负责加载渲染node
Layout Engineer:node布局

Texture节点容器与UIKit

节点容器

Texture节点子类与UIKit

节点子类

Texture节点子类继承树

继承树

为什么要使用Texture

  • 布局计算、解码、绘制,异步并发执行
  • Runloop任务分发(异步渲染)
  • 声明式布局系统
  • 图层预合成
  • 深度优化列表性能(智能预加载)
Texture:图层预合成

有时一个 layer 会包含很多 sub-layer,而这些 sub-layer 并不需要响应触摸事件,也不需要进行动画和位置调整。ASDK 为此实现了一个被称为 pre-composing 的技术,可以把这些 sub-layer 合成渲染为一张图片。开发时,ASNode 已经替代了 UIView 和 CALayer;直接使用各种 Node 控件并设置为 layer backed 后,ASNode 甚至可以通过预合成来避免创建内部的 UIView 和 CALayer。

通过这种方式,把一个大的层级,通过一个大的绘制方法绘制到一张图上,性能会获得很大提升。CPU 避免了创建 UIKit 对象的资源消耗,GPU 避免了多张 texture 合成和渲染的消耗,更少的 bitmap 也意味着更少的内存占用。


图层预合成1.png

图层预合成2
Texture:智能预加载

所有节点都持有当前界面状态interfaceState,由ASRangeController控制属性值更新,在所有节点容器的内部创建和维护。


智能预加载
  • Preload:节点还不可见,这时节点收集外部源(API或磁盘数据)
  • Display:节点开始渲染,包括文本的光栅化以及图像解码等
  • Visible:节点可见,在屏幕上至少拥有一个像素

Texture的作者

Scott Goodson

ASDK 的作者是 Scott Goodson ,他曾经在苹果工作,负责 iOS 的一些内置应用的开发,比如股票、计算器、地图、钟表、设置、Safari 等,当然他也参与了 UIKit framework 的开发。后来他加入 Facebook 后,负责 Paper 的开发,创建并开源了 AsyncDisplayKit。目前他在 Pinterest 和 Instagram 负责 iOS 开发和用户体验的提升等工作。

Node的异步绘制如何实现

UIKit的绘制机制图解

CALayer的display方法由系统调用,用来更新layer的内容,如果layer有delegate对象,那么display方法将尝试调用delegate的displayLayer:方法来更新layer的内容。如果delegate没有实现displayLayer:方法,则这个方法会创建一个backing store来保存原来的内容,然后调用layer的drawInContext:方法来填充back store。最后以新的backing store替换layer的先前内容达到更新layer的目的。通常UIKit中CALayer的delegate是UIView对象。

有两种方式来自定义CALayer的内容,一种是直接设置CALayer的contents属性来创建寄宿图;另一种是通过实现CALayer的delegate方法,可以用于直接对CALayer进行操作。

Node的异步绘制

UIKit

Texture

ASDisplayNode是整个Texture的基石,也是页面异步绘制的核心,其持有UIView和CALayer两种对象,均由node自己生成并管理。


异步绘制时序图

_ASDisplayLayer通过重写了CALayer的display方法来自定义CALayer的寄宿图属性。_ASDisplayLayer与ASDisplayNode的关系类似于CALayer与UIView的关系。

Node的异步绘制流程

1.获取node的displayBlock,也就是负责根据node的视图层级得到需要显示的内容的绘制任务。
2.生成Node绘制完成后的回调completeBlock。
3.根据displaysAsynchronously属性来判断是否需要异步绘制,如果是异步的,则将displayBlock提交至_ASAsyncTransaction中,否则立即执行displayBlock。

tips

Node的异步渲染

1.寻找Layer相关的ASAsyncTransaction。
2.将displayBlock和completeBlock添加至ASAsyncTransaction。
3.利用ASAsyncTransactionQueue进行调度。
4.mainRunloop在开始sleep和exit的时候提交ASAsyncTransaction。
5.ASAsyncTransaction在提交的时候回调completeBlock,完成layer寄宿图的赋值。
<ps:displayBlock执行不在主线程,completeBlock执行在主线程!>

底层信号驱动原理

信号驱动

iOS的显示系统由VSync信号驱动的,VSync信号由硬件时钟生成,每秒钟发出60次。iOS图形服务接收到VSync信号后,会通过IPC通知到APP内。APP的Runloop在启动后会注册对应CFRunloopSource通过mach_port接收传过来的时钟信号通知,随后source的回调会驱动整个App的动画与显示。

Runloop任务分发 ->CoreAnimation

CA在Runloop中注册了一个Observer,监听了BeforeWaiting和Exit事件,优先级低于其他Observer。当一个触摸事件到来时,Runloop被唤醒,App中的代码会执行一些操作,比如创建和调整视图层级、设置UIView的frame、修改CALayer的透明度、为视图添加一个动画;这些操作最终会被CALayer捕获,并通过CATransaction提交到一个中间状态去。当上面的所有操作结束后,Runloop即将进入休眠(或者退出)时,关注该事件的Observer都会得到通知。这时CA注册的Observer就会在回调中,把所有的中间状态合并提交到GPU去显示;如果此处有动画,CA会通过CADisplayLink等机制多次触发相关流程。

Runloop任务分发->Texture

Texture在此处模拟了Core Animation的这个机制:所有针对ASNode的修改和提交,总有些任务是必须放入主线程执行的。当出现这种任务时,ASNode会把任务用ASAsyncTransaction(Group)封装并提交到一个全局的容器去。Texture也在Runloop中注册了一个Observer,监视的事件和CA一样,但优先级比CA要低。当Runloop进入休眠前、CA处理完事件后,Texture就会执行该loop内提交的所有任务。通过这种机制,Texture可以在合适的机会把异步、并发的操作同步到主线程去,并且能获得不错的性能。

Texture布局引擎

相对于AutoLayout

UIKit AutoLayout 在复杂的视图结构中,计算量会呈指数级增长,Texture的布局方案相对AutoLayout有以下优点:

  • 快:Texture的布局计算和手写frame一样快
  • 异步和并发:布局可以在后台线程上计算
  • 声明式渲染:布局使用不可变的数据结构声明,实现一个layout视角从专注view之间的距离和约束,转变成划分和制定不同view子域的布局规则,抽象层级变高,使得布局代码更容易开发、维护
  • 可缓存:如果布局是不变的,自动在后台预先计算并缓存
  • 可拓展:在不同的类中使用相同的布局会变得很方便

Texture的布局系统

Texture自己定义了一套强大的automatic layout布局系统,这套布局系统基于CSS的Box Model,通过提出LayoutSpec概念,使得我们可以通过声明式的方法来定义布局。


layoutTable

<LayoutSpec> :一种特殊的layoutTable,与node不同的是,它本质只是内存中的数据结构,用以辅助view的位置计算,绘制时不需要view来占位或者承载子元素。


布局系统

Texture布局规则&布局元素

  • 布局规则:充当LayoutElements的容器,通过多个LayoutElements之间的关联,完成LayoutElements的位置排列,继承自ASLayoutSpec。
  • 布局元素:所有的ASDisplayNode和ASLayoutSpec都遵守<ASLayoutElement>协议,可以通过两个Nodes和其他的LayoutSpecs,生成或者组合一个新的LayoutSpecs。<ASLayoutElement>协议及LayoutSpecs有一些属性用于创建非常复杂的布局。


    布局规则

Texture布局流程图

布局流程

Texture布局示例

示例

Texture布局调试

在任何ASDisplayNode或ASLayoutSpec上调用-asciiArtString都会返回该对象及其子项的字符图,也可以设置.debugName这样也会包含在字符图中。


调试

还可以在任何ASLayoutElement,比如Node和LayoutSpec上打印样式对象,调试.size属性。


console

声明式布局Demo链接:

https://ysw-hello@github.com/ysw-hello/TextureLayoutDemo.git

Texture所能带来的收益

  • 异步绘制、异步渲染通过Runloop任务分发,优化复杂界面的主线程卡顿现象。
  • 图层预合成、智能预加载的机制,对列表进行深度优化,使得体验与性能得到进一步的提升。
  • 声明式布局方式,FlexBox布局特点,给iOS原生开发的布局模式带来一种新的布局思维,很新颖,很有特点。

Texture相关参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容