<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    iOS 下離屏渲染的問題

    共 6023字,需瀏覽 13分鐘

     ·

    2022-01-12 18:11


    ????關(guān)注后回復(fù) “進群” ,拉你進程序員交流群????

    Offscreen Rendering

    如何檢測你的項目中是否 觸發(fā)了離屏渲染問題

    那么為何有一些會觸發(fā)離屏渲染,而有一些卻不會觸發(fā)呢?下面我們開始深入的探索。

    離屏渲染的具體過程

    我們知道通常的渲染流程是這樣的:

    App通過CPU和GPU的合作,不停的將內(nèi)容渲染完成放入FrameBuffer幀緩存區(qū),而屏幕顯示不斷從FrameBuffer中獲取內(nèi)容,顯示實時的內(nèi)容。

    但是離屏渲染的流程是這樣的:

    在普通的情況下,GPU直接將渲染好的內(nèi)容放入FrameBuffer中,但是在離屏渲染時不同,需要先額外創(chuàng)建離屏渲染緩存區(qū)OffscreenBuffer。將提前渲染好的內(nèi)容放入其中,等到合適的時機再將OffSreeBuffer中的內(nèi)容進一步疊加、渲染。完成后將結(jié)果切換到FrameBuffer中。

    離屏渲染的效率問題

    從上面的流程來看,離屏渲染時,由于App需要提前對部分內(nèi)容進行額外的渲染并保存到OffScreenBuffer,以及需要在必要時對OffScreenBuffer和FrameBuffer進行內(nèi)容切換,所以會需要更長的處理時間。(實際上這兩步切換的代價是非常大的)。

    OffScreenBuffer本身就需要額外的空間,大量的離屏渲染可能造成內(nèi)存過大的壓力。與此同時,OffScreenBuffer的總大小也是有限的:不能超過屏幕總像素的2.5倍。

    可見離屏渲染的開銷非常大,一旦需要離屏渲染的內(nèi)容過多,就容易造成掉幀問題,所以大部分情況下,我們要避免出現(xiàn)離屏渲染。

    為什么要用離屏渲染

    既然離屏渲染會造成性能問題,那么為什么還要使用離屏渲染?

    其實主要是以下兩種原因:

    1. 一些特殊的效果需要使用額外的OffScreenBuffer來保存渲染中間的狀態(tài),所以不得不使用離屏渲染
    2. 出于效率的目的,可以將內(nèi)容提前渲染并保存到OffScreenBuffer中,從而達到復(fù)用的目的

    例如,第一種原因,也就是不得不使用離屏渲染的情況。一般都是系統(tǒng)自動觸發(fā)。如:陰影、圓角等。比如我們使用的蒙版(mask)功能,因為最終的結(jié)果是有超過一層的渲染結(jié)果進行疊加,所以必須要利用額外的內(nèi)存空間對中間的渲染結(jié)果進行保存,因此系統(tǒng)會默認(rèn)觸發(fā)離屏渲染.

    比如,iOS8開始提供模糊特效UIBlurEffectView:

    1. 先渲染需要模糊的內(nèi)容本身;
    2. 對內(nèi)容進行縮放;
    3. 對上一步結(jié)果進行垂直模糊;
    4. 對上一步結(jié)果進行橫向模糊;
    5. 最后一步,將模糊后的結(jié)果進行疊加合成,實現(xiàn)最終完整的模糊效果。

    在這樣的5次過程,系統(tǒng)也會自動觸發(fā)離屏渲染,用來保存復(fù)雜的特效下,利用額外的內(nèi)存空間對中間的結(jié)果進行保存,以便最后進行效果的合成。

    離屏渲染的第二種原因:shouldRasterize 光柵化

    開啟光柵化后,就會主動觸發(fā)離屏渲染。Render Server會強制將CALayer渲染位圖結(jié)果bitmap保存下來,這樣下次渲染可以直接復(fù)用,提高效率;而保存下來的bitmap就已經(jīng)包含了layer和sublayer、圓角、陰影、透明度等。

    如果layer的構(gòu)成包含了以上幾種元素,結(jié)構(gòu)非常復(fù)雜且還需要重復(fù)利用,可以考慮開啟光柵化;因為layer 上的圓角、陰影、透明度等會由系統(tǒng)自動觸發(fā)離屏渲染,那么打開光柵化就可以節(jié)約第二次以及以后的渲染時間。

    而多層的subLayer的情況由于不會自動觸發(fā)離屏渲染,所以相比之下會花費第一次離屏渲染的時間,但是可以節(jié)約后續(xù)重復(fù)的渲染開銷。

    使用光柵化的注意點:

    1. 如果layer并不能被復(fù)用,則沒必要開啟;
    2. 如果layer不是靜態(tài)的,需要 被頻繁修改,比如處于動畫之中,那么開啟離屏渲染反而影響效率了;
    3. 離屏渲染緩存內(nèi)容有時間限制,緩存內(nèi)容如果100ms沒被復(fù)用,那么就會被丟棄,無法進行復(fù)用;
    4. 離屏渲染緩存空間有限,超過2.5倍屏幕像素大小的話,也會失效,且無法復(fù)用

    圓角的離屏渲染探索

    通常來講,設(shè)置了Layer的圓角效果后,會自動觸發(fā)離屏渲染,但是具體什么情況下設(shè)置圓角才會觸發(fā)離屏渲染?

    如上圖所示,Layer由3層組成,我們設(shè)置圓角通常是用下面的代碼:

    view.layer.cornerRadius = 2;

    CornerRadius - Apple 官方介紹

    根據(jù)蘋果的描述,上面這句代碼,只會默認(rèn)設(shè)置backgroundColor和border的圓角,而不會設(shè)置content的圓角,除非設(shè)置了layer.maskToBounds為true(對應(yīng)view的clickToBounds屬性)。

    如果只設(shè)置了CornerRadius而沒有設(shè)置masksToBounds,由于不需要疊加裁剪,此時是不會觸發(fā)離屏渲染的。而當(dāng)設(shè)置了裁剪屬性時,由于maskToBounds會對layer以及所有 的subLayer的content都進行裁剪,所以不得不觸發(fā)離屏渲染。

    view.layer.masksToBounds = true //觸發(fā)離屏渲染的原因

    離屏渲染的邏輯

    剛才說圓角加上masksToBounds時,因為masksToBounds會對layer上的所有內(nèi)容進行裁剪,從而誘發(fā)了離屏渲染,那么這個過程具體是怎么回事呢?我們來仔細(xì)研究一下:

    • 在普通的layer繪制中,上層的subLayer會覆蓋下層的subLayer,下層的subLayer在繪制完成后就可以拋棄了,從而節(jié)約空間提高效率。
    • 所有subLayer一次繪制完畢后,整個繪制過程完成,就可以進行后續(xù)的呈現(xiàn)了。假設(shè)我們需要繪制一個三層的subLayer,并不設(shè)置裁剪和圓角,那么整個繪制過程就如下圖所示:

    繪制完進行display

    設(shè)置了CornerRadius以及masksToBounds

    當(dāng)我們設(shè)置了CornerRadius以及masksToBounds進行圓角加裁剪時,masksToBounds裁剪屬性會應(yīng)用到所有的subLayer上,也就意味著所有的subLayer都要進行圓角+裁剪,意味著所有的subLayer在第一次繪制后,并不能立刻丟棄,而必須保存在OffScreenBuffer中等待下一輪的圓角加裁剪操作,這樣便引發(fā)了離屏渲染。

    實際上,并不單只有圓角加裁剪會觸發(fā)離屏渲染。如果設(shè)置了透明度和組透明(layer.allowsGroupOpacity+layer.opacity),陰影屬性(shadowOffset)等,都會產(chǎn)生這樣的離屏渲染,因為這些都不是對單一的layer進行處理,而是對layer及其所有 的subLayer進行處理,從而引發(fā)離屏渲染。

    避免圓角離屏渲染的手段:

    除了盡量減少圓角裁剪的使用,還有什么別的辦法可以避免圓角+裁剪引起的離屏渲染?

    由于剛才提到,圓角引起離屏渲染的本質(zhì)是裁剪的疊加,導(dǎo)致了masksToBounds對layer及其所有的subLayer進行了二次處理,那么我們只要避免使用masksToBounds進行二次處理,而是對所有的subLayer進行預(yù)處理,就可以只進行“畫家算法(先繪制離屏幕較遠(yuǎn)的圖層,然后繪制距離屏幕較近的圖層,根據(jù)深度值,確定繪制順序)”,用一次疊加就完成繪制。

    有哪些可行方案

    1.換資源
    直接使用帶圓角的圖片,或者替換背景色為帶圓角的純色背景圖,從而避免使用圓角裁剪。不過這周方法需要依賴具體情況,并不通用 。

    2.mask
    再增加一個和背景色相同的遮罩mask覆蓋在最上層,蓋住四個角,營造出圓角的形狀。但這種方式難以解決背景色為圖片 或漸變色的情況。注意這里的mask并不是指的layer上的mask,而是用兩個view的疊加

    3.UIBezierPath
    用貝塞爾曲線繪制閉合帶圓角的矩形,在上下文設(shè)置只有內(nèi)部可見,再將不帶圓角的layer渲染成圖片,添加到貝塞爾矩形中。這種方法效率較高,但是layer的布局一旦改變,貝塞爾曲線都需要手動進行重新繪制,所以需要對frame、color等進行手動監(jiān)聽并重繪。

    4.CoreGraphics
    重寫 drawRect:,用coreGraphics相關(guān)方法,在需要應(yīng)用圓角時進行手動繪制。不過CoreGraphics效率也有限,如果多次調(diào)用也會有效率問題。

    觸發(fā)離屏渲染的幾種情況

    1. 使用了mask的layer(layer.mask)
    2. 需要進行裁剪的layer(layer.masksToBounds / view.clipsToBounds)
    3. 設(shè)置了組透明度YES,并且透明度不為1的layer (layer.allowsGroupOpacity/layer.opacity)
    4. 添加了投影的layer(layer.shadow)
    5. 采用了光柵化的layer(layer.shouldRasterize)
    6. 繪制了文字的layer (UILabel,CATextLayer,CoreText等)
    舉幾個例子,加深下理解:
    //按鈕存在背景圖片  因為 會對button的layer和其imageView的layer進行圓角加裁剪 所以會觸發(fā)離屏渲染
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
    [self.view addSubview:btn1];
    btn1.layer.cornerRadius = 50;
    btn1.clipsToBounds = YES;
    //按鈕存在背景圖片  只設(shè)置了clipsToBounds,裁剪是針對于imageview的,所以不會觸發(fā)離屏渲染(not offscreen rendering)
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
    [self.view addSubview:btn1];
    btn1.imageView.layer.cornerRadius = 50;
    btn1.clipsToBounds = YES;
    //按鈕不存在背景圖片 雖然設(shè)置了圓角+裁剪,但是只有一層layer  所以不會觸發(fā)離屏渲染
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn2.frame = CGRectMake(100, 180, 100, 100);
    btn2.backgroundColor = [UIColor blueColor];
    [self.view addSubview:btn2];
    btn2.layer.cornerRadius = 50;
    btn2.clipsToBounds = YES;
    //UIImageView 設(shè)置了圖片+背景色; 背景色和圖片兩層layer都需要進行圓角+裁剪  所以會觸發(fā)離屏渲染
    UIImageView *img1 = [[UIImageView alloc]init];
    img1.frame = CGRectMake(100, 320, 100, 100);
    img1.backgroundColor = [UIColor blueColor];
    img1.image = [UIImage imageNamed:@"btn.png"];
    [self.view addSubview:img1];
    img1.layer.cornerRadius = 50;
    img1.layer.masksToBounds = YES;
    //UIImageView 只設(shè)置了圖片,無背景色; 只有一層 layer 需要圓角+裁剪 所以不會觸發(fā)離屏渲染
    UIImageView *img2 = [[UIImageView alloc]init];
    img2.frame = CGRectMake(100, 480, 100, 100);
    img2.image = [UIImage imageNamed:@"btn.png"];
    [self.view addSubview:img2];
    img2.layer.cornerRadius = 50;
    img2.layer.masksToBounds = YES;

    作者:聰莞

    https://juejin.cn/post/7043676783735996424 


    -End-

    最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

    點擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

    在看點這里好文分享給更多人↓↓

    瀏覽 96
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    国产又黄又大又粗的视频 | 日韩欧美高清 | 羞羞午夜福利视频 | 国 产 成 人 在 线 视频观看 | 日韩一区二区免费视频 |