謎一樣的GPU
手機,現在已經是人手一部甚至兩部了,餐廳酒吧、地鐵巴士、馬路街邊隨處可見的低頭族大家早就見慣不怪,在飯桌上如果你發現沒有人低頭看手機的話反而會懷疑自己是不是到了外星球。
吸引人們對手機目不轉睛的自然是它顯示的內容, 相對于個人電腦剛剛問世時候只能呈現有限的文字以及低分辨率的畫面而言,現在的智能手機已經可以在巴掌大的屏幕上做到高達 3840×2160 的分辨率,能呈現非常豐富的畫面元素。
臺式機和智能手機雖然存在較大的性能分野,但是兩者間一直在互相借鑒,例如操作系統界面都采用了硬件三維加速來強化用戶體驗:豐富元素的表現力以及實現界面的視覺擴展(例如窗口的旋轉切換),而實現這些三維硬件加速的正是許多手機、電腦文章報道中的 GPU,比如說Qualcomm(高通)的Adreno、蘋果的PowerVR、ARM的Mali。
因為應用上的相似性,移動端的GPU和臺式機GPU發展最近這幾年幾乎都是齊頭并進,理論上如果將移動端的GPU放大后也是可以拿到臺式機上使用的。
?。∟VIDIA推出的NV10)
GPU的全稱是 Graphics Processing Unit,也即是圖形處理單元的意思。這個概念的最早確定是NVIDIA(英偉達)公司在1999年發布型號為 GeForce (代號NV10)的三維芯片時首次提出,當時的定義是:
三角形變換能力達到每秒一千萬個三角形以上的三維芯片。
時至今日,當年NVIDIA提出的GPU定義相信已經很少人記得,GPU這個名詞因為微軟DirectX 7采用而普及,此后 NVIDIA、微軟、ATI(后來的 AMD)、Intel以及幾乎整個相關行業、媒體大量采用而成為大家非常熟悉的名詞,不管是臺式機、服務器、工作站還是游戲機、移動設備,它已經是無處不在。
相較于GPU的定義,GPU 到底是怎么一回事,它是如何實現三維渲染以及為何后來可以勝任非圖形計算,知道的人就更少了,本文嘗試在這方面做一些簡單的介紹。
GPU到底是如何實現三維渲染呢?
要了解GPU是如何進行渲染操作其實并不難,宏觀角度來可以將其簡化為下面的樣子:
應用程序-》幾何處理-》光柵處理
在圖形處理中,應用程序執行的相關操作包括了碰撞偵測、全局加速算法、動畫處理、物理模擬等。
幾何處理就是對圖元進行處理,所謂圖元是指點、線、面這類幾何體,而光柵化則是將確定了位置、大小和光照的幾何體映射到屏幕空間柵格化后的處理,例如像素著色、貼圖、混合。
在沒有圖形芯片(顯卡)之前,幾何處理、光柵處理都是由 CPU 或者 FPU(浮點單元)、SIMD (單指令多數據)單元協助來完成,隨著芯片技術的進步,其中的幾何處理、光柵處理開始逐步放到專門的芯片上執行,之后這些專用芯片又被集成到一塊,逐漸形成了現在的 GPU。移動處理器同樣經歷PC這一過程,比如說高通最早的處理器MSM7225/7625就沒有集成GPU ,甚至是2D處理都是交由CPU完成。
(智能手機與PC一樣,經歷過無GPU時代)
應用程序把需要進行三維渲染或者計算的數據和指令遞交給 GPU,由GPU來執行幾何處理以及光柵處理,這樣的處理方式被稱作流水線(pipeline)。
采用流水線的方式可以將工作拆分為若干個處理環節,也就是所謂的工位(stage)或者功能階段,這些工位本身也可以繼續拆分成若干部分,也可以實現(部分的)并行化。
幾何處理階段需要做些什么啥呢?
幾何處理階段執行的是頂點、多邊形級別的處理。這一步可以拆解為 5 個工位或者說 5 個步驟:
•對模型及視圖進行變換(transform)
•頂點著色
•投影
•裁剪
•屏幕映射
模型及視圖的變換
模型變換
由于每個模型都有自己的坐標,因此在成為屏幕上的畫面對象之前,模型需要變換為多個空間或者坐標系。
作為對象的模型空間(Model Space,或者叫模型自空間)的坐標被稱作模型坐標,在經過坐標變換后,模型就會處于世界坐標或者世界空間(World Space)里,也就是確定了該模型在場景中的方向、位置。
我們允許在場景中存在多個模型的拷貝(被稱作引用),這些大小一樣的引用可以在同一個場景中有不同的位置、方向。
視圖(Viewport,或者視口)變換
現在的實時渲染場景中包含的對象(模型)可以有很多個,但是只有被攝像機(或者說觀察者,也即是設定的視角覆蓋)的區域才會被渲染。這個攝像機在世界空間里有一個用來擺放的位置和面向的方向。
為了實現接下來的投影、裁剪處理,攝像機和模型都需要進行視圖變換這個操作,目的是將攝像機放置在坐標原點上,使其正對的方向為 Z 軸(負向),Y 軸指向上(上圖是從攝像機正上方俯視,所以沒法給出 Y 軸),X 軸指向右。
頂點著色
所謂著色就是指確定光照在物料上所呈現效果的操作,這類操作既可能運行于幾何階段的模型頂點上,也可能運行于光柵階段的各個像素上,也就是所謂的頂點著色和像素著色。
在頂點著色的時候,各個頂點需要存放若干個相關的物料數據,例如頂點位置、法線、色彩以及其他任何進行著色處理計算相關的數字信息。
頂點著色的計算結果(可以是色彩、向量、紋理坐標以及任何其他著色數據)會被發送到光柵化階段進行插值處理。
投影
在完成了著色處理后,渲染系統會把可視體轉換為一個位于(-1, -1, -1)到(1, 1, 1)的單元立方體(unit-cube)中,這個立方體被稱作正則觀察體(canonical view volume),使用到的投影方式一般有兩種:平行投影和透視投影。
前一種主要在 CAD 等軟件中使用,后一種因為模擬了我們人類的視覺體驗,所以在游戲或者虛擬現實中經常使用:
上圖分別是一部 iPhone 6s Plus 以平行投影和透視投影的方式呈現在屏幕上的效果。
三角形裁剪
只有在可視體內的圖元會被傳送到在屏幕上繪制這些圖元的光柵階段,在進行裁剪動作的時候,如果圖元有頂點落在可視體之外,裁剪的時候就會將可視體之外的這部分剪切掉并且在可視體與圖元相交的位置生成新的頂點,而位于立方體外部的舊圖元就會被拋棄掉。
屏幕映射
經過上一步裁剪后的位于可視體內的圖元會被傳遞到屏幕映射階段,此時的坐標信息依然是三維的。圖元的 X、Y 坐標被變換到屏幕坐標系,屏幕坐標再加上 Z 軸坐標就被稱作窗口坐標。
我們假定場景要渲染到一個最小角落坐標為(x1, y1)和最大角落坐標為 (x2, y2)的窗口中,也就是 x1 《 x2,y1 《 y2。此時,屏幕映射執行的是一個縮放處理后的轉換操作。Z 軸坐標并不受此操作的影響。
現在,新的 x 軸、 y 軸坐標就是屏幕坐標,有對應的屏幕像素位置,不再是之前投影處理后的那個立方體所采用的映像坐標系統。
光柵處理階段要干些什么呢?
在獲得了經過變換和投影處理的頂點及其相關聯的著色信息后,光柵化處理階段的目的就是計算并設置好被對象覆蓋區域的像素顏色。這個處理被稱作光柵化或者掃描轉換,也就是把二維坐標上包含深度(Z 軸)信息和各種相關著色信息的頂點到屏幕上像素的轉換。
這個處理階段一般可以拆分為 4 個工位:
1、三角形設定
2、三角形遍歷
3、像素著色
4、輸出合并
三角形設定
這一步會進行三角形表面的微分以及其他關于三角形表面數據的計算,計算出來的數據會被用于掃描轉換以及幾何階段所產生的各種著色數據的插值處理。在GPU上這一步會采用固定硬件功能單元來實現。
三角形遍歷
這一步用作確定像素的中心是否被三角形覆蓋 ,如果該像素被三角形覆蓋的話,就會生成對應的片元(fragment)。
查找哪些樣本或者像素是否位于三角形內通常被稱作三角形遍歷或者掃描轉換。
每個三角形對應片元的屬性都是由該三角形的三個頂點數據插值而成,例如片元的深度值以及來自幾何階段的著色數據。
像素著色
所有的逐像素(per-pixel)著色計算都在這一步執行,使用的輸入數據是之前插值的著色數據。像素著色發送到下一個工位的計算的結果可能是一個色彩值也可能是多個色彩值。
和三角形設定以及三角形遍歷不同采用固定硬件功能單元不同的是,現在的像素著色都是由可編程的GPU內核執行。
在像素著色所依賴的眾多技術中最為重要的就是貼圖(texturing),所謂貼圖就是把一張或者多張圖片“貼”到對象上。
輸出合并
在這一步執行的操作主要是將之前步驟生成的色彩信息進行合并形成最終輸出的像素色彩。
用于存放像素色彩信息的緩存被稱作色彩緩存,一般情況下是以紅、綠、藍三個色元的方式存放,此外還有一個用于存放像素對應深度信息值的深度緩存(一般采用 Z-Buffer)。
在GPU中實現這一步的功能單元有幾種叫法,例如 ROP、Output Merger 或者 Back-End。
在這個階段,Output Merger 會根據深度緩存(depth buffer 或者 Z-buffer)存放的深度信息判斷是否更新色彩緩存中的色彩值。
例如當前像素計算出來的深度值(例如是 0.1)比深度緩存中對應像素的值?。ɡ缡?0.2),則表示當前像素的三角形比色彩緩存存放的像素所對應的三角形更靠近“攝像頭”,于是GPU會對該圖元的色彩進行計算并把新計算出來的色彩值和深度值更新到色彩緩存和深度緩存中,否則的話就不會更新當前像素的緩存。
在整個場景完成渲染后,色彩緩存中存放的都是從攝像機視角位置看到的可視圖元色彩值。
這樣處理的好處是三角形可以使用任意次序來渲染,但是如果圖元或者說三角形是部分透明的話,則必須依照從遠到近的三角形層次進行渲染。這是 Z-Buffer 的主要缺點之一。
像素除了色彩緩存和深度緩存外,還有其他利用通道或者緩存的技術來用于過濾和捕捉片元(fragmet)信息。
通常和色彩一起存放于色彩緩存的阿爾法通道(Alpha Channel)包含了每個像素的相對不透明值,開發人員可以在進行深度測試之前對到來的片元先執行名為阿爾法測試(Alpha Test)的操作。如果片元的 alpha 值測試(一般是等于、大于等簡單的操作)為“假”,那么這個像素的后續處理操作就會被省略掉。這個操作通常用于確保完全透明的片元不會對 Z-buffer 構成影響。
此外,還可能會涉及到名為蠟板緩存(Stencil Buffer)的技術。Stencil Buffer 作為一個離屏緩存一般用于存放已渲染圖元的位置,它通常用于進行一些特殊效果的處理,例如將一個“實心”圓存放到蠟板緩存中,之后配合其他操作,就可以將被覆蓋圖元的色彩值控制為只有在位于這個實心圓中的時候才被呈現,相當于一個遮罩的作用。
以上這些緩存都被統稱為幀緩存,但是在一般情況下,幀緩存特指色彩緩存和深度緩存。由于畫面渲染是需要時間的,為了保證出到顯示器或者顯示屏的時候圖元都是已經完成渲染的,人們引入了雙緩存技術,渲染中的被稱為后臺緩存(back buffer),完成渲染的稱作前臺緩存(front buffer),在后臺緩存完成渲染后,馬上變成前臺緩存,而前臺緩存就切換為后臺緩存,以此類推。
片元(fragmet)和像素(pixel)的區別?
上文中我們提到了片元和像素,像素是相對容易理解的,嚴格來說,像素就是對應屏幕上的一個點,它有表示屏幕位置的 x、y 坐標以及顏色的紅綠藍(RGB)值(像素是沒有 Alpha 通道值的),圖形流水線所作的所有事情都是為了給輸入的圖元計算在屏幕上像素的顏色。
那么,片元又是怎么一回事呢?
在三角形遍歷和輸出合并之間計算的柵格數據就是片元,它們是像素的前身,在遍歷的時候由頂點內插而成。片元除了具備像素的 x、y 坐標外,還有表示深度的 z 坐標以及頂點的屬性信息(顏色、片元法線、紋理坐標)。
正如前面所說的,深度坐標記錄的是圖元的相對距離,在深度測試的時候被遮蔽(或者阿爾法測試中為透明)的圖元會在輸出合并階段被拋棄掉,。
片元這個說法是 OpenGL 或者大多數實時圖形渲染文獻中的概念,而 D3D 則沒有這么嚴格的區分,片元和像素都統稱為像素。
我們這里討論的都是以實時三角形渲染為例,除此以外還有其他三維渲染流水線形式,例如 micropolygon(微型多邊形)、Voxel(體素) 渲染。
現在的GPU流水線也是遵照這樣的圖形流水線來設計,在一段時間里,出于成本效益的考慮,GPU 的各個功能單元都是有專門的電路來實現的。
不過在新式的GPU中,可編程部分(例如頂點程序、片元程序)由于指令集得以統一,所以都采用了同樣的計算單元來跑。
公認必須采用固定功能硬件單元來實現的主要是三角形設置、遍歷以及輸出合并單元,英特爾曾經試圖在名為 Larrabee 的GPU項目里將這些工位采用通用單元來執行,但是最終結果是不了了之,至少說明現階段或者在未來可見的較長時期里,三角形設置/遍歷以及輸出合并的最合理實現方式還是使用固定功能硬件單元。
(Intel Larrabee)
在現實中GPU并不僅僅是上面討論的三維處理、計算單元,廣義的GPU還應該包括視頻編解碼單元、掃描輸出單元、總線單元、存儲單元,手機GPU現在都和 CPU、基帶、周邊等單元集成到同一個芯片里,這樣的芯片被稱作 SoC(片上系統),SoC 并非是手機獨有的,在此之前的單片機(例如洗衣機等里就有,一般幾塊錢一顆)其實就是 SoC 的一種實現方式。
GPU 在移動應用中能發揮的作用
GPU 最初是為三維游戲加速設計的,所以它最拿手的自然是三維游戲加速,現在無論是安卓、iOS 都有不少三維游戲大作,例如:勞拉 Go、FIFA 系列、真人快打 X、Hitman Sniper、Marvel Future Fight、Godfire: Rise of Promotheus、Over Kill、Implosion、Battle supremacy 等等。
除了游戲渲染加速外,智能手機的操作系統界面也是采用了GPU硬件加速的,例如選單的彈出、桌面平移等。和桌面操作系統使用三維加速相比,移動操作系統由于受到屏幕空間小的約束,因此三維加速體驗帶來的空間感拓展是更加不可或缺的。從安卓4.0系統開始,不少用戶就感覺到系統流暢性大幅度提高正是因為從系統層面上線了GPU硬件加速。
通用計算加速原本也是在非移動應用上的技術,不過隨著智能手機的高速發展,已經有了不少開發人員利用GPU的通用計算能力來進行一些有意義的加速。
例如在蘋果機上有一款名為極拍的 APP,利用蘋果手機的GPU對攝像頭拍攝的視頻進行實時降噪處理,可以將蘋果手機的夜間視頻達到單反攝像機的夜間視頻拍攝效果,這是非常具有實用意義的。
另一個典型是高通驍龍處理器,ATI是最早提倡通用計算加速的GPU制造商,而Adreno 200 GPU正源自ATI Imageon項目。發展至今,在高通驍龍820上的Adreno 530已經能實現輔助全景照片合成、幫助“認知計算平臺”Zeroth識別物體。
(“認知計算平臺”Zeroth演示)
GPU 在這三方面的作用對我們手機的日常應用產生了直接的影響,許多手機媒體口頭整天掛著的使用體驗,其實都離不開 GPU。
淺析GPU的常見術語
在下篇文章中SIMD、Core、GPU 中的線程、統一著色器、紋理單元這些GPU常見術語對一般讀者來說都是相當陌生或者容易產生困擾,在此先進行淺析。
SIMD:
Single Instruction Multiple Data,單指令多數據流,目前所有的GPU在基本功能單元層面都屬于SIMD(業界也接受NVIDIA提出的SIMT——單指令多線程),一般是16路SIMD或者32路 SIMD。
“內核”或者core:
目前在GPU行業或者GPU行銷上被嚴重濫用的名詞,它被用作指代SIMD一條Lane(計算通道)上的單元集合,里面可能有一個單周期 32位FMA(積和熔加運算)運算器、一個雙周期64位FMA運算器等等,GPU廠商把GPU里的SIMD Lane數加起來就對外宣稱有多少個內核。
這樣的說法是否屬于錯誤宣傳還真不好說,因為目前并沒有什么法律文件規定怎樣的集合才算是一個內核。
不過對于計算機科學來說,微架構里對內核約定俗成的看法是它必須有一個PC(程序計算器,它是一個寄存器,其中存放的一般是指向該內核要執行的下一條指令的地址)。
這樣的話,一個GPU內核顯然不能是SIMD單元中的一個Lane,它的層級至少應該高一級。
所以,GPU上相對嚴格的“內核”概念單元對應的應該是類似與AMD GCN的Compute Unite、NVIDIA Maxwell中的SMM、PowerVR Series 6/7的USC等名詞命名的單元集合,在OpenCL中,這個層級的單元集合被稱作Compute Unit(計算單元,簡稱CU,AMD的GCN微架構也采用Compute Unit這個術語,完全對應OpenCL的Compute Unit),而GPU廠商的行銷術語 “內核” 或者“core” 在OpenCL中被稱作Process Element,簡稱PE。
GPU中的線程:
現在的GPU都采用了多層次線程技術,按照硬件開發商提供的文檔,對應SIMD Lane的被稱作thread(OpenCL中稱作work-item,在圖形渲染的時候你可以將其看作是屏幕上的一個像素),是最小的線程單位;
往上的一層線程單位在新的OpenCL被稱作sub-group,NVIDIA稱作warp 或者thread warp,AMD稱作wavefront,屬于GPU執行調度的最小硬件線程單位。再往上就是workgroup(NVIDIA 稱之為thread block)和NDRange(NVIDIA稱之為Grid,由若干個workgroup或者thread block組成)。
Workgroup的對應GPU硬件關系是Compute Unit,同一時間里Compute Unit跑的都是一個workgroup,而Grid則對應GPU的一個partition(分區,在設備或者說加速器允許的情況下,OpenCL可以把一個設備分成若干個分區來使用)。
你可以把飯粒比作是work-item,而每一口飯則算是一個sub-group,一碗飯看作是一個 workgroup,飯煲看作是NDRange(sorry,我的比喻未必很恰當)。
統一著色器
在 DX10以后,由于幾何、像素的指令格式一樣,使得幾何和像素處理可以在同樣的單元上執行,自此以后臺式GPU都采用了統一著色器設計,以確保著色器的利用率提高。
紋理單元
紋理單元或者說紋理映射單元是GPU中計算紋理坐標和獲得紋理樣本的單元。在繪制某個對象的時候,每個紋理單元進行一個紋理取樣動作,不同的繪制對象每次的取樣動作都可以更改使用的紋理。紋理單元的性能指標一般用 TexOps/s 來表示,表示每秒的紋理操作數,不同的紋理格式和不同的紋理過濾算法以及硬件實現、內存帶寬都會對這個指標產生影響。紋理單元一般和若干個著色器綁定在一起。
(審核編輯: 滄海一土)
分享