嘗試和大家一起探討以下問題:
- view繪制渲染機制和runloop什么關系?
- 所謂的列表卡頓,到底是什么原因引發的?
- 我們經常在drawrect方法里繪制代碼,但該方法是誰調用的 何時調用的?
- drawrect方法內為何第一行代碼往往要獲取圖形的上下文?
- layer的代理必須是view嗎,可以是vc嗎,為何CALayerDelegate 不能主動遵循?
- view繪制機制和CPU之間關系?
- view渲染機制和GPU之間關系?
- 所有的切圓角都很浪費性能嗎?
- 離屏渲染很nb嗎?
- 那些繪制API都是哪個類提供的 我如何系統的學習它?
- 如何優化CPU /GPU使用率?
view繪制渲染機制和runloop什么關系?
代碼示例
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYYView *view = [[ZYYView alloc] init];
view.backgroundColor = [UIColor whiteColor];
view.bounds = CGRectMake(0, 0, 100, 100);
view.center = CGPointMake(100, 100);
[self.view addSubview:view];
}
@end
@implementation ZYYView
- (void)drawRect:(CGRect)rect {
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,200));
CGContextSetRGBFillColor(con, 0, 0, 1, 1);
CGContextFillPath(con);
}
@end
堆棧展示
底層原理
當在操作 UI 時,比如改變了 Frame、更新了 UIView/CALayer 的層次時,或者手動調用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,這個 UIView/CALayer 就被標記為待處理,并被提交到一個全局的容器去。
蘋果注冊了一個 Observer 監聽 BeforeWaiting(即將進入休眠) 和 Exit (即將退出Loop) 事件,回調去執行一個很長的函數:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。這個函數里會遍歷所有待處理的 UIView/CAlayer 以執行實際的繪制和調整,并更新 UI 界面。
這個函數內部的調用棧大概是這樣的:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()
QuartzCore:CA::Transaction::observer_callback:
CA::Transaction::commit();
CA::Context::commit_transaction();
CA::Layer::layout_and_display_if_needed();
CA::Layer::layout_if_needed();
[CALayer layoutSublayers];
[UIView layoutSubviews];
CA::Layer::display_if_needed();
[CALayer display];
[UIView drawRect];
我們上圖的堆棧信息 截圖 ,看到巴拉巴拉一大堆調用堆棧信息,其實這就是個函數做的孽 。如何不能理解,那直接看下面的流程圖吧。
流程圖
<svg height="1359.75" version="1.1" width="1014.421875" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.671875px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,305.0547,18.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">程序啟動</tspan></text></svg> <text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,268.5547,125.5)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">UIApplicationMain()</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,162.6172,232.6875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">主線程:我是UI線程不能停,Runloop來和我一起吧。</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,241.0938,339.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">MainRunloop create and run </tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,154.3359,447.0625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">MainRunloop:我想睡覺了,observer,你那邊有事嗎?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op5t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,554.25)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">observer:我去檢查一下_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op6t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,205.9922,661.4375)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">我去看看 圖層樹中有沒有待處理的對象</tspan></text><path fill="#ffffff" stroke="#000000" d="M33.75,16.875L0,33.75L67.5,67.5L135,33.75L67.5,0L0,33.75" stroke-width="2" font-family="sans-serif" font-weight="normal" id="cond" class="flowchart" transform="matrix(1,0,0,1,277.5547,754.3125)" style="font-family: sans-serif; font-weight: normal;"></path><text x="38.75" y="33.75" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="condt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,277.5547,754.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">有沒有?</tspan><tspan dy="18" x="38.75"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op7t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,162.4609,890.125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">CPU:我在更新圖層樹,一會交給Core Animation運走</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op8t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,118.6328,997.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">Core Animation:把待處理的圖層對象 通過IPC發送到渲染服務進程</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op9t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,228.4844,1104.5)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">GPU:渲染服務進程開始渲染工作</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op10t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,205.8906,1211.6875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">GPU:Compositing\Offscreen Rendering </tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op11t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,297.5547,1318.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">展示到屏幕 </tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,602.7969,768.625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">告訴runloop 讓它睡會吧。有東西,我在叫你observer。</tspan></text><path fill="none" stroke="#000000" d="M345.0546875,57.1875C345.0546875,57.1875,345.0546875,109.31428690254688,345.0546875,122.49869882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,164.375C345.0546875,164.375,345.0546875,216.50178690254688,345.0546875,229.68619882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,271.5625C345.0546875,271.5625,345.0546875,323.6892869025469,345.0546875,336.87369882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,378.75C345.0546875,378.75,345.0546875,430.8767869025469,345.0546875,444.06119882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,485.9375C345.0546875,485.9375,345.0546875,538.0642869025469,345.0546875,551.2486988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,593.125C345.0546875,593.125,345.0546875,645.2517869025469,345.0546875,658.4361988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,700.3125C345.0546875,700.3125,345.0546875,739.9665999412537,345.0546875,751.3129390846007" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,821.8125C345.0546875,821.8125,345.0546875,873.9392869025469,345.0546875,887.1236988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="350.0546875" y="831.8125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">yes</tspan></text><path fill="none" stroke="#000000" d="M412.5546875,788.0625C412.5546875,788.0625,575.5197486574762,788.0625,599.7894477068073,788.0625" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="417.5546875" y="778.0625" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">no</tspan></text><path fill="none" stroke="#000000" d="M345.0546875,929C345.0546875,929,345.0546875,981.1267869025469,345.0546875,994.3111988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1036.1875C345.0546875,1036.1875,345.0546875,1088.3142869025469,345.0546875,1101.4986988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1143.375C345.0546875,1143.375,345.0546875,1195.5017869025469,345.0546875,1208.6861988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1250.5625C345.0546875,1250.5625,345.0546875,1302.6892869025469,345.0546875,1315.8736988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path>
所謂的列表卡頓,到底是什么原因引發的?
iOS的mainRunloop是一個60fps的回調,也就是說每16.7ms會繪制一次屏幕,這個時間段內要完成view的緩沖區創建,view內容的繪制(如果重寫了drawRect),這些CPU的工作。然后將這個緩沖區交給GPU渲染,這個過程又包括多個view的拼接(compositing),紋理的渲染(Texture)等,最終顯示在屏幕上。整個過程就是我們上面畫的流程圖。 因此,如果在16.7ms內完不成這些操作,比如,CPU做了太多的工作,或者view層次過于多,圖片過于大,導致GPU壓力太大,就會導致“卡”的現象,也就是丟幀
我們經常在drawrect方法里繪制代碼,但該方法是誰調用的 何時調用的?
產品繪圖需求
首先我們假設有這樣一個需求:實現下面的橢圓效果:
代碼示例
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYYView *view = [[ZYYView alloc] init];
view.backgroundColor = [UIColor whiteColor];
view.bounds = CGRectMake(0, 0, 100, 100);
view.center = CGPointMake(100, 100);
[self.view addSubview:view];
}
@end
@implementation ZYYView
- (void)drawRect:(CGRect)rect {
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,200));
CGContextSetRGBFillColor(con, 0, 0, 1, 1);
CGContextFillPath(con);
}
@end
堆棧展示
底層原理
1、 在[ZYYView drawRect:] 方法之前,先調用了 [UIView(CALayerDelegate) drawLayer:inContext:] 和 [CALayer drawInContext:]
2、如果 [self.view addSubview:view]; 被注銷掉 則 drawRect 不執行??梢钥隙?drawRect
方法是由 addSubview 函數觸發的。
流程圖
<svg height="323.5" version="1.1" width="355.578125" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.4375px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,73.2344,4)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[self.view addSubview:view]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,82.5313,96.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[CALayer drawInContext:]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,189.75)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[UIView(CALayerDelegate) drawLayer:inContext:]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="et" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,99.9531,282.625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[ZYYView drawRect:]</tspan></text></svg>
drawrect方法內為何第一行代碼總要獲取圖形的上下文
代碼示例
CGContextRef con = UIGraphicsGetCurrentContext();
堆棧展示
底層原理
每一個UIView都有一個layer,每一個layer都有個content,這個content指向的是一塊緩存,叫做backing store
當UIView被繪制時(從 CA::Transaction::commit:以后),CPU執行drawRect,通過context將數據寫入backing store
當backing store寫完后,通過render server交給GPU去渲染,將backing store中的bitmap數據顯示在屏幕上
所以在 drawRect 方法中 要首先獲取 context
layer的代理必須是view嗎,可以是vc嗎?為何CALayerDelegate 不能主動遵循?
代碼示例
代碼1
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYYLayer *layer = [ZYYLayer layer];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(100, 100);
[layer setNeedsDisplay];
[self.view.layer addSublayer:layer];
}
@end
@implementation ZYYLayer
- (void)drawInContext:(CGContextRef)ctx {
CGContextAddEllipseInRect(ctx, CGRectMake(0,0,100,200));
CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
CGContextFillPath(ctx);
}
@end
代碼二
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *layer = [CALayer layer];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(100, 100);
layer.delegate = self;
[layer setNeedsDisplay];
[self.view.layer addSublayer:layer];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGContextAddEllipseInRect(ctx, CGRectMake(0,0,100,200));
CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
CGContextFillPath(ctx);
}
@end
圖標展示
綜合以上2種不同的繪制函數加上uiview下的drawrect方法 一起區別 :
編號 | 所在的類或類別 | 方法 | 出現范圍 | 可以使用的API | viewDidLoad | 優先級 |
---|---|---|---|---|---|---|
1 | UIView(UIViewRendering) | drawRect | 自定義view類 | UIkit 、CoreGraphics | 3 | |
2 | CALayer | drawInContext | 自定義layer類 | CoreGraphics | [layer setNeedsDisplay] | 1 |
3 | NSObject (CALayerDelegate) | drawLayer:inContext | vc類、自定義layer、view類 | UIkit、CoreGraphics | [layer setNeedsDisplay] layer.delegate = self | 2 |
底層原理
不能再將某個UIView設置為CALayer的delegate,因為UIView對象已經是它內部根層的delegate,再次設置為其他層的delegate就會出問題。
在設置代理的時候,它并不要求我們遵守協議,說明這個方法為非正式協議,就不需要再額外的顯示遵守協議了
view繪制機制和CPU之間關系
創建對象
性能瓶頸:
創建對象會分配內存,對象過多,比較消耗 CPU 資源 。
優化方案:
1、盡量用輕量的對象代替重量的對象,可以對性能有所優化。比如 CALayer 比 UIView 要輕量,如果不需要響應觸摸事件,用 CALayer 顯示會更加合適。如果對象不涉及 UI 操作,則盡量放到后臺線程去創建,但如果是包含了 CALayer 的控件,都只能在主線程創建和操作。
2、通過 Storyboard 創建視圖對象時,其資源消耗會比直接通過代碼創建對象要大非常多。
3、使用懶加載,盡量推遲對象創建的時間,并把對象的創建分散到多個任務中去。
調整對象
調整對象視圖層級
性能瓶頸:
對象的調整也經常是消耗 CPU 資源的地方。視圖層次調整時,UIView、CALayer 之間會出現很多方法調用與通知。
優化方案:
盡量的避免或者減少調整視圖層次、添加和移除視圖。
調整對象布局計算
性能瓶頸:視圖布局的計算是 App 中最為常見的消耗 CPU 資源的地方
優化方案:不論通過何種技術對視圖進行布局,其最終都會落到對 UIView.frame/bounds/center 等屬性的調整上。對這些屬性的調整非常消耗資源,所以盡量提前計算好布局,如果一次性可以調整好對應屬性,就不要多次、頻繁的計算和調整這些屬性。
調整對象文本計算
性能瓶頸:如果一個界面中包含大量文本(比如微博微信朋友圈等),文本的寬高計算會占用很大一部分資源。
優化方案:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本,記住放到后臺線程進行以避免阻塞主線程。
圖像的繪制
流程圖
<svg height="4560.33984375" version="1.1" width="1263.32421875" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.078125px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,325.5547,175.5313)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">自定義ZYYView</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,330.9375,439.9375)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">初始化、坐標</tspan></text><text x="70.923828125" y="65.923828125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,254.0898,657.8574)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">確認 alloc setFrame?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,278.1328,1015.2363)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[self.view addSubview:view];</tspan></text></svg> <path fill="#ffffff" stroke="#000000" d="M60.603515625,30.3017578125L0,60.603515625L121.20703125,121.20703125L242.4140625,60.603515625L121.20703125,0L0,60.603515625" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c2" class="flowchart" transform="matrix(1,0,0,1,264.7305,1238.4766)" style="font-family: sans-serif; font-weight: normal;"></path><text x="65.603515625" y="60.603515625" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,264.7305,1238.4766)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.337890625">確認 addsubview ?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,196.6172,1585.2148)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">(隱式) 此view的layer的CALayerDelegate設置成此view</tspan></text><text x="20" y="28.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,127.1094,1840.6211)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="-3.671875">1、首先CPU會為layer分配一塊內存用來繪制bitmap,叫做backing store</tspan><tspan dy="18" x="20">2、layer創建指向這塊bitmap緩沖區的指針,叫做CGContextRef</tspan><tspan dy="18" x="20"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,224.4375,2123.0273)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">調用此view的self.layer的drawInContext:方法</tspan><tspan dy="18" x="10"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op5t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,221.6016,2387.4336)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執行 - (void)drawInContext:(CGContextRef)ctx</tspan></text><path fill="#ffffff" stroke="#000000" d="M190.96875,95.484375L0,190.96875L381.9375,381.9375L763.875,190.96875L381.9375,0L0,190.96875" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c3" class="flowchart" transform="matrix(1,0,0,1,4,2480.3086)" style="font-family: sans-serif; font-weight: normal;"></path><text x="195.96875" y="190.96875" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,2480.3086)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">if([self.delegate responseToSelector:@selector(drawLayer:inContext:)])</tspan><tspan dy="18" x="195.96875"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op6t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,150.5156,3087.7773)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執行(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx</tspan></text><path fill="#ffffff" stroke="#000000" d="M129.09375,64.546875L0,129.09375L258.1875,258.1875L516.375,129.09375L258.1875,0L0,129.09375" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c4" class="flowchart" transform="matrix(1,0,0,1,127.75,3242.5273)" style="font-family: sans-serif; font-weight: normal;"></path><text x="134.09375" y="129.09375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,127.75,3242.5273)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">是否調用[super drawLayer:layer inContext:ctx] </tspan><tspan dy="18" x="134.09375"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op7t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,259.0391,3726.2461)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執行 - (void)drawRect:(CGRect)rect</tspan><tspan dy="18" x="10"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op8t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,181.6406,3990.6523)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">使用 UIkit繪制API 或者 CoreGraphics繪制API,繪制bitmap</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op9t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,256.6641,4255.0586)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">將layer的content指向生成的bitmap</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="et" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,295.6328,4519.4648)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">交付layer的content屬性</tspan><tspan dy="18" x="10"></tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,853.668,1279.6426)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">drawrect 方法不調用</tspan><tspan dy="18" x="20"></tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,1082.4961,704.3438)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">drawrect 方法不調用</tspan><tspan dy="18" x="20"></tspan></text><path fill="none" stroke="#000000" d="M385.9375,214.40625C385.9375,214.40625,385.9375,410.25966608710587,385.9375,436.9408687669411" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,478.8125C385.9375,478.8125,385.9375,631.4584060381167,385.9375,654.8621240537263" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,789.705078125C385.9375,789.705078125,385.9375,985.5584942121059,385.9375,1012.2396968919411" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="799.705078125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">yes</tspan></text><path fill="none" stroke="#000000" d="M517.78515625,723.78125C517.78515625,723.78125,1035.272804287728,723.78125,1079.4917195253267,723.78125" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="522.78515625" y="713.78125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,1054.111328125C385.9375,1054.111328125,385.9375,1211.6257607354783,385.9375,1235.4682773903846" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,1359.68359375C385.9375,1359.68359375,385.9375,1555.5370098371059,385.9375,1582.218212516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="1369.68359375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M507.14453125,1299.080078125C507.14453125,1299.080078125,816.8033232688904,1299.080078125,850.6722536459565,1299.080078125" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="512.14453125" y="1289.080078125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,1624.08984375C385.9375,1624.08984375,385.9375,1811.5368828047067,385.9375,1837.6220420481595" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,1897.49609375C385.9375,1897.49609375,385.9375,2093.349509837106,385.9375,2120.030712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2161.90234375C385.9375,2161.90234375,385.9375,2357.755759837106,385.9375,2384.436962516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2426.30859375C385.9375,2426.30859375,385.9375,2465.9626936912537,385.9375,2477.3090328346007" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2862.24609375C385.9375,2862.24609375,385.9375,3058.099509837106,385.9375,3084.780712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="2872.24609375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M767.875,2671.27734375C767.875,2671.27734375,792.875,2671.27734375,792.875,2671.27734375C792.875,2671.27734375,792.875,4494.46484375,792.875,4494.46484375C792.875,4494.46484375,385.9375,4494.46484375,385.9375,4494.46484375C385.9375,4494.46484375,385.9375,4509.83828830719,385.9375,4516.474091524258" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="772.875" y="2661.27734375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,3126.65234375C385.9375,3126.65234375,385.9375,3221.3582960292697,385.9375,3239.531850348485" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,3500.71484375C385.9375,3500.71484375,385.9375,3696.568259837106,385.9375,3723.249462516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="3510.71484375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M644.125,3371.62109375C644.125,3371.62109375,669.125,3371.62109375,669.125,3371.62109375C669.125,3371.62109375,669.125,4494.46484375,669.125,4494.46484375C669.125,4494.46484375,385.9375,4494.46484375,385.9375,4494.46484375C385.9375,4494.46484375,385.9375,4509.83828830719,385.9375,4516.474091524258" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="649.125" y="3361.62109375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,3765.12109375C385.9375,3765.12109375,385.9375,3960.974509837106,385.9375,3987.655712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,4029.52734375C385.9375,4029.52734375,385.9375,4225.380759837106,385.9375,4252.061962516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,4293.93359375C385.9375,4293.93359375,385.9375,4489.787009837106,385.9375,4516.468212516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path>
底層原理
我們回過頭思考 圖形的上下文 CGContextRef的創建歷程。
? addsubview 的時候 觸發的
? CPU會為layer分配一塊內存用來繪制bitmap,叫做backing store
? layer創建指向這塊bitmap緩沖區的指針,叫做CGContextRef
? 通過CoreGraphic的api,也叫Quartz2D,繪制bitmap
? 將layer的content指向生成的bitmap
其實 CGContextRef 的創建過程 就是CPU的工作過程
CPU 將view變成了bitmap 完成自己工作,剩下就是GPU的工作了。
view渲染機制和GPU之間關系
GPU功能
GPU處理的單位是Texture
基本上我們控制GPU都是通過OpenGL來完成的,但是從bitmap到Texture之間需要一座橋梁,Core Animation正好充當了這個角色:
Core Animation對OpenGL的api有一層封裝,當我們的要渲染的layer已經有了bitmap content的時候,這個content一般來說是一個CGImageRef,CoreAnimation會創建一個OpenGL的Texture并將CGImageRef(bitmap)和這個Texture綁定,通過TextureID來標識。
這個對應關系建立起來之后,剩下的任務就是GPU如何將Texture渲染到屏幕上了。
GPU工作模式:
整個過程也就是一件事:CPU將準備好的bitmap放到RAM里,GPU去搬這快內存到VRAM中處理。
而這個過程GPU所能承受的極限大概在16.7ms完成一幀的處理,所以最開始提到的60fps其實就是GPU能處理的最高頻率。
GPU性能瓶頸
因此,GPU的挑戰有兩個:
? 將數據從RAM搬到VRAM中
? 將Texture渲染到屏幕上
這兩個中瓶頸基本在第二點上。渲染Texture基本要處理這么幾個問題:
Compositing:
Compositing是指將多個紋理拼到一起的過程,對應UIKit,是指處理多個view合到一起的情況,如
[self.view addsubview : subview]
如果view之間沒有疊加,那么GPU只需要做普通渲染即可。 如果多個view之間有疊加部分,GPU需要做blending。
加入兩個view大小相同,一個疊加在另一個上面,那么計算公式如下:
R = S+D*(1-Sa)
R: 為最終的像素值
S: 代表 上面的Texture(Top Texture)
D: 代表下面的Texture(lower Texture)
Sa代表Texture的alpha值。
其中S,D都已經pre-multiplied各自的alpha值。
假如Top Texture(上層view)的alpha值為1,即不透明。那么它會遮住下層的Texture。即,R = S。是合理的。 假如Top Texture(上層view)的alpha值為0.5,S 為 (1,0,0),乘以alpha后為(0.5,0,0)。D為(0,0,1)。 得到的R為(0.5,0,0.5)。
基本上每個像素點都需要這么計算一次。
因此,view的層級很復雜,或者view都是半透明的(alpha值不為1)都會帶來GPU額外的計算工作。
應用應當盡量減少視圖數量和層次,并在不透明的視圖里標明 opaque 屬性以避免無用的 Alpha 通道合成。
Size
這個問題,主要是處理image帶來的,假如內存里有一張400x400的圖片,要放到100x100的imageview里,如果不做任何處理,直接丟進去,問題就大了,這意味著,GPU需要對大圖進行縮放到小的區域顯示,需要做像素點的sampling,這種smapling的代價很高,又需要兼顧pixel alignment。計算量會飆升。
shouldRasterize
其中shouldRasterize(光柵化)是比較特別的一種:
光柵化概念:將圖轉化為一個個柵格組成的圖象。
光柵化特點:每個元素對應幀緩沖區中的一像素。
shouldRasterize = YES在其他屬性觸發離屏渲染的同時,會將光柵化后的內容緩存起來,如果對應的layer及其sublayers沒有發生改變,在下一幀的時候可以直接復用。shouldRasterize = YES,這將隱式的創建一個位圖,各種陰影遮罩等效果也會保存到位圖中并緩存起來,從而減少渲染的頻度(不是矢量圖)。
相當于光柵化是把GPU的操作轉到CPU上了,生成位圖緩存,直接讀取復用。
當你使用光柵化時,你可以開啟“Color Hits Green and Misses Red”來檢查該場景下光柵化操作是否是一個好的選擇。綠色表示緩存被復用,紅色表示緩存在被重復創建。
如果光柵化的層變紅得太頻繁那么光柵化對優化可能沒有多少用處。位圖緩存從內存中刪除又重新創建得太過頻繁,紅色表明緩存重建得太遲??梢葬槍π缘倪x擇某個較小而較深的層結構進行光柵化,來嘗試減少渲染時間。
注意:
對于經常變動的內容,這個時候不要開啟,否則會造成性能的浪費
例如我們日程經常打交道的TableViewCell,因為TableViewCell的重繪是很頻繁的(因為Cell的復用),如果Cell的內容不斷變化,則Cell需要不斷重繪,如果此時設置了cell.layer可光柵化。則會造成大量的離屏渲染,降低圖形性能。
Offscreen Rendering And Mask(離屏渲染)
GPU屏幕渲染有以下兩種方式:
On-Screen Rendering
意為當前屏幕渲染,指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區中進行。
Off-Screen Rendering
意為離屏渲染,指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。
設置了以下屬性時,都會觸發離屏繪制:
shouldRasterize(光柵化)
masks(遮罩)
shadows(陰影)
edge antialiasing(抗鋸齒)
group opacity(不透明)
復雜形狀設置圓角等
漸變
為什么會使用離屏渲染
當使用圓角,陰影,遮罩的時候,圖層屬性的混合體被指定為在未預合成之前不能直接在屏幕中繪制,所以就需要屏幕外渲染被喚起。
屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU)。
性能瓶頸:
如果我們對layer做這樣的操作:
label.layer.cornerRadius = 5.0f;
label.layer.masksToBounds = YES;
會產生offscreen rendering,它帶來的最大的問題是,當渲染這樣的layer的時候,需要額外開辟內存,繪制好radius,mask,然后再將繪制好的bitmap重新賦值給layer。所以當使用離屏渲染的時候會很容易造成性能消耗,屏幕外緩沖區跟當前屏幕緩沖區上下文切換是很耗性能的。
優化方案:
1、因此繼續性能的考慮,Quartz提供了優化的api:
label.layer.cornerRadius = 5.0f;
label.layer.masksToBounds = YES;
label.layer.shouldRasterize = YES;
label.layer.rasterizationScale = label.layer.contentsScale;
簡單的說,這是一種cache機制。
2、只需要圓角的某些場合,也可以用一張已經繪制好的圓角圖片覆蓋到原本視圖上面來模擬相同的視覺效果。
3、最徹底的解決辦法,就是把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角、陰影、遮罩等屬性.
同樣GPU的性能也可以通過instrument去衡量:
紅色代表GPU需要做額外的工作來渲染View,綠色代表GPU無需做額外的工作來處理bitmap。