標籤:
一、概念理解
OpenGL中,GPU螢幕渲染有以下兩種方式:
意為當前螢幕渲染,指的是GPU的渲染操作是在當前用於顯示的螢幕緩衝區中進行。
意為離屏渲染,指的是GPU在當前螢幕緩衝區以外新開闢一個緩衝區進行渲染操作。
圖 1- 1
通常來說,電腦系統中 CPU、GPU、顯示器是以上面這種方式協同工作的。CPU 計算好顯示內容提交到 GPU,GPU 渲染完成後將渲染結果放入框架緩衝區,隨後視頻控制器會按照 VSync 訊號如1-4所示,逐行讀取框架緩衝區的資料,經過可能的數模轉換傳遞給顯示器顯示。
二、離屏渲染觸發方式
設定了以下屬性時,都會觸發離屏繪製:
- shouldRasterize(光柵化)
- masks(遮罩)
- shadows(陰影)
- edge antialiasing(消除鋸齒)
- group opacity(不透明)
需要注意的是,如果shouldRasterize被設定成YES,在觸發離屏繪製的同時,會將光柵化後的內容緩衝起來,如果對應的layer及其sublayers沒有發生改變,在下一幀的時候可以直接複用。這將在很大程度上提升渲染效能。
而其它屬性如果是開啟的,就不會有緩衝,離屏繪製會在每一幀都發生。
相比於當前螢幕渲染,離屏渲染的代價是很高的,主要體現在兩個方面:
1.建立新緩衝區
要想進行離屏渲染,首先要建立一個新的緩衝區。
2. 環境切換
離屏渲染的整個過程,需要多次切換上下文環境:先是從當前螢幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束以後,將離屏緩衝區的渲染結果顯示到螢幕上有需要將上下文環境從離屏切換到當前螢幕。而上下文環境的切換是要付出很大代價的。
在 VSync 訊號到來後,系統圖形服務會通過 CADisplayLink 等機制通知 App,App 主線程開始在 CPU 中計算顯示內容,比如視圖的建立、布局計算、圖片解碼、文本繪製等。隨後 CPU 會將計算好的內容提交到 GPU 去,由 GPU 進行變換、合成、渲染。隨後 GPU 會把渲染結果提交到框架緩衝區去,等待下一次 VSync 訊號到來時顯示到螢幕上。由於垂直同步的機制,如果在一個 VSync 時間內,CPU 或者 GPU 沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留之前的內容不變。這就是介面卡頓的原因。從中可以看到,CPU 和 GPU 不論哪個阻礙了顯示流程,都會造成掉幀現象。所以開發時,也需要分別對 CPU 和 GPU 壓力進行評估和最佳化。
iOS 的顯示系統是由 VSync 訊號驅動的,VSync 訊號由硬體時鐘產生,每秒鐘發出 60 次(這個值取決裝置硬體,比如 iPhone 真機上通常是 59.97)。iOS 圖形服務接收到 VSync 訊號後,會通過 IPC 通知到 App 內。App 的 Runloop 在啟動後會註冊對應的 CFRunLoopSource 通過 mach_port 接收傳過來的時鐘訊號通知,隨後 Source 的回調會驅動整個 App 的動畫與顯示。
Core Animation 在 RunLoop 中註冊了一個 Observer,監聽了 BeforeWaiting 和 Exit 事件。當一個觸摸事件到來時,RunLoop 被喚醒,App 中的代碼會執行一些操作,比如建立和調整視圖層級、設定 UIView 的 frame、修改 CALayer 的透明度、為視圖添加一個動畫;這些操作最終都會被 CALayer 標記,並通過 CATransaction 提交到一個中間狀態去。當上面所有操作結束後,RunLoop 即將進入休眠(或者退出)時,關注該事件的 Observer 都會得到通知。這時 Core Animation 註冊的那個 Observer 就會在回調中,把所有的中間狀態合并提交到 GPU 去顯示;如果此處有動畫,通過 DisplayLink 穩定的重新整理機制會不斷的喚醒runloop,使得不斷的有機會觸發observer回調,從而根據時間來不斷更新這個動畫的屬性值並繪製出來。
為了不阻塞主線程,Core Animation 的核心是 OpenGL ES 的一個抽象物,所以大部分的渲染是直接提交給GPU來處理。 而Core Graphics/Quartz 2D的大部分繪製操作都是在主線程和CPU上同步完成的,比如自訂UIView的drawRect裡用CGContext來畫圖。
AsyncDisplay介紹
阻塞主線程的繪製任務主要是這三大類:Layout計算視圖布局文本寬高、Rendering文本渲染圖片解碼圖片繪製、UIKit對象建立更新釋放。除了UIKit和CoreAnimation相關操作必須在主線程中進行,其他的都可以挪到後台線程非同步執行。
AsyncDisplay通過抽象UIView的關聯建立了ASDisplayNode類,ASDisplayNode是安全執行緒的,它可以在後台線程建立和修改。Node 剛建立時,並不會在內部建立 UIView 和 CALayer,直到第一次在主線程訪問 view 或 layer 屬性時,它才會在內部產生對應的對象。當它的屬性(比如frame/transform)改變後,它並不會立刻同步到其持有的 view 或 layer 去,而是把被改變的屬性儲存到內部的一個中間變數,稍後在需要時,再通過某個機制一次性設定到內部的 view 或 layer。從而可以實現非同步並行作業。
AsyncDisplay實現依賴如同Core Animation在runloop中註冊observer事件來觸發。
同樣附上一篇介紹AsyncDisplay的好文 《iOS保持介面流暢的技巧和AsyncDisplay介紹》
離屏渲染學習筆記