iOS 初識離屏渲染

2021-10-08 20:39:45 字數 3742 閱讀 9265

nslog(@"show time");
【 ⚠️⚠️⚠️  離屏渲染易造成滑動卡頓掉幀,要盡量避免離屏渲染~~ 】

off-screen-rendering,發生在gpu上,指的是gpu在當前螢幕緩衝區以外新開闢乙個緩衝區進行渲染工作。

1、要消耗資源新建乙個緩衝區;

2、上下文切換代價大。離屏渲染過程需要多次切換上下文:先從當前螢幕(on-screen)切換到離屏(off-screen)進行渲染,渲染結束後上下文再從離屏切換到當前螢幕,而上下文的切換代價較大。

以上因素使得gpu在離屏渲染時產生了額外的開銷,由於垂直同步的機制,如果在乙個 hsync 時間內,cpu 或者 gpu 沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留之前的內容不變。這就是介面掉幀卡頓的原因。

- 設定圓角(同時設定masktobounds=yes才觸發);

- 圖層蒙版

- 設定陰影

- 光柵化

官方對離屏渲染產生效能問題也進行了優化:

ios 9.0 之前uiimageview跟uibutton設定圓角都會觸發離屏渲染。

ios 9.0 之後uibutton設定圓角會觸發離屏渲染,而uiimageview裡png設定圓角不會觸發離屏渲染了,如果設定其他陰影效果之類的還是會觸發離屏渲染的。

我們設定圓角一般通過如下方式:

imageview.layer.cornerradius = cgfloat(10);

imageview.layer.maskstobounds = yes;

這樣處理的渲染機制是gpu在當前螢幕緩衝區外新開闢乙個渲染緩衝區進行工作,也就是離屏渲染,這會給我們帶來額外的效能損耗,如果這樣的圓角操作達到一定數量,會觸發緩衝區的頻繁合併和上下文的的頻繁切換,效能的代價會巨集觀地表現在使用者體驗上——掉幀。

使用貝塞爾曲線uibezierpath和core graphics框架畫出乙個圓角

uiimageview *imageview = [[uiimageview alloc] initwithframe:cgrectmake(100,100,100,100)];

imageview.image = [uiimage imagenamed:@"myimg"];

//開始對imageview進行畫圖

uigraphicsbeginimagecontextwithoptions(imageview.bounds.size,no,1.0);

//使用貝塞爾曲線畫出乙個圓形圖

[[uibezierpath bezierpathwithroundedrect:imageview.bounds cornerradius:imageview.frame.size.width]addclip];

[imageview drawrect:imageview.bounds];

imageview.image=uigraphicsgetimagefromcurrentimagecontext();

//結束畫圖

uigraphicsendimagecontext();

[self.view addsubview:imageview];

使用cashapelayer和uibezierpath設定圓角

uiimageview *imageview = [[uiimageview alloc]initwithframe:cgrectmake(100, 100, 100, 100)];

imageview.image = [uiimage imagenamed:@"myimg"];

uibezierpath *maskpath = [uibezierpath bezierpathwithroundedrect:imageview.bounds byroundingcorners:uirectcornerallcorners cornerradii:imageview.bounds.size];

cashapelayer *masklayer = [[cashapelayer alloc]init];

//設定大小

masklayer.frame = imageview.bounds;

//設定圖形樣子

masklayer.path = maskpath.cgpath;

imageview.layer.mask = masklayer;

[self.view addsubview:imageview];

對於方案2需要解釋的是:

cashapelayer繼承於calayer,可以使用calayer的所有屬性值;

cashapelayer需要貝塞爾曲線配合使用才有效果;

使用cashapelayer(屬於coreanimation)與貝塞爾曲線可以實現不在view的drawrect(繼承於coregraphics走的是cpu,消耗的效能較大)方法中畫出一些想要的圖形

cashapelayer動畫渲染直接提交到手機的gpu當中,相較於view的drawrect方法使用cpu渲染而言,其效率極高,能大大優化記憶體使用情況。

總的來說就是用cashapelayer的記憶體消耗少,渲染速度快,建議使用優化方案2。

對於shadow,如果圖層是個簡單的幾何圖形或者圓角圖形,我們可以通過設定shadowpath

來優化效能,能大幅提高效能。示例如下:

imageview.layer.shadowcolor = [uicolor graycolor].cgcolor;

imageview.layer.shadowopacity = 1.0;

imageview.layer.shadowradius = 2.0;

uibezierpath *path = [uibezierpath bezierpathwithrect:imageview.frame];

imageview.layer.shadowpath = path.cgpath;

我們還可以通過設定shouldrasterize屬性值為yes來強制開啟離屏渲染。其實就是光柵化(rasterization)。既然離屏渲染這麼不好,為什麼我們還要強制開啟呢?當乙個影象混合了多個圖層,每次移動時,每一幀都要重新合成這些圖層,十分消耗效能。當我們開啟光柵化後,會在首次產生乙個位圖快取,當再次使用時候就會復用這個快取。但是如果圖層發生改變的時候就會重新產生位圖快取。所以這個功能一般不能用於uitableviewcell中,cell的復用反而降低了效能。最好用於圖層較多的靜態內容的圖形。而且產生的點陣圖快取的大小是有限制的,一般是2.5個螢幕尺寸。在100ms之內不使用這個快取,快取也會被刪除。所以我們要根據使用場景而定。

當我們需要圓角效果時,可以使用一張中間透明蒙上去

使用shadowpath指定layer陰影效果路徑

使用非同步進行layer渲染(facebook開源的非同步繪製框架asyncdisplaykit)

設定layer的opaque值為yes,減少複雜圖層合成

盡量使用不包含透明(alpha)通道的資源

盡量設定layer的大小值為整形值

直接讓美工把切成圓角進行顯示,這是效率最高的一種方案

很多情況下使用者上傳進行顯示,可以讓服務端處理圓角

使用**手動生成圓角image設定到要顯示的view上,利用uibezierpath(coregraphics框架)畫出來圓角

nslog(@"end...");

iOS之離屏渲染

on screen rendering 意為當前螢幕渲染,指的是gpu的渲染操作是在當前用於顯示的螢幕緩衝區中進行。off screen rendering 意為離屏渲染,指的是gpu在當前螢幕緩衝區以外新開闢乙個緩衝區進行渲染操作。特殊的離屏渲染 如果將不在gpu的當前螢幕緩衝區中進行的渲染都稱為...

iOS 離屏渲染研究

gpu渲染機制 gpu螢幕渲染有以下兩種方式 離屏渲染的觸發方式 設定了以下屬性時,都會觸發離屏繪製 其中shouldrasterize 光柵化 是比較特別的一種 光柵化概念 將圖轉化為乙個個柵格組成的圖象。光柵化特點 每個元素對應幀緩衝區中的一畫素。shouldrasterize yes在其他屬性...

iOS 筆記 離屏渲染

gpu渲染機制 特殊的離屏渲染 如果將不在gpu的當前螢幕緩衝區中進行的渲染都稱為離屏渲染,那麼就還有另一種特殊的 離屏渲染 方式 cpu渲染。完成,渲染得到的bitmap最後再交由gpu用於顯示。備註 coregraphic通常是執行緒安全的,所以可以進行非同步繪製,顯示的時候再放回主線程,乙個簡...