級別:★★☆☆☆
標籤:「GPU」「原生渲染」「大前端」「Flutter」
做者: 647
審校: QiShare團隊php
前言:
最近,研究了一下GPU以及App的渲染流程與原理。
首先,感謝 QiShare團隊 的指導與支持,以及 鵬哥(@snow) 對本文的審覈與幫助。
接下來,讓咱們開始咱們今天的探索之旅。css
GPU(
Graphics Processing Unit
):
又名圖形處理器,是顯卡的 「核心」。
主要負責圖像運算工做,具備高並行能力,經過計算將圖像顯示在屏幕像素中。html
GPU的工做原理,簡單來講就是:
—— 將 「3D座標」 轉換成 「2D座標」 ,再將 「2D座標」 轉換爲 「實際有顏色的像素」 。前端
那麼,GPU具體的工做流水線 會分爲「六個階段」,分別是:git
頂點着色器
=>
形狀裝配=>
幾何着色器=>
光柵化=>
片斷着色器=>
測試與混合github
該階段輸入的是頂點數據(Vertex Data
),頂點數據是一系列頂點的集合。頂點着色器主要的目的是把 3D 座標轉爲 「2D」 座標,同時頂點着色器能夠對頂點屬性進行一些基本處理。 ( 一句話簡單說,肯定形狀的點。)編程
該階段將頂點着色器輸出的全部頂點做爲輸入,並將全部的點裝配成指定圖元的形狀。 圖元(Primitive
) 用於表示如何渲染頂點數據,如:點、線、三角形。 這個階段也叫圖元裝配。 ( 一句話簡單說,肯定形狀的線。)json
該階段把圖元形式的一系列定點的集合做爲輸入,經過生產新的頂點,構造出全新的(或者其餘的)圖元,來生成幾何形狀。 ( 一句話簡單說,肯定三角形的個數,使之變成幾何圖形。)小程序
該階段會把圖元映射爲最終屏幕上相應的像素,生成片斷。片斷(Fragment
) 是渲染一個像素所須要的全部數據。 ( 一句話簡單說,將圖轉化爲一個個實際屏幕像素。)swift
該階段首先會對輸入的片斷進行裁切(Clipping
)。裁切會丟棄超出視圖之外的全部像素,用來提高執行效率。並對片斷(Fragment
)進行着色。 ( 一句話簡單說,對屏幕像素點着色。)
該階段會檢測片斷的對應的深度值(z 座標),來判斷這個像素位於其它圖層像素的前面仍是後面,決定是否應該丟棄。此外,該階段還會檢查 alpha
值( alpha
值定義了一個像素的透明度),從而對圖層進行混合。 ( 一句話簡單說,檢查圖層深度和透明度,並進行圖層混合。)
(PS:這個很關鍵,會在以後推出的「App性能優化實戰」系列博客中,是我會提到優化UI性能的一個點。)
所以,即便在片斷着色器中計算出來了一個像素輸出的顏色,在經歷測試與混合圖層以後,最後的像素顏色也可能徹底不一樣。
關於混合,GPU採用以下公式進行計算,並得出最後的實際像素顏色。
R = S + D * (1 - Sa)
含義:
R:Result,最終像素顏色。
S:Source,來源像素(上面的圖層像素)。
D:Destination,目標像素(下面的圖層像素)。
a:alpha,透明度。
結果 = S(上)的顏色 + D(下)的顏色 * (1 - S(上)的透明度)
GPU渲染流水線的完整過程,以下圖所示:
問:CPU vs. GPU?
這裏引用咱們團長(@月影)以前分享的一頁PPT:
因爲屏幕每一個像素點有每一幀的刷新需求,因此對GPU的並行工做效率要求更高。
簡單說完了GPU渲染的流水線,咱們來聊一聊App的渲染流程與原理。
iOS App的渲染主要分爲如下三種:
WebView
、類React Native
)說到原生渲染,首先想到的就是咱們最熟悉使用的iOS渲染框架:UIKit
、SwiftUI
、Core Animation
、Core Graphics
、Core Image
、OpenGL ES
、Metal
。
UIKit
:平常開發最經常使用的UI框架,能夠經過設置UIKit
組件的佈局以及相關屬性來繪製界面。其實自己UIView
並不擁有屏幕成像的能力,而是View
上的CALayer
屬性擁有展現能力。(UIView
繼承自UIResponder
,其主要負責用戶操做的事件響應,iOS事件響應傳遞就是通過視圖樹遍歷實現的。)SwiftUI
:蘋果在WWDC-2019
推出的一款全新的「聲明式UI」框架,使用Swift
編寫。一套代碼,便可完成iOS
、iPadOS
、macOS
、watchOS
的開發與適配。(關於SwiftUI
,我去年寫過一篇簡單的Demo
,可供參考:《用SwiftUI寫一個簡單頁面》)Core Animation
:核心動畫,一個複合引擎。儘量快速的組合屏幕上不一樣的可視內容。分解成獨立的圖層(CALayer
),存儲在圖層樹中。Core Graphics
:基於 Quartz
高級繪圖引擎,主要用於運行時繪製圖像。Core Image
:運行前圖像繪製,對已存在的圖像進行高效處理。OpenGL ES
:OpenGL for Embedded Systems
,簡稱 GLES
,是 OpenGL
的子集。由GPU
廠商定製實現,可經過C/C++
編程操控GPU
。Metal
:由蘋果公司實現,WWDC-2018
已經推出Metal2
,渲染性能比OpenGL ES
高。爲了解決OpenGL ES
不能充分發揮蘋果芯片優點的問題。那麼,iOS原生渲染的流程有那幾部分組成呢?
主要分爲如下四步:
第一步:更新視圖樹、圖層樹。(分別對應View
的層級結構、View
上的Layer
層級結構)
第二步:CPU開始計算下一幀要顯示的內容(包括視圖建立、佈局計算、視圖繪製、圖像解碼)。當 runloop
在 kCFRunLoopBeforeWaiting
和 kCFRunLoopExit
狀態時,會通知註冊的監聽,而後對圖層打包,打完包後,將打包數據發送給一個獨立負責渲染的進程 Render Server
。 前面 CPU 所處理的這些事情統稱爲 Commit Transaction
。
Render Server
後會被反序列化,獲得圖層樹,按照圖層樹的圖層順序、RGBA
值、圖層 frame
來過濾圖層中被遮擋的部分,過濾後將圖層樹轉成渲染樹,渲染樹的信息會轉給 OpenGL ES
/Metal
。Render Server
會調用 GPU
,GPU
開始進行前面提到的頂點着色器、形狀裝配、幾何着色器、光柵化、片斷着色器、測試與混合六個階段。完成這六個階段的工做後,就會將 CPU
和 GPU
計算後的數據顯示在屏幕的每一個像素點上。那麼,關於iOS原生渲染的總體流程,我也畫了一張圖:
對於WebView
渲染,其主要工做在WebKit
中完成。
WebKit
自己的渲染基於macOS
的Lay Rendering
架構,iOS
自己渲染也是基於這套架構。
所以,自己從渲染的實現方式來講,性能應該和原生差異不大。
但爲何咱們能明顯感受到使用WebView
渲染要比原生渲染的慢呢?
第一,首次加載。會額外多出網絡請求和腳本解析工做。 即便是本地網頁加載,WebView
也要比原生多出腳本解析的工做。 WebView
要額外解析HTML+CSS+JavaScript
代碼。
第二,語言解釋執行性能來看。JS的語言解析執行性能要比原生弱。 特別是遇到複雜的邏輯與大量的計算時,WebView
的解釋執行性能要比原生慢很多。
第三,WebView
的渲染進程是獨立的,每一幀的更新都要經過IPC
調用GPU
進程,會形成頻繁的IPC
進程通訊,從而形成性能消耗。而且,兩個進程沒法共享紋理資源,GPU
沒法直接使用context
光柵化,而必需要等待WebView
經過IPC
把context
傳給GPU
再光柵化。所以GPU
自身的性能發揮也會受影響。
所以,WebView
的渲染效率,是弱於原生渲染的。
JavaScriptCore
引擎作爲虛擬機方案)表明:React Native
、Weex
、小程序等。
咱們以 ReactNative
舉例:
React Native
的渲染層直接走的是iOS原生渲染,只不過是多了Json
+JavaScript
腳本解析工做。
經過JavaScriptCore
引擎將「JS」與「原生控件」產生相對應的關聯。
進而,達成經過JS
來操控iOS
原生控件的目標。 (簡單來講,這個json
就是一個腳本語言到本地語言的映射表,KEY
是腳本語言認識的符號,VALUE
是本地語言認識的符號。)
簡單介紹一下,
JavaScriptCore
:
JavaScriptCore
是iOS
原生與JS
之間的橋樑,其本來是WebKit
中解釋執行JavaScript
代碼的引擎。目前,蘋果公司有JavaScriptCore
引擎,谷歌有V8
引擎。
但與 WebView
同樣,RN也須要面臨JS語言解釋性能的問題。
所以,從渲染效率角度來講,WebView < 類ReactNative < 原生。 (由於json
的複雜度比html
+css
低)
首先,推薦YouTube上的一個視頻:《Flutter's Rendering Pipeline》專門講Flutter
渲染相關的知識。
能夠看到,Flutter
重寫了UI
框架,從UI
控件到渲染所有本身從新實現了。
不依賴 iOS
、Android
平臺的原生控件,
依賴Engine(C++)
層的Skia
圖形庫與系統圖形繪製相關接口。
所以,在不一樣的平臺上有了相同的體驗。
簡單來講,Flutter的界面由Widget
組成。
全部Widget
會組成Widget Tree
。
界面更新時,會更新Widget Tree
,
再更新Element Tree
,最後更新RenderObjectTree
。
更新Widget
的邏輯以下:
\ | newWidget == null | newWidget != null |
---|---|---|
child == null | 返回null | 返回新的Element |
child != null | 移除舊的child並返回null | 若是舊child被更新就返回child,不然返回新的Element |
接下來的渲染流程,
Flutter
渲染在 Framework
層會有 Build
、Widget Tree
、Element Tree
、RenderObject Tree
、Layout
、Paint
、Composited Layer
等幾個階段。
在 Flutter
的 C++
層,使用 Skia
庫,將 Layer
進行組合,生成紋理,使用 OpenGL
的接口向 GPU
提交渲染內容進行光柵化與合成。
提交到 GPU
進程後,合成計算,顯示屏幕的過程和 iOS
原生渲染基本是相似的,所以性能上是差很少的。
渲染方式 | 語言 | 性能 | 對應羣體 |
---|---|---|---|
原生 | Objective-C、Swift | ★★★ | iOS開發者 |
WebView | HTML、CSS、JavaScript | ★ | 前端開發者 |
類React Native | JavaScript | ★★ | 前端開發者 |
Flutter | Dart | ★★★ | Dart開發者 |
但Flutter的優點在於:
iOS
、Android
兩個平臺。Hot Reload
),省去了從新編譯代碼的時間,極大的提升了開發效率。「Fuchsia」
的發佈與加持。若是谷歌將來的新系統 Fuchsia
能應用到移動端,而且替代 Android
。因爲Fuchsia
的上層是Flutter
編寫的,所以Flutter
開發成爲了移動端領域的必選項。同時Flutter
又支持跨平臺開發,那麼其餘移動端領域的技術棧存在的價值會愈來愈低。固然,蘋果的但願在於 SwiftUI
。 若是 Fuchisa
最終失敗了,SwiftUI
也支持跨端了。同時,SwiftUI
自己也支持熱重載。也許也是一個將來呢。
期待,蘋果今年6月的線上WWDC-2020
吧,但願能給咱們帶來不同的驚喜。
參考與致謝:
1.《iOS開發高手課》(戴銘老師)
2.《你不知道的GPU》(月影大大)
3.《Flutter從加載到顯示》 (聖文前輩)
4.《UIKit性能調優實戰講解》(bestswifter)
5.《iOS - 渲染原理》
6.《iOS 圖像渲染原理》
7.《計算機那些事(8)——圖形圖像渲染原理》
8.《WWDC14:Advanced Graphics and Animations for iOS Apps》
瞭解更多iOS及相關新技術,請關注咱們的公衆號:
可添加以下小編微信,並備註加入QiShare技術交流羣,小編會邀請你加入《QiShare技術交流羣》。
關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)
推薦文章:
iOS 查看及導出項目運行日誌
Flutter Platform Channel 使用與源碼分析
開發沒切圖怎麼辦?矢量圖標(iconFont)上手指南
DarkMode、WKWebView、蘋果登陸是否必須適配?
iOS 接入 Google、Facebook 登陸(二)
iOS 接入 Google、Facebook 登陸(一)
Nginx 入門實戰 iOS中的3D變換(二)
iOS中的3D變換(一)
WebSocket 雙端實踐(iOS/ Golang)
今天咱們來聊一聊WebSocket(iOS/Golang)
奇舞團安卓團隊——aTaller
奇舞週刊