原文連接html
最近在 iOS 開發中作了較多動畫相關的編程工做。所以想借此機會深刻了解了一下 iOS 動畫及渲染相關原理。隨着對相關方面的深刻了解,發現這裏面涉及到從硬件底層到軟件框架等一系列相關知識。ios
本文將從相對底層的角度對計算圖形渲染原理進行簡要介紹,以做爲後續的知識儲備。git
做爲程序員,咱們或多或少知道可視化應用程序都是由 CPU 和 GPU 協做執行的。那麼咱們就先來了解一下二者的基本概念:程序員
這時候可能會產生一個問題:CPU 難道不能代替 GPU 來進行圖形渲染嗎?答案固然是確定的,不過在看了下面這個視頻就明白爲何要用 GPU 來進行圖形渲染了。github
GPU CPU 模擬繪圖視頻spring
使用 GPU 渲染圖形的根本緣由就是:速度。GPU 的並行計算能力使其可以快速將圖形結果計算出來並在屏幕的全部像素中進行顯示。編程
那麼像素是如何繪製在屏幕上的?計算機將存儲在內存中的形狀轉換成實際繪製在屏幕上的對應的過程稱爲 渲染。渲染過程當中最經常使用的技術就是 光柵化。segmentfault
關於光柵化的概念,如下圖爲例,假若有一道綠光與存儲在內存中的一堆三角形中的某一個在三維空間座標中存在相交的關係。那麼這些處於相交位置的像素都會被繪製到屏幕上。固然這些三角形在三維空間中的先後關係也會以遮擋或部分遮擋的形式在屏幕上呈現出來。一句話總結:光柵化就是將數據轉化成可見像素的過程。數組
GPU 則是執行轉換過程的硬件部件。因爲這個過程涉及到屏幕上的每個像素,因此 GPU 被設計成了一個高度並行化的硬件部件。緩存
下面,咱們來簡單瞭解一下 GPU 的歷史。
GPU 還未出現前,PC 上的圖形操做是由 視頻圖形陣列(VGA,Video Graphics Array) 控制器完成。VGA 控制器由鏈接到必定容量的DRAM上的存儲控制器和顯示產生器構成。
1997 年,VGA 控制器開始具有一些 3D 加速功能,包括用於 三角形生成、光柵化、紋理貼圖 和 陰影。
2000 年,一個單片處圖形處理器繼承了傳統高端工做站圖形流水線的幾乎每個細節。所以誕生了一個新的術語 GPU 用來表示圖形設備已經變成了一個處理器。
隨着時間的推移,GPU 的可編程能力愈發強大,其做爲可編程處理器取代了固定功能的專用邏輯,同時保持了基本的 3D 圖形流水線組織。
近年來,GPU 增長了處理器指令和存儲器硬件,以支持通用編程語言,並創立了一種編程環境,從而容許使用熟悉的語言(包括 C/C++)對 GPU 進行編程。
現在,GPU 及其相關驅動實現了圖形處理中的 OpenGL
和 DirectX
模型,從而容許開發者可以輕易地操做硬件。OpenGL
嚴格來講並非常規意義上的 API,而是一個第三方標準(由 khronos 組織制定並維護),其嚴格定義了每一個函數該如何執行,以及它們的輸出值。至於每一個函數內部具體是如何實現的,則由 OpenGL 庫的開發者自行決定。實際 OpenGL 庫的開發者一般是顯卡的生產商。DirectX
則是由 Microsoft 提供一套第三方標準。
GPU 圖形渲染流水線的主要工做能夠被劃分爲兩個部分:
GPU 圖形渲染流水線的具體實現可分爲六個階段,以下圖所示。
第一階段,頂點着色器。該階段的輸入是 頂點數據(Vertex Data) 數據,好比以數組的形式傳遞 3 個 3D 座標用來表示一個三角形。頂點數據是一系列頂點的集合。頂點着色器主要的目的是把 3D 座標轉爲另外一種 3D 座標,同時頂點着色器能夠對頂點屬性進行一些基本處理。
第二階段,形狀(圖元)裝配。該階段將頂點着色器輸出的全部頂點做爲輸入,並將全部的點裝配成指定圖元的形狀。圖中則是一個三角形。圖元(Primitive) 用於表示如何渲染頂點數據,如:點、線、三角形。
第三階段,幾何着色器。該階段把圖元形式的一系列頂點的集合做爲輸入,它能夠經過產生新頂點構造出新的(或是其它的)圖元來生成其餘形狀。例子中,它生成了另外一個三角形。
第四階段,光柵化。該階段會把圖元映射爲最終屏幕上相應的像素,生成片斷。片斷(Fragment) 是渲染一個像素所須要的全部數據。
第五階段,片斷着色器。該階段首先會對輸入的片斷進行 裁切(Clipping)。裁切會丟棄超出視圖之外的全部像素,用來提高執行效率。
第六階段,測試與混合。該階段會檢測片斷的對應的深度值(z
座標),判斷這個像素位於其它物體的前面仍是後面,決定是否應該丟棄。此外,該階段還會檢查 alpha
值( alpha
值定義了一個物體的透明度),從而對物體進行混合。所以,即便在片斷着色器中計算出來了一個像素輸出的顏色,在渲染多個三角形的時候最後的像素顏色也可能徹底不一樣。
關於混合,GPU 採用以下公式進行計算,並得出最後的顏色。
R = S + D * (1 - Sa)
複製代碼
關於公式的含義,假設有兩個像素 S(source) 和 D(destination),S 在 z
軸方向相對靠前(在上面),D 在 z
軸方向相對靠後(在下面),那麼最終的顏色值就是 S(上面像素) 的顏色 + D(下面像素) 的顏色 * (1 - S(上面像素) 顏色的透明度)。
上述流水線以繪製一個三角形爲進行介紹,能夠爲每一個頂點添加顏色來增長圖形的細節,從而建立圖像。可是,若是讓圖形看上去更加真實,須要足夠多的頂點和顏色,相應也會產生更大的開銷。爲了提升生產效率和執行效率,開發者常常會使用 紋理(Texture) 來表現細節。紋理是一個 2D 圖片(甚至也有 1D 和 3D 的紋理)。紋理 通常能夠直接做爲圖形渲染流水線的第五階段的輸入。
最後,咱們還須要知道上述階段中的着色器事實上是一些程序,它們運行在 GPU 中成千上萬的小處理器核中。這些着色器容許開發者進行配置,從而能夠高效地控制圖形渲染流水線中的特定部分。因爲它們運行在 GPU 中,所以能夠下降 CPU 的負荷。着色器可使用多種語言編寫,OpenGL 提供了 GLSL(OpenGL Shading Language) 着色器語言。
早期的 GPU,不一樣的着色器對應有着不一樣的硬件單元。現在,GPU 流水線則使用一個統一的硬件來運行全部的着色器。此外,nVidia 還提出了 CUDA(Compute Unified Device Architecture) 編程模型,能夠容許開發者經過編寫 C 代碼來訪問 GPU 中全部的處理器核,從而深度挖掘 GPU 的並行計算能力。
下圖所示爲 GPU 內部的層級結構。最底層是計算機的系統內存,其次是 GPU 的內部存儲,而後依次是兩級 cache:L2 和 L1,每一個 L1 cache 鏈接至一個 流處理器(SM,stream processor)。
GPU 上的各級存儲系統與對應層級的計算機存儲系統相比要小很多。
此外,GPU 內存並不具備一致性,也就意味着並不支持併發讀取和併發寫入。
下圖所示爲 GPU 中每一個流處理器的內部結構示意圖。每一個流處理器集成了一個 L1 Cache。頂部是處理器核共享的寄存器堆。
至此,咱們大體瞭解了 GPU 的工做原理和內部結構,那麼實際應用中 CPU 和 GPU 又是如何協同工做的呢?
下圖所示爲兩種常見的 CPU-GPU 異構架構。
左圖是分離式的結構,CPU 和 GPU 擁有各自的存儲系統,二者經過 PCI-e 總線進行鏈接。這種結構的缺點在於 PCI-e 相對於二者具備低帶寬和高延遲,數據的傳輸成了其中的性能瓶頸。目前使用很是普遍,如PC、智能手機等。
右圖是耦合式的結構,CPU 和 GPU 共享內存和緩存。AMD 的 APU 採用的就是這種結構,目前主要使用在遊戲主機中,如 PS4。
注意,目前不少 SoC 都是集成了CPU 和 GPU,事實上這僅僅是在物理上進行了集成,並不意味着它們使用的就是耦合式結構,大多數採用的仍是分離式結構。耦合式結構是在系統上進行了集成。
在存儲管理方面,分離式結構中 CPU 和 GPU 各自擁有獨立的內存,二者共享一套虛擬地址空間,必要時會進行內存拷貝。對於耦合式結構,GPU 沒有獨立的內存,與 GPU 共享系統內存,由 MMU 進行存儲管理。
圖形應用程序調用 OpenGL
或 Direct3D
API 功能,將 GPU 做爲協處理器使用。API 經過面向特殊 GPU 優化的圖形設備驅動向 GPU 發送命令、程序、數據。
下圖所示爲分離式異構系統中 GPU 的資源管理模型示意圖。
下圖所示爲 CPU-GPU 異構系統的工做流,當 CPU 遇到圖像處理的需求時,會調用 GPU 進行處理,主要流程能夠分爲如下四步:
介紹屏幕圖像顯示的原理,須要先從 CRT 顯示器原理提及,以下圖所示。CRT 的電子槍從上到下逐行掃描,掃描完成後顯示器就呈現一幀畫面。而後電子槍回到初始位置進行下一次掃描。爲了同步顯示器的顯示過程和系統的視頻控制器,顯示器會用硬件時鐘產生一系列的定時信號。當電子槍換行進行掃描時,顯示器會發出一個水平同步信號(horizonal synchronization),簡稱 HSync;而當一幀畫面繪製完成後,電子槍回覆到原位,準備畫下一幀前,顯示器會發出一個垂直同步信號(vertical synchronization),簡稱 VSync。顯示器一般以固定頻率進行刷新,這個刷新率就是 VSync 信號產生的頻率。雖然如今的顯示器基本都是液晶顯示屏了,但其原理基本一致。
下圖所示爲常見的 CPU、GPU、顯示器工做方式。CPU 計算好顯示內容提交至 GPU,GPU 渲染完成後將渲染結果存入幀緩衝區,視頻控制器會按照 VSync
信號逐幀讀取幀緩衝區的數據,通過數據轉換後最終由顯示器進行顯示。
最簡單的狀況下,幀緩衝區只有一個。此時,幀緩衝區的讀取和刷新都都會有比較大的效率問題。爲了解決效率問題,GPU 一般會引入兩個緩衝區,即 雙緩衝機制。在這種狀況下,GPU 會預先渲染一幀放入一個緩衝區中,用於視頻控制器的讀取。當下一幀渲染完畢後,GPU 會直接把視頻控制器的指針指向第二個緩衝器。
雙緩衝雖然能解決效率問題,但會引入一個新的問題。當視頻控制器還未讀取完成時,即屏幕內容剛顯示一半時,GPU 將新的一幀內容提交到幀緩衝區並把兩個緩衝區進行交換後,視頻控制器就會把新的一幀數據的下半段顯示到屏幕上,形成畫面撕裂現象,以下圖:
爲了解決這個問題,GPU 一般有一個機制叫作垂直同步(簡寫也是 V-Sync),當開啓垂直同步後,GPU 會等待顯示器的 VSync 信號發出後,才進行新的一幀渲染和緩衝區更新。這樣能解決畫面撕裂現象,也增長了畫面流暢度,但須要消費更多的計算資源,也會帶來部分延遲。