02總結(jié)--006--OpenGL 離屏渲染

[TOC]

緩存栋猖!緩存净薛!還是緩存!

緩存
  1. 緩存是啥蒲拉?cache肃拜、buffer。也許你沒用過雌团,但是你一定見過無數(shù)次這兩個(gè)單詞燃领。
  2. 緩存有啥用?復(fù)用锦援、效率提升猛蔽,典型的空間換時(shí)間。

這篇文章講的內(nèi)容就是從緩存開始的,下面來看看一些常見的緩存曼库。

class : cache_t

cache_t
方法查找和轉(zhuǎn)發(fā)流程

在方法查找階段:

  • 先從類的緩存中去取
  • 若沒有找到区岗,再從類的方法列表中找
  • 找到后,會(huì)將方法存到緩存中(方便下一次的讀取毁枯,提高查找效率)

http 緩存

  • cookies:用戶信息慈缔,http中解決無法定位用戶身份問題
  • body:數(shù)據(jù)緩存,避免每次都要從后臺(tái)讀取种玛,使得數(shù)據(jù)的獲取更快藐鹤,效率更高,減少流量的浪費(fèi)赂韵,降低服務(wù)器的負(fù)載

SDWebImage :圖片緩存

SDWebImageSequenceDiagram
  • 內(nèi)存緩存
  • 磁盤緩存

NSStream : buffer

這個(gè)例子和其他的稍微有點(diǎn)區(qū)別娱节,應(yīng)該叫緩沖區(qū),其實(shí)也是一個(gè)緩存祭示,但它的使用場景更加偏向于本章重點(diǎn)——離屏渲染

NSStream:Input -> Output

建立通道之后括堤,數(shù)據(jù)流不是直接從 Input 輸入到 Output,而是先輸入到一個(gè) data buffer 里面绍移,然后 Output 從 data buffer 里面取

NSStream:Input -> Buffer -> Output

這樣有什么好處呢悄窃?

  • 當(dāng)接收端下行網(wǎng)絡(luò)環(huán)境較差時(shí),我們可以將更多的數(shù)據(jù)存到這個(gè) buffer 里面蹂窖,等到它網(wǎng)絡(luò)恢復(fù)的時(shí)候再讀取轧抗,不會(huì)造成數(shù)據(jù)的丟失
  • 同樣,當(dāng)發(fā)送端上行網(wǎng)絡(luò)差時(shí)瞬测,接收端可以從 buffer 中取數(shù)據(jù)横媚,降低網(wǎng)絡(luò)對數(shù)據(jù)傳輸?shù)挠绊?/li>

UITableView : 緩存行高

tableview的問題對于iOS開發(fā)者來說是老生常談的問題了,其中有一條就是盡量避免使用 estimatedHeightForRowAtIndexPath 來設(shè)置高度月趟,對于動(dòng)態(tài)高度灯蝴,我們一般會(huì)提前計(jì)算好高度,緩存起來孝宗,然后通過 heightForRowAtIndexPath 來設(shè)置高度穷躁。

除了上面提到的一些常見的緩存,我們在實(shí)際開發(fā)中還有更多的自定義的緩存策略因妇,比如組件化開發(fā)中问潭,對組件的緩存。

render buffer:渲染緩存(幀緩存)

片元著色器給片元上色之后的像素怎么處理呢婚被?直接顯示到屏幕上嗎狡忙?

并不是,而是存在一個(gè)渲染緩存(幀緩存)里面址芯,等到下一次runloop到來時(shí)灾茁,從幀緩存中讀取數(shù)據(jù)窜觉,然后顯示到屏幕上。

image

下圖展示了蘋果的雙緩存技術(shù)北专,當(dāng)只有一個(gè)緩存時(shí)禀挫,會(huì)出現(xiàn)掉幀等不良現(xiàn)象,所以蘋果給了兩個(gè)緩存區(qū)來存儲(chǔ)數(shù)據(jù)逗余。

image

offscreen buffer:離屏緩存

上面說到了特咆,蘋果都給了兩個(gè)緩存區(qū)來存儲(chǔ)渲染數(shù)據(jù)季惩,那為啥還會(huì)有離屏緩存呢录粱?

當(dāng)需要繪制的圖像由多個(gè)圖層組成時(shí),便需要將前面的渲染數(shù)據(jù)存起來画拾,然后再對這些數(shù)據(jù)進(jìn)行混合啥繁,得到新的數(shù)據(jù),顯示到屏幕上青抛。離屏的操作便發(fā)生在存數(shù)據(jù)的時(shí)候旗闽。(幀緩存里面的渲染數(shù)據(jù),使用完就會(huì)被丟棄蜜另,不會(huì)保存)

  • 案例1:mask 渲染流程
image
  1. 渲染 layer mask 紋理适室,存儲(chǔ)到離屏緩存區(qū)里面
  2. 渲染 layer content 紋理,存儲(chǔ)到離屏緩存區(qū)里面
  3. 混合上面的紋理举瑰,存儲(chǔ)到幀緩存區(qū)捣辆,等待顯示
  • 案例2:UIBlurEffect 渲染流程
image

1-4:渲染內(nèi)容、捕獲內(nèi)容此迅、水平模糊汽畴、垂直模糊,這些結(jié)果都是存儲(chǔ)在離屏緩存區(qū)里面耸序;
5:合成上面的結(jié)果忍些,存儲(chǔ)到幀緩存區(qū),等待下一次顯示坎怪。

離屏渲染的理解

關(guān)于離屏渲染以及渲染過程罢坝,可以查看02總結(jié)--005--OpenGL 渲染全解析[轉(zhuǎn)載]這篇文章,這里主要是補(bǔ)充一些對離屏渲染的理解搅窿。比如前面講了離屏渲染其實(shí)就是一個(gè)緩存區(qū)炸客,這是一個(gè)很重要的理解思路。

離屏渲染流程

渲染流程
  • 正常流程:將內(nèi)容渲染完成之后戈钢,不停地放入 Framebuffer 中痹仙,然后顯示屏幕不斷地從 Framebuffer 中讀取內(nèi)容,顯示實(shí)時(shí)內(nèi)容殉了;
  • 離屏渲染:創(chuàng)建 Offscreenbuffer >> 將提前渲染好的內(nèi)容放入其中 >> 等到合適的時(shí)機(jī)在將 OffScreenbuffer 中的內(nèi)容進(jìn)一步疊加开仰、渲染 >> 最后將結(jié)果放入 Framebuffer 中;
  • 顯示屏幕最后獲取數(shù)據(jù)的來源都是 幀緩存Framebuffer;
  • Offscreenbuffer 只是一個(gè)臨時(shí)存儲(chǔ)渲染數(shù)據(jù)的地方

離屏渲染原理

  • Layer的層級
    • backgroundColor
    • contents
    • borderWidth / borderColor
layer的層級

如上圖所示众弓,layer 由三層組成恩溅,我們設(shè)置圓角通常會(huì)首先像下面這行代碼一樣進(jìn)行設(shè)置:

view.layer.cornerRadius = 2
cornerRadius官方解釋
  • 設(shè)置 layer.cornerRadius 只會(huì)設(shè)置 backgroundColor 和 border 的圓角,不會(huì)設(shè)置 contents
  • 同時(shí)設(shè)置 layer.masksToBounds 才會(huì)設(shè)置 contents 的圓角谓娃。(對應(yīng)view中的clipsToBounds屬性)

下面通過幾個(gè)案例來理解這張圖片

  1. 按鈕存在背景圖片
    1. 同時(shí)設(shè)置了 cornerRadiusclipsToBounds 這兩個(gè)屬性脚乡,會(huì)對 layer 的三層都起作用;
    2. button中設(shè)置了圖片 setImage滨达,說明它的contents中存在內(nèi)容奶稠,需要渲染的內(nèi)容有;(這里相當(dāng)于給按鈕添加了一個(gè)imageview)
    3. 對于復(fù)合圖形的渲染捡遍,是需要借助 Offscreenbuffer 緩存的锌订,所以觸發(fā)了離屏渲染
//1.按鈕存在背景圖片
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(100, 30, 100, 100);
btn1.layer.cornerRadius = 50;
[self.view addSubview:btn1];

[btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
btn1.clipsToBounds = YES;
按鈕中的imageview

既然會(huì)造成離屏渲染,那如果直接對 imageview 設(shè)置圓角呢画株?結(jié)果是不會(huì)觸發(fā)離屏渲染

btn1.imageView.layer.cornerRadius = 50;
btn1.imageView.layer.masksToBounds = YES;
  1. 按鈕不存在背景圖片
    1. 同時(shí)設(shè)置了 cornerRadiusclipsToBounds 這兩個(gè)屬性辆飘,會(huì)對 layer 的三層都起作用
    2. 而且還設(shè)置了 backgroundColor
    3. 但是 btn2 中并沒有 contents 的元素谓传,只有本身的 backgroundColor蜈项,是一個(gè)單一圖層,所以不會(huì)觸發(fā)離屏渲染
//2.按鈕不存在背景圖片
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(100, 180, 100, 100);
btn2.layer.cornerRadius = 50;
btn2.backgroundColor = [UIColor blueColor];
[self.view addSubview:btn2];
btn2.clipsToBounds = YES;
  1. UIImageView 設(shè)置了圖片+背景色;
    1. backgroundColor:[UIColor blueColor];
    2. contents: [UIImage imageNamed:@"btn.png"];
    3. 所以會(huì)離屏渲染
//3.UIImageView 設(shè)置了圖片+背景色;
UIImageView *img1 = [[UIImageView alloc]init];
img1.frame = CGRectMake(100, 320, 100, 100);
img1.backgroundColor = [UIColor blueColor];
[self.view addSubview:img1];
img1.layer.cornerRadius = 50;
img1.layer.masksToBounds = YES;
img1.image = [UIImage imageNamed:@"btn.png"];
  1. UIImageView 只設(shè)置了圖片,無背景色;
    1. 只有 contents: [UIImage imageNamed:@"btn.png"];
    2. 所以不會(huì)造成離屏渲染
//4.UIImageView 只設(shè)置了圖片,無背景色;
UIImageView *img2 = [[UIImageView alloc]init];
img2.frame = CGRectMake(100, 480, 100, 100);
[self.view addSubview:img2];
img2.layer.cornerRadius = 50;
img2.layer.masksToBounds = YES;
img2.image = [UIImage imageNamed:@"btn.png"];
image

【總結(jié)】

通過上面的這幾個(gè)例子來說续挟,我們不能直接根據(jù) 圓角和裁剪 來判斷是否觸發(fā)離屏渲染紧卒,我們應(yīng)該根據(jù)離屏渲染的原理來說明。

離屏渲染優(yōu)劣勢

  • 優(yōu)勢:使用離屏渲染的原因

    1. 一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài)庸推,所以不得不使用離屏渲染常侦。
    2. 出于效率目的,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中贬媒,達(dá)到復(fù)用的目的聋亡。
  • 劣勢:避免離屏渲染的原因

    1. 離屏渲染時(shí)由于 App 需要提前對部分內(nèi)容進(jìn)行額外的渲染并保存到 Offscreen Buffer,以及需要在必要時(shí)刻對 Offscreen Buffer 和 Framebuffer 進(jìn)行內(nèi)容切換际乘,所以會(huì)需要更長的處理時(shí)間(實(shí)際上這兩步關(guān)于 buffer 的切換代價(jià)都非常大)
    2. 并且 Offscreen Buffer 本身就需要額外的空間坡倔,大量的離屏渲染可能早能內(nèi)存的過大壓力。與此同時(shí)脖含,Offscreen Buffer 的總大小也有限罪塔,不能超過屏幕總像素的 2.5 倍。
    3. 可見離屏渲染的開銷非常大养葵,一旦需要離屏渲染的內(nèi)容過多征堪,很容易造成掉幀的問題。所以大部分情況下关拒,我們都應(yīng)該盡量避免離屏渲染佃蚜。

離屏渲染具體邏輯

1. 畫家算法

剛才說了圓角加上 masksToBounds 的時(shí)候庸娱,因?yàn)?masksToBounds 會(huì)對 layer 上的所有內(nèi)容進(jìn)行裁剪,從而誘發(fā)了離屏渲染谐算,那么這個(gè)過程具體是怎么回事呢熟尉,下面我們來仔細(xì)講一下。

圖層的疊加繪制大概遵循“畫家算法”洲脂,在這種算法下會(huì)按層繪制斤儿,首先繪制距離較遠(yuǎn)的場景,然后用繪制距離較近的場景覆蓋較遠(yuǎn)的部分恐锦。

image

在普通的 layer 繪制中往果,上層的 sublayer 會(huì)覆蓋下層的 sublayer,下層 sublayer 繪制完之后就可以拋棄了踩蔚,從而節(jié)約空間提高效率棚放。所有 sublayer 依次繪制完畢之后枚粘,整個(gè)繪制過程完成馅闽,就可以進(jìn)行后續(xù)的呈現(xiàn)了。假設(shè)我們需要繪制一個(gè)三層的 sublayer馍迄,不設(shè)置裁剪和圓角福也,那么整個(gè)繪制過程就如下圖所示:

[圖片上傳失敗...(image-cde829-1594132539692)]

而當(dāng)我們設(shè)置了 cornerRadius 以及 masksToBounds 進(jìn)行圓角 + 裁剪時(shí),如前文所述攀圈,masksToBounds 裁剪屬性會(huì)應(yīng)用到所有的 sublayer 上暴凑。這也就意味著所有的 sublayer 必須要重新被應(yīng)用一次圓角+裁剪,這也就意味著所有的 sublayer 在第一次被繪制完之后赘来,并不能立刻被丟棄现喳,而必須要被保存在 Offscreen buffer 中等待下一輪圓角+裁剪,這也就誘發(fā)了離屏渲染犬辰,具體過程如下:

image

實(shí)際上不只是圓角+裁剪嗦篱,如果設(shè)置了透明度+組透明(layer.allowsGroupOpacity+layer.opacity),陰影屬性(shadowOffset 等)都會(huì)產(chǎn)生類似的效果幌缝,因?yàn)榻M透明度灸促、陰影都是和裁剪類似的,會(huì)作用與 layer 以及其所有 sublayer 上涵卵,這就導(dǎo)致必然會(huì)引起離屏渲染浴栽。

離屏渲染案例

mask rendering
  1. 渲染mask,存入離屏緩存區(qū)轿偎;
  2. 渲染layer典鸡,存入離屏緩存區(qū);
  3. 讀取離屏緩存區(qū)的數(shù)據(jù)坏晦,然后進(jìn)行混合操作萝玷,將結(jié)果存入幀緩存區(qū)伊者;
  4. 等待下一次 runloop到來,顯示到屏幕上间护;
image
  1. Content:渲染內(nèi)容
  2. capture content:捕獲內(nèi)容
  3. Horizontal Blur:水平模糊
  4. Vertical Blur:垂直模糊
  5. Compositing Pass:合并過程
  6. 合并完成之后將結(jié)果存入幀緩存區(qū)亦渗,等待下一次 runloop到來,顯示到屏幕上

光柵化在離屏渲染中扮演的角色

shouldRasterize 光柵化的使用建議:

  • 如果 layer 不能被復(fù)用汁尺,則沒有必要打開光柵化法精;
  • 如果 layer 不是靜態(tài)的,需要被頻繁修改痴突,比如處于動(dòng)畫之中搂蜓,那么開啟了離屏渲染反而會(huì)影響效率;
  • 離屏渲染緩存內(nèi)容有時(shí)間限制辽装,緩存內(nèi)容 100ms 內(nèi)如果沒有被使用帮碰,那么它就會(huì)被丟棄調(diào),無法進(jìn)行復(fù)用拾积;
  • 離屏渲染緩存內(nèi)容有空間限制殉挽,超過 2.5 倍屏幕像素大小的話,也會(huì)失效拓巧,且無法進(jìn)行復(fù)用斯碌;

這里的 shouldRasterize 光柵化的使用建議 也是我們使用離屏渲染的建議。

光柵化和離屏渲染的聯(lián)系

從上面離屏渲染的原理中可以知道肛度,如果我們只是單一的圖層顯示傻唾,是不會(huì)觸發(fā)離屏渲染的,而當(dāng)我們開啟光柵化之后承耿,不管是單一圖層還是復(fù)合圖層冠骄,都會(huì)觸發(fā)離屏渲染。
所以光柵化的目的就是強(qiáng)制開啟離屏渲染加袋。

  • 最后一行打開了光柵化凛辣,所以也開啟了離屏渲染
image

(離屏)緩存的時(shí)效性

上面說的離屏渲染其實(shí)就是一個(gè)緩存,我們知道緩存一般時(shí)效性很低锁荔,對于Offscreenbuffer中存儲(chǔ)的數(shù)據(jù)的緩存時(shí)間是 100ms

常見圓角觸發(fā)的情況以及處理辦法

常見圓角觸發(fā)的情況

  1. 使用了 mask 的 layer(layer.mask)
  2. 需要進(jìn)行裁剪的 layer(layer.masksToBounds / view.clipsToBounds)
  3. 設(shè)置了組透明度為 YES蟀给,并且透明度不為1 的layer(layer.allosGroupOpacity / layer.opacity)
  4. 添加了投影的 layer(layer.shadow*)
  5. 繪制了文字的 layer(UILabel,CATextLayer阳堕,Core Text等)

圓角的處理辦法

  1. 方案一【按鈕上的圖片】:使用 btn1.imageView

    上面的案例中也提到了跋理,直接對 imageview 設(shè)置圓角,不要對button設(shè)置圓角

    btn1.imageView.layer.cornerRadius = 50;
    btn1.imageView.layer.masksToBounds = YES;
    
  2. 方案二:創(chuàng)建一個(gè)圓角圖片

    @implementation UIImage (CornerRadius)
    
    - (UIImage *)roundedCornerImageWithCornerRadius:(CGFloat)cornerRadius {
        CGFloat w = self.size.width;
        CGFloat h = self.size.height;
        CGFloat scale = UIScreen.mainScreen.scale;
        // 防止圓角半徑小于0, 或者大于寬/高中較小值的一半
        cornerRadius = MAX(cornerRadius, 0);
        cornerRadius = MIN(cornerRadius, MIN(w, h)/2);
    
        UIImage* image = nil;
        CGRect imageFrame = CGRectMake(0, 0, w, h);
        UIGraphicsBeginImageContextWithOptions(self.size, NO, scale);
        UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:cornerRadius];
        [path addClip];
        [self drawInRect:imageFrame];
        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    
    @end
    
  3. 方案三:設(shè)置Imageview的image

    @implementation UIImageView (MaskBounds)
    
    - (void)addMaskToBounds:(CGRect)maskBounds WithCornerRadius:(CGFloat)cornerRadius {
        CGFloat w = maskBounds.size.width;
        CGFloat h = maskBounds.size.height;
        CGFloat scale = UIScreen.mainScreen.scale;
        CGSize size = maskBounds.size;
        CGRect imageRect = CGRectMake(0, 0, w, h);
        // 防止圓角半徑小于0, 或者大于寬/高中較小值的一半
        cornerRadius = MAX(cornerRadius, 0);
        cornerRadius = MIN(cornerRadius, MIN(w, h)/2);
    
        UIImage* image = self.image;
        UIGraphicsBeginImageContextWithOptions(size, NO, scale);
        UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:cornerRadius];
        [path addClip];
        [image drawInRect:imageRect];
        self.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    
    @end
    

自測也是總結(jié)

以下內(nèi)容都是筆者的理解恬总,歡迎留言給出其他解釋前普。

  1. CPU 和 GPU 的設(shè)計(jì)目的分別是什么?

    • CPU 處理邏輯壹堰、控制核心拭卿、依賴高
    • GPU 處理大量簡單的計(jì)算骡湖、依賴低
  2. CPU 和 GPU 哪個(gè)的 Cache\ALU\Control unit 的比例更高?

    • CPU 中緩存和控制單元比例高
    • GPU 中計(jì)算單元比例高
  3. 計(jì)算機(jī)圖像渲染流水線的大致流程是什么峻厚?

    • Application:處理事件响蕴、提交動(dòng)畫(界面可能會(huì)發(fā)生變化);
    • CoreAnimation:CPU處理顯示內(nèi)容的前置計(jì)算惠桃,例如布局計(jì)算浦夷、解碼等任務(wù),然后將圖層打包傳遞到下一層(渲染層)辜王;
    • Render server:GPU渲染流程劈狐。(過程:頂點(diǎn)著色器->光柵化->片元著色器->存到幀緩存區(qū),結(jié)果:原始圖元->新圖元->片元->像素->位圖
    • 等待下一個(gè)runloop的到來呐馆,將位圖顯示到屏幕上
  4. Framebuffer 幀緩沖器的作用是什么肥缔?

    • 存儲(chǔ)GPU渲染結(jié)果(位圖),等待下一個(gè)runloop的到來汹来,顯示到屏幕上
  5. Screen Tearing 屏幕撕裂是怎么造成的续膳?

    • 電子束在掃描新的一幀時(shí),位圖還沒有處理好
    • 掃描到中間的時(shí)候俗慈,位圖處理好了
    • 這時(shí)姑宽,上半部是上一幀的畫面遣耍,下半部是這一幀的畫面闺阱,所以造成撕裂
  6. 如何解決屏幕撕裂的問題?

    • 垂直同步(Vsync)+雙緩存區(qū)(Double Buffering)
  7. 掉幀是怎么產(chǎn)生的舵变?

    • 由于同步的問題酣溃,幀緩存區(qū)中畫面的顯示是按順序顯示的
    • 當(dāng)CPU+GPU在16.67ms內(nèi)沒有完成一幀的計(jì)算時(shí)
    • 下一次runloop的到來,并不能從幀緩存區(qū)中拿到新圖像
    • 所以顯示的還是上一次的畫面纪隙,所以造成掉幀
  8. CoreAnimation 的職責(zé)是什么赊豌?

    • 主要職責(zé)包含:渲染、構(gòu)建和實(shí)現(xiàn)動(dòng)畫绵咱。
    • 是 app 界面渲染和構(gòu)建的最基礎(chǔ)架構(gòu)
    • 盡可能快地組合屏幕上不同的可視內(nèi)容碘饼,并且被存儲(chǔ)為樹狀層級結(jié)構(gòu)
    • 這個(gè)樹也形成了 UIKit 以及在 iOS 應(yīng)用程序當(dāng)中你所能在屏幕上看見的一切的基礎(chǔ)
  9. UIView 和 CALayer 是什么關(guān)系?有什么區(qū)別悲伶?

    • 相同的層級結(jié)構(gòu):我們對 UIView 的層級結(jié)構(gòu)非常熟悉艾恼,由于每個(gè) UIView 都對應(yīng) CALayer 負(fù)責(zé)頁面的繪制,所以 CALayer 也具有相應(yīng)的層級結(jié)構(gòu)麸锉。
    • 部分效果的設(shè)置:因?yàn)?UIView 只對 CALayer 的部分功能進(jìn)行了封裝钠绍,而另一部分如圓角、陰影花沉、邊框等特效都需要通過調(diào)用 layer 屬性來設(shè)置柳爽。
    • 是否響應(yīng)點(diǎn)擊事件:CALayer 不負(fù)責(zé)點(diǎn)擊事件媳握,所以不響應(yīng)點(diǎn)擊事件,而 UIView 會(huì)響應(yīng)磷脯。
    • 不同繼承關(guān)系:CALayer 繼承自 NSObject蛾找,UIView 由于要負(fù)責(zé)交互事件,所以繼承自 UIResponder赵誓。
  10. 為什么會(huì)同時(shí)有 UIView 和 CALayer腋粥,能否合成一個(gè)?

    • 單一職責(zé)原則架曹,UIView 和 CALayer 分別負(fù)責(zé)自己獨(dú)立的職責(zé)
    • CALayer的復(fù)用隘冲,CALayer除了服務(wù)于UIKit之外,還服務(wù)于AppKit绑雄,在mac開發(fā)中也會(huì)用到
  11. 渲染流水線中展辞,CPU 會(huì)負(fù)責(zé)哪些任務(wù)?

    • 點(diǎn)擊事件的處理
    • 顯示內(nèi)容的前置計(jì)算万牺,例如布局計(jì)算罗珍、圖片解碼等任務(wù)
  12. 離屏渲染為什么會(huì)有效率問題?

    1. 離屏渲染時(shí)由于 App 需要提前對部分內(nèi)容進(jìn)行額外的渲染并保存到 Offscreen Buffer脚粟,以及需要在必要時(shí)刻對 Offscreen Buffer 和 Framebuffer 進(jìn)行內(nèi)容切換覆旱,所以會(huì)需要更長的處理時(shí)間(實(shí)際上這兩步關(guān)于 buffer 的切換代價(jià)都非常大)
    2. 并且 Offscreen Buffer 本身就需要額外的空間,大量的離屏渲染可能早能內(nèi)存的過大壓力核无。與此同時(shí)扣唱,Offscreen Buffer 的總大小也有限,不能超過屏幕總像素的 2.5 倍团南。
    3. 可見離屏渲染的開銷非常大噪沙,一旦需要離屏渲染的內(nèi)容過多,很容易造成掉幀的問題吐根。所以大部分情況下正歼,我們都應(yīng)該盡量避免離屏渲染。
  13. 什么時(shí)候應(yīng)該使用離屏渲染拷橘?

    1. 一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài)局义,所以不得不使用離屏渲染。
    2. 出于效率目的冗疮,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中萄唇,達(dá)到復(fù)用的目的。
  14. shouldRasterize 光柵化是什么赌厅?

    • 光柵化的目的就是強(qiáng)制開啟離屏渲染穷绵。
    • 如果 layer 不能被復(fù)用,則沒有必要打開光柵化特愿;
    • 如果 layer 不是靜態(tài)的仲墨,需要被頻繁修改勾缭,比如處于動(dòng)畫之中,那么開啟了離屏渲染反而會(huì)影響效率目养;
    • 離屏渲染緩存內(nèi)容有時(shí)間限制俩由,緩存內(nèi)容 100ms 內(nèi)如果沒有被使用,那么它就會(huì)被丟棄調(diào)癌蚁,無法進(jìn)行復(fù)用幻梯;
    • 離屏渲染緩存內(nèi)容有空間限制,超過 2.5 倍屏幕像素大小的話努释,也會(huì)失效碘梢,且無法進(jìn)行復(fù)用;
  15. 有哪些常見的觸發(fā)離屏渲染的情況伐蒂?

    1. 使用了 mask 的 layer(layer.mask)
    2. 需要進(jìn)行裁剪的 layer(layer.masksToBounds / view.clipsToBounds)
    3. 設(shè)置了組透明度為 YES煞躬,并且透明度不為1 的layer(layer.allosGroupOpacity / layer.opacity)
    4. 添加了投影的 layer(layer.shadow*)
    5. 繪制了文字的 layer(UILabel,CATextLayer逸邦,Core Text等)
  16. cornerRadius 設(shè)置圓角會(huì)觸發(fā)離屏渲染嗎恩沛?

    • 不會(huì)觸發(fā)。
    • 設(shè)置了 maskToBounds(clipsToBounds)才有可能觸發(fā)
  17. 圓角觸發(fā)的離屏渲染有哪些解決方案缕减?

    • 如果是圖片雷客,可以讓UI提供帶圓角的圖片
    • 先將圖片圓角切好,然后使用切好圓角的圖片
    • 如果是按鈕背景圖桥狡,可以直接設(shè)置imageview的圓角
    • 繪制子layer搅裙,插入一個(gè)子layer作為顯示層(類比于按鈕上的imageview)
  18. 重寫 drawRect 方法會(huì)觸發(fā)離屏渲染嗎?

    • 不會(huì)觸發(fā)離屏渲染
    • drawRect 是在CPU中執(zhí)行的
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末总放,一起剝皮案震驚了整個(gè)濱河市呈宇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌局雄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件存炮,死亡現(xiàn)場離奇詭異炬搭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)穆桂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門宫盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人享完,你說我怎么就攤上這事灼芭。” “怎么了般又?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵彼绷,是天一觀的道長巍佑。 經(jīng)常有香客問我,道長寄悯,這世上最難降的妖魔是什么萤衰? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮猜旬,結(jié)果婚禮上脆栋,老公的妹妹穿的比我還像新娘。我一直安慰自己洒擦,他們只是感情好椿争,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著熟嫩,像睡著了一般丘薛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邦危,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天洋侨,我揣著相機(jī)與錄音,去河邊找鬼倦蚪。 笑死希坚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陵且。 我是一名探鬼主播裁僧,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慕购!你這毒婦竟也來了聊疲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤沪悲,失蹤者是張志新(化名)和其女友劉穎获洲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殿如,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贡珊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涉馁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片门岔。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烤送,靈堂內(nèi)的尸體忽然破棺而出寒随,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布妻往,位于F島的核電站互艾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒲讯。R本人自食惡果不足惜忘朝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望判帮。 院中可真熱鬧局嘁,春花似錦、人聲如沸晦墙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晌畅。三九已至但指,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抗楔,已是汗流浹背棋凳。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留连躏,地道東北人剩岳。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像入热,于是被迫代替她去往敵國和親拍棕。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345