iOS圖形顯示基本知識
從一個像素點到真正顯示在屏幕上,iOS到底在里面做了哪些工作,涉及到哪些Frameworks與Libraries?這是這一章想搞明白的問題。
1.1 圖形顯示原理
屏幕渲染相關 CRT(Cathode ray tube) 顯示器 和 LCD(Liquid-crystal display) 顯示器
可以參考文章
圖像想顯示到屏幕上使人肉眼可見都需借助像素的力量。簡單地說,每個像素由紅,綠,藍三種顏色組成,它們密集的排布在手機屏幕上,將任何圖形通過不同的色值表現出來。
計算機顯示的流程大致可以描述為將圖像轉化為一系列像素點的排列然后打印在屏幕上,由圖像轉化為像素點的過程又可以稱之為光柵化,就是從矢量的點線面的描述,變成像素的描述。
回溯歷史,可以從過去的 CRT 顯示器原理說起。CRT 的電子槍按照上面方式,從上到下一行行掃描,掃描完成后顯示器就呈現一幀畫面,隨后電子槍回到初始位置繼續下一次掃描。為了把顯示器的顯示過程和系統的視頻控制器進行同步,顯示器(或者其他硬件)會用硬件時鐘產生一系列的定時信號。當電子槍換到新的一行,準備進行掃描時,顯示器會發出一個水平同步信號(horizonal synchronization),簡稱 HSync;而當一幀畫面繪制完成后,電子槍回復到原位,準備畫下一幀前,顯示器會發出一個垂直同步信號(vertical synchronization),簡稱 VSync。顯示器通常以固定頻率進行刷新,這個刷新率就是 VSync 信號產生的頻率。盡管現在的設備大都是液晶顯示屏了,但原理仍然沒有變。1
如在 iPhone5 的上就有1,136×640=727,040個像素,而在15寸Retain的MBP上,這一數字達到15.5百萬以上,當你滾動整個屏幕的時候,數以百萬計的顏色單元必須以每秒60次的速度刷新,計算量可想而知。
1.2 iOS的顯示架構
從軟件層面上,iOS借助Core Graohics
,Core Animation
,Core Image
完成圖形的處理,它們又都是借助OpenGL ES
來完成底層的工作,其結構如下圖所示:
Display 的上一層便是圖形處理單元 GPU,GPU 是一個專門為圖形高并發計算而量身定做的處理單元。這也是為什么它能同時更新所有的像素,并呈現到顯示器上。它并發的本性讓它能高效的將不同紋理合成起來。因為涉及到各種圖形矩陣的計算,它跟CPU最直觀的區別在于浮點計算能力要超出CPU很多。所以在開發中,我們應該盡量讓CPU負責主線程的UI調動,把圖形顯示相關的工作交給GPU來處理,因為涉及到光柵化等一些工作時,CPU也會參與進來,這點在后面再詳細描述。
GPU Driver
是直接和 GPU 交流的代碼塊。不同的GPU是不同的性能怪獸,但是驅動使他們在下一個層級上顯示的更為統一,典型的下一層級有 OpenGL/OpenGL ES.
OpenGL
(Open Graphics Library) 是一個提供了 2D 和 3D 圖形渲染的 API。GPU 是一塊非常特殊的硬件,OpenGL 和 GPU 密切的工作以提高GPU的能力,并實現硬件加速渲染。
OpenGL 之上擴展出很多東西。在 iOS 上,幾乎所有的東西都是通過 Core Animation 繪制出來,然而在 OS X 上,繞過 Core Animation 直接使用 Core Graphics 繪制的情況并不少見。2
在硬件層面的調度我們可以看下圖所示:
計算機系統中 CPU、GPU、顯示器是以上面這種方式協同工作的。CPU 計算好顯示內容提交到 GPU,GPU 渲染完成后將渲染結果放入幀緩沖區,隨后視頻控制器會按照 VSync 信號逐行讀取幀緩沖區的數據,經過可能的數模轉換傳遞給顯示器顯示。
在最簡單的情況下,幀緩沖區只有一個,這時幀緩沖區的讀取和刷新都都會有比較大的效率問題。為了解決效率問題,顯示系統通常會引入兩個緩沖區,即雙緩沖機制。在這種情況下,GPU 會預先渲染好一幀放入一個緩沖區內,讓視頻控制器讀取,當下一幀渲染好后,GPU 會直接把視頻控制器的指針指向第二個緩沖器。如此一來效率會有很大的提升。
1.3 iOS圖形顯示流程
我們可以再從上層看一下iOS中不同的Frameworks和Libraries之間的一些聯系:
在最頂層的就是UIKit,一個在iOS中用來管理用戶圖形交互的Objc高級的框架,它由一系列的集合類構成,例如UIButton、UILabel,每一個都負責他們指定的UI Control角色。UIKit本身構建在一個叫Core Animation的框架之上。最后一部分是Core Graphics,曾經在Quartz(一個基于CPU的繪制引擎,在OS X系統上初次露臉)中被引入。這兩個較為底層的框架都是用C語言編寫的。
我們經常說到的硬件加速其實是指OpenGL,Core Animation/UIKit基于GPU之上對計算機圖形合成以及繪制的實現,直到目前為止,iOS上的硬件加速能力還是大大領先與android,后者由于依賴CPU的繪制,絕大多數的動畫實現都會讓人感覺明顯的卡頓3
CoreAnimation的渲染流程可以用下圖來概括:
在GPU的渲染過程中,我們能看到頂點著色器與像素著色器參與到圖像的處理。
在objc.io中有一篇文章進一步地闡明了頂點著色器與像素著色器 (GPU 加速下的圖像處理)4
1.4 補充知識
1.4.1 圖像多層次的合成—為何設置透明會增加GPU工作量5
合成 | Blended
在圖形世界中,合成是一個描述不同位圖如何放到一起來創建你最終在屏幕上看到圖像的過程。
一個不透明的紅色蓋在藍色上那我們看到的就是一個藍色,但一個半透明的紅色蓋在藍色讓我們得到的卻是一個紫色,這便是合成所要做的工作。
我們可以用下面這個公式來計算每一個像素:
R = S + D * ( 1 – Sa )
結果的顏色是源色彩(頂端紋理)+目標顏色(低一層的紋理)*(1-源顏色的透明度)。在這個公式中所有的顏色都假定已經預先乘以了他們的透明度。
假定兩個紋理都完全不透明,比如 alpha=1.如果目標紋理(低一層的紋理)是藍色(RGB=0,0,1),并且源紋理(頂層的紋理)顏色是紅色(RGB=1,0,0),因為 Sa 為1,所以結果為:
R = S
如果源顏色層為50%的透明,比如 alpha=0.5,既然 alpha 組成部分需要預先乘進 RGB 的值中,那么 S 的 RGB 值為(0.5, 0, 0),公式看起來便會像這樣:
所以當源紋理是完全不透明的時候,目標像素就等于源紋理。這可以省下 GPU 很大的工作量
這也是為什么 CALayer 有一個叫做 opaque 的屬性了。如果這個屬性為 NO,GPU 將不會做任何合成,而是簡單從這個層拷貝,不需要考慮它下方的任何東西(因為都被它遮擋住了)。
1.4.2 圖層對齊—為何圖片縮放會增加GPU工作量
當所有的像素是對齊的時候我們得到相對簡單的計算公式。每當 GPU 需要計算出屏幕上一個像素是什么顏色的時候,它只需要考慮在這個像素之上的所有 layer 中對應的單個像素,并把這些像素合并到一起。或者,如果最頂層的紋理是不透明的(即圖層樹的最底層),這時候 GPU 就可以簡單的拷貝它的像素到屏幕上。
當一個 layer 上所有的像素和屏幕上的像素完美的對應整齊,那這個 layer 就是像素對齊的。主要有兩個原因可能會造成不對齊。第一個便是滾動;當一個紋理上下滾動的時候,紋理的像素便不會和屏幕的像素排列對齊。另一個原因便是當紋理的起點不在一個像素的邊界上。
在這兩種情況下,GPU 需要再做額外的計算。它需要將源紋理上多個像素混合起來,生成一個用來合成的值。當所有的像素都是對齊的時候,GPU 只剩下很少的工作要做。
Core Animation 工具和模擬器有一個叫做 color misaligned images 的選項,當這些在你的 CALayer 實例中發生的時候,這個功能便可向你展示。
關于iOS設備的一些尺寸限制可以看這里:iOSRes