離屏渲染原理及案例解析

1、模擬器開啓離屏渲染檢測

2、離屏渲染原理

APP -> FrameBuffer -> Display緩存

APP -> offscreenBuffer -> FrameBuffer -> Displayapp

mask layer 渲染完畢,不能單獨當即顯示,要等待 contents layer 渲染完畢進行混合,等待過程當中,先存在 離屏渲染緩衝區。
等待內容都渲染完畢,傳遞到 幀緩衝區,等待顯示。優化

app 進行額外的渲染和合並 -> offscreenBuffer 組合 -> FrameBuffer -> Display動畫

離屏渲染缺點:

  1. 額外的存儲空間;
  2. offscreenBuffer 傳遞到 FrameBuffer 須要時間 最終可能致使 掉幀

離屏渲染空間限制:屏幕的 2.5 倍ui

使用離屏渲染的緣由

  1. 特殊效果
    須要使用額外的 offscreenBuffer 保存中間狀態,不得不使用。
    系統自動觸發:圓角、陰影、高斯模糊等
  2. 效率優點
    效果會屢次渲染 -> 提早渲染保存在 offscreenBuffer -> 能夠實現複用

1、高斯模糊 觸發離屏渲染

一、獲取圖像 -> Render Content
二、縮放處理 -> Capture Content
三、水平毛玻璃 -> Horizontal Blur Content
四、垂直毛玻璃 -> Vertical Blur Content
五、合成 -> Compositing Content
六、輸出到 FrameBufferspa

因爲兩個 幀緩衝區 空間有限,因此不能存在 幀緩衝區。
處理過程當中的 content 存儲在 offscreenBuffer。code

2、開啓光柵化(shouldRasterize) 觸發離屏渲染

開啓光柵化,layer 渲染爲位圖後, 會保存在緩衝區,能夠複用在其餘內容。orm

不建議開啓的場景:cdn

  1. layer 不能被複用
  2. layer 不是靜態的,須要被頻繁修改。好比處於動畫之中,開啓會影響效率。
  3. 離屏渲染緩存有時間限制,100ms 沒被複用,就會被丟棄。
  4. 離屏渲染緩存空間有限,超過 2.5 倍屏幕像素大小的話,也會失效。

layer.cornerRadius 屬性與離屏渲染之間的解讀

layer 渲染的時候分爲三層 blog

Apple 文檔

文檔顯示:

layer.cornerRadius 只會設置 backgroundColor 和 border 的圓角。不會設置 contents 的圓角。
除非同時設置了 layer.masksToBounds 爲 YES(對應 view.clipsToBounds)。

簡單理解:

layer.cornerRadius 會修改 backgroundColor 和 border 兩個圖層。
layer.masksToBounds 會讓上述切圓角擴張到修改 contents 圖層,子視圖的圖層也在這裏。 此時,繪製完 backgroundColor 層後,須要暫存到 offscreenBuffer,等待 contents 層渲染完畢,再進行 裁剪操做,而後輸出到 FrameBuffer。這就是就是離屏渲染。

3、觸發離屏渲染的案例

  • 案例一
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    btn1.layer.cornerRadius = 50;
    [self.view addSubview:btn1];
    
    btn1.clipsToBounds = YES;
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
複製代碼

解析
設置 image 後,觸發離屏渲染。
若是不設置 image, contents = nil,系統進行了優化, 不用切圓角,所以直接對 backgroundColor 層操做後,輸出到 FrameBuffer 便可。

  • 案例二
// UIImageView 設置了切圓角,設置了背景色,設置了圖片
    UIImageView *img1 = [[UIImageView alloc]init];
    img1.frame = CGRectMake(100, 320, 100, 100);
    [self.view addSubview:img1];
    
    img1.layer.cornerRadius = 50;
    img1.layer.masksToBounds = YES;
    
    img1.backgroundColor = [UIColor blueColor];
    img1.image = [UIImage imageNamed:@"btn.png"];
複製代碼

解析
若是不設置 backgroundColor,不會觸發離屏渲染。
由於 backgroundColor 圖層爲空,不要渲染,所以,只須要對 contents 層操做後,輸出到 FrameBuffer 便可。

  • 案例三
// UIImageView 設置了切圓角,沒有設置背景色,可是加了一個子視圖
    UIImageView *img2 = [[UIImageView alloc]init];
    img2.frame = CGRectMake(100, 480, 100, 100);
    [self.view addSubview:img2];
    
    img2.layer.cornerRadius = 50;
    img2.layer.masksToBounds = YES;
    
    UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    subView.backgroundColor = [UIColor redColor];
    [img2 addSubview:subView];
複製代碼

解析
案例二中,若是沒有背景色,img2.layer.contents 直接是位圖,能夠直接裁剪,輸出到 FrameBuffer。
可是案例三中,img2.layer.contents 實際上是子視圖的渲染出來的位圖,若是要給 contents 加圓角,則必須等待子視圖渲染完成,存到 offscreenBuffer,而後再對緩衝區中的渲染結果進行裁剪,所以產生了離屏渲染。

‼️️ 注意

案例二中,UIImageView 不設置背景色,只設置 image,就不會觸發背景色。
那爲何在案例一中,UIButton 只是設置了 image,沒有設置背景色,卻觸發了離屏渲染呢?
實際上是由於 UIButton 設置 image 的時候,是把 image 添加到了 UIButton 的一個 UIImageView 子視圖上,就變成了案例三的這種狀況,會觸發離屏渲染。

4、離屏渲染原理總結

離屏渲染本質:要等待對多個非空圖層的渲染結果進行操做,過程當中的圖層須要儲存起來,這個存儲位置就是 offscreenBuffer(離屏渲染緩衝區),整個過程就叫作離屏渲染

5、iOS 不觸發離屏渲染設置圓角

方案一

// UIImageView 設置了切圓角,不設置背景色
    imageView.backgroundColor = [UIColor clearColor];
    imageView.layer.cornerRadius = 50;
    imageView.layer.masksToBounds = YES;
複製代碼

方案二

UI 切圖帶圓角

方案三

使用 貝塞爾曲線,裁剪 UIImage,繪製一個帶圓角的 UIImage。

方案五

增長一個帶圓角,中間鏤空的切圖,蓋在須要圓角的視圖上面。

6、iOS 觸發離屏渲染的常見案例

  1. 使用了 mask 的 layer (layer.mask)
  2. 須要進行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
  3. 設置了組透明度爲 YES,而且透明度不爲 1 的 layer (layer.allowsGroupOpacity/ layer.opacity)
  4. 添加了投影的 layer (layer.shadow*)
  5. 採用了光柵化的 layer (layer.shouldRasterize)
  6. 繪製了文字的 layer (UILabel, CATextLayer, Core Text 等)
相關文章
相關標籤/搜索