iOS離屏渲染

開場白


本文簡單介紹了iOS中離屏渲染的相關內容呢。算法

1.什麼是離屏渲染:


要了解離屏渲染,咱們先簡單瞭解一下非離屏渲染的邏輯緩存

非離屏渲染

非離屏渲染也就是正常的渲染流程,簡要流程如圖: bash

APP將要渲染的信息提交給CPU,CPU經過必定的處理後提交給GPU。GPU不停的將內容渲染完成放到幀緩衝區中(FrameBuffer)。最後顯示到屏幕上。

離屏渲染

離屏渲染簡要流程如圖: markdown

與普通流程不一樣的是,GPU把渲染好的的內容存放到離屏渲染緩衝區中,在離屏渲染緩衝區(OffscreenBuffer)中進一步作一些處理後,再提交到幀緩衝區(FrameBuffer)中。

2.離屏渲染對性能的影響?


  • 和非離屏渲染模式相比多了進行額外的渲染合併,是對多個texture進行合併的過程。多了這麼一步,因此說對性能要求更高一些。更容易出現掉幀的狀況。
  • 增長了額外的存儲空間offscreen buffer,空間大小是屏幕的2.5倍

3.模擬器打開離屏渲染顏色標註


模擬器->Debug->Color Off-screen Rendered oop

-w626
開啓後在模擬器界面中能看到使用離屏渲染的View了 圖片中的例子是一個button,設置了一個背景色和背景圖,對layer層設置cornerRadius和masksToBounds。

masksToBounds=YES,若是不設置是看不到效果的。下面會具體說明緣由。佈局

4.爲何要用離屏渲染?


使用離屏渲染大體有一下兩種狀況:性能

  1. 能夠顯示一些特殊效果,須要用到Offscreen buffer來保存中間狀態。
  2. 若是texture會屢次顯示到屏幕上,可使用offscreen buffer進行提早渲染,而且保存在其中,達到複用的效果。layer中有shouldRasterize屬性

狀況1:

通常都是系統去觸發,例如對layer層相關處理:包括圓角、陰影、mask等等。iOS系統扁平化後出現的高斯模糊也是利用離屏渲染方式。spa

圓角處理

設置圓角爲何會觸發離屏渲染呢?這個要從layer的結構提及。 layer結構中包含3部分:code

  1. backgroundColor
  2. contents
  3. boarder相關信息(borderWidth和borderColor)

設置圓角代碼,這個你們應該都知道:orm

view.layer.cornerRadius = 2
複製代碼

咱們先看看官方文檔中cornerRadius相關說明:

-w944
Discussion中的說明: 設置cornerRadius超過0.0,不會影響contents,可是會影響background color和border。若是設置了masksToBounds會對content進行裁剪。 因此說出發離屏渲染的主要緣由:

masksToBounds=YES;
複製代碼

這就是上面模擬器開啓離屏渲染模式中說明的爲何要設置masksToBounds的緣由。masksToBounds須要對layer上的全部內容進行裁剪,過程當中須要對中間值進行保存。因此進行了離屏渲染操做。

注意: 若是說layer圖層比較簡單,也是不會觸發離屏渲染的。例如:UIImageView設置cornerRadius和masksToBounds是不會觸發離屏渲染的,若是再對UIImageView設置背景色,則會觸發。

self.view.backgroundColor = [UIColor grayColor];
    
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
    imageView.frame = CGRectMake(100, 100, 100, 100);
    imageView.layer.cornerRadius = 30;
    imageView.layer.masksToBounds = YES;
    [self.view addSubview:imageView];
    
    UIImageView *imageView2 = [[UIImageView alloc]  initWithImage:[UIImage imageNamed:@"1"]];
    imageView2.backgroundColor = UIColor.redColor;
    imageView2.frame = CGRectMake(100, 250, 100, 100);
    imageView2.layer.cornerRadius = 30;
    imageView2.layer.masksToBounds = YES;
    [self.view addSubview:imageView2];
    
    UIButton *btn = [UIButton   buttonWithType:UIButtonTypeCustom];
    [btn setBackgroundImage:[UIImage imageNamed:@"1"]   forState:UIControlStateNormal];
    btn.layer.cornerRadius = 30;
    btn.layer.masksToBounds = YES;
    btn.frame = CGRectMake(100, 400, 100, 100);
    [self.view addSubview:btn];
複製代碼

Simulator Screen Shot - iPhone SE -2nd generation- - 2020-07-07 at 15.24.27

代碼中有三個控件,前兩個是UIImageView,最後一個是UIButton。

  • 第一個UIImageView設置了圖片沒有設置背景色,沒有觸發離屏渲染。
  • 第二個UIImageView設置了圖片和背景色,觸發了離屏渲染。
  • 最後一個UIButton,設置圖片沒有設置背景色,觸發了離屏渲染。緣由是咱們看到UIButton是由它的layer和UIImageView的layer混合起來的效果(UIButton有imageView),因此設置圓角的時候會觸發離屏渲染。

狀況2:

是一種主動行爲,是爲了提升複用的效率。一般是設置layer的shouldRasterize屬性來實現。

shouldRasterize 光柵化

shouldRasterize官方文檔

-w964
開啓後,會將layer做爲位圖保存下來,下次直接與其餘內容進行混合。這個保存的位置就是OffscreenBuffer中。這樣下次須要再次渲染的時候,就能夠直接拿來使用了。

shouldRasterize使用建議:

  • layer不復用,不必使用shouldRasterize
  • layer不是靜態的,也就是說要頻繁的進行修改,不必使用shouldRasterize
  • 時間方面:離屏渲染緩存有100ms時間限制,超過該時間的內容會被丟棄,進而不能達到複用的目的
  • 空間方面:離屏渲染空間是屏幕像素的2.5倍,若是超過也沒法複用。

5.離屏渲染邏輯


圖層的疊加繪製大體遵循「畫家算法」,也就是由遠到近的方式將圖層繪製到屏幕上,繪製近距離圖層會有覆蓋遠圖層的邏輯。

普通渲染方式,繪製完一層圖層後,直接捨棄掉。緊接着繪製稍近的圖層。以此類推:

而咱們進行離屏渲染方式(圓角、剪裁等操做)時,每層圖層繪製完不會立刻刪除,而是先保存在離屏緩存區中,等圖層繪製完成後,再依次進行特殊處理(圓角、剪裁等操做)。
注意:一般會觸發離屏渲染,除了圓角、剪裁外,還有設置了透明度+組透明(layer.allowsGroupOpacity+layer.opacity),陰影屬性(shadowOffset 等)都會產生相似的效果,由於組透明度、陰影都是和裁剪相似的,會做用與 layer 以及其全部 sublayer 上,這就致使必然會引發離屏渲染。

6.避免圓角離屏渲染


爲了不設置圓角時引發的離屏渲染操做,能夠用一下方案代替直接設置圓角的操做

  • 直接更換支援,讓UI提供帶圓角的圖片。
  • 使用layer.mask屬性,增長一個和背景色相同的遮罩覆蓋上層,蓋住四個角,營造出圓角的形狀。但這種方式難以解決背景色爲圖片或漸變色的狀況。
  • 使用貝塞爾曲線繪製閉合圓角的矩形,在上下文中設置只有內部可見,再將不帶圓角的 layer 渲染成圖片,添加到貝塞爾矩形中。這種方法效率更高,可是 layer 的佈局一旦改變,貝塞爾曲線都須要手動地從新繪製,因此須要對 frame、color 等進行手動地監聽並重繪。
  • CoreGraphics重寫 drawRect:,用 CoreGraphics 相關方法,在須要應用圓角時進行手動繪製。不過 CoreGraphics 效率也頗有限,若是須要屢次調用也會有效率問題。

7.觸發離屏渲染緣由的總結


  1. 設置layer.mashsToBounds/view.clipsToBounds
  2. 設置layer.mask
  3. 設置layer.shadow等相關屬性
  4. 設置layer.shouldRasterize光柵化
  5. 設置了組透明度爲 YES,而且透明度不爲 1 的 layer (layer.allowsGroupOpacity/layer.opacity)
  6. 繪製了文字的 layer (UILabel, CATextLayer, Core Text 等)

8.參考文章


iOS 渲染原理解析

相關文章
相關標籤/搜索