01-計算機原理-計算機圖形渲染原理

語言: CN / TW / HK

[TOC]

通篇內容

前言

最近在 iOS 開發中做了較多動畫相關的功課,在進一步探索動畫及渲染相關原理的時候,隨著逐步深入瞭解發現這裡面涉及到從硬體底層到軟體框架等一系列相關知識。 本文將從相對底層的角度對計算圖形渲染原理進行簡要介紹,以作為後續的知識儲備.

一、CPU與GPU

視覺化應用程式都是由 CPU 和 GPU 協作執行的,但是CPU和GPU其設計的理念不相同,主要的工作目標也不一樣

1. CPU 與 GPU 的架構

對於現代計算機系統,簡單來說可以大概視作三層架構:硬體作業系統程序。 對於移動端來說, 程序就是app ,而 CPU 與 GPU 是硬體層面的重要組成部分

  1. CPU 與 GPU 提供了計算能力,通過作業系統被 app 呼叫

    • CPU(Central Processing Unit): 現代計算機的三大核心部分之一,是整個系統的運算和控制單元。CPU 內部的流水線結構使其擁有一定程度的平行計算能力。
    • GPU(Graphics Processing Unit):GPU是一種可進行繪圖運算工作的專用微處理器
      • GPU 能夠生成 2D/3D 的圖形影象和影片,從而能夠支援基於視窗的作業系統、圖形使用者介面、影片遊戲、視覺化影象應用和影片播放,是連線計算機和顯示終端的紐帶。
      • GPU 具有非常強的平行計算能力
  2. CPU 和 GPU 其設計目標就是不同的,它們分別針對了兩種不同的應用場景

    • 設計理念不同
      • CPU 是運算核心與控制核心,需要有很強的運算通用性,相容各種資料型別,同時也需要能處理大量不同的跳轉、中斷等指令,因此 CPU 的內部結構更為複雜
      • 而 GPU 則面對的是型別統一、更加單純的運算,也不需要處理複雜的指令,但也肩負著更大的運算任務
  3. 架構不同
    • 因為設計理念不同,所以 CPU 與 GPU 的架構也不同
      • CPU
        • 因為 CPU 面臨的情況更加複雜,因此從上圖中也可以看出,CPU 擁有更多的快取空間 Cache 以及複雜的控制單元,計算能力並不是 CPU 的主要訴求
        • CPU 是設計目標是低時延,更多的快取記憶體也意味著可以更快地訪問資料;
        • 同時複雜的控制單元也能更快速地處理邏輯分支,更適合序列計算。
      • GPU
      • GPU 擁有更多的計算單元 Arithmetic Logic Unit, 具有更強的計算能力 ,同時也具有更多的控制單元
      • GPU 基於大吞吐量而設計,每一部分快取都連線著一個流處理器(stream processor),更加適合大規模的平行計算
  4. 結論
    • 使用 GPU 渲染圖形的根本原因就是:速度。GPU 的平行計算能力使其能夠快速將圖形結果計算出來並在螢幕的所有畫素中進行顯示。
    • 那麼畫素是如何繪製在螢幕上的?計算機將儲存在記憶體中的形狀轉換成實際繪製在螢幕上的對應的過程稱為 渲染。渲染過程中最常用的技術就是 光柵化
    • 關於光柵化的概念,以下圖為例

      • 假如有一道綠光與儲存在記憶體中的一堆三角形中的某一個在三維空間座標中存在相交的關係。
      • 那麼這些處於相交位置的畫素都會被繪製到螢幕上。
      • 當然這些三角形在三維空間中的前後關係也會以遮擋或部分遮擋的形式在螢幕上呈現出來。
      • 一句話總結:光柵化就是將資料轉化成可見畫素的過程。
      • GPU 則是執行轉換過程的硬體部件。由於這個過程涉及到螢幕上的每一個畫素,所以 GPU 被設計成了一個高度並行化的硬體部件。

2. GPU的歷史(瞭解)

  • GPU 還未出現前,PC 上的圖形操作是由 影片圖形陣列(VGA,Video Graphics Array) 控制器完成。VGA 控制器由連線到一定容量的DRAM上的儲存控制器和顯示產生器構成。

  • 1997 年,VGA 控制器開始具備一些 3D 加速功能,包括用於 三角形生成、光柵化、紋理貼圖 和 陰影

  • 2000 年,一個單片處圖形處理器繼承了傳統高階工作站圖形流水線的幾乎每一個細節。因此誕生了一個新的術語 GPU 用來表示圖形裝置已經變成了一個處理器。

  • 隨著時間的推移,GPU 的可程式設計能力愈發強大,其作為可程式設計處理器取代了固定功能的專用邏輯,同時保持了基本的 3D 圖形流水線組織(可程式設計管線 代替 固定管線)

  • 近年來,GPU 增加了處理器指令和儲存器硬體,以支援通用程式語言,並創立了一種程式設計環境,從而允許使用熟悉的語言(包括 C/C++)對 GPU 進行程式設計(GLSL語言

  • 如今, GPU 及其相關驅動 實現了圖形處理中的 OpenGLDirectX 模型,從而允許開發者能夠輕易地操作硬體

  • OpenGL 嚴格來說並不是常規意義上的 API,而是一個第三方標準(由 khronos 組織制定並維護),其嚴格定義了每個函式該如何執行,以及它們的輸出值。至於 每個函式內部具體是如何實現的,則由 OpenGL 庫的開發者自行決定。實際 OpenGL 庫的開發者通常是顯示卡的生產商

  • DirectX 則是由 Microsoft 提供一套第三方標準

二、GPU圖形渲染流水線

1. App影象渲染流程

影象渲染流程粗粒度地大概分為下面這些步驟: 上述影象渲染流水線中,除了第一部分 Application 階段,後續主要都由 GPU 負責。 渲染流水線中各個部分的具體任務:

1.1 Application 應用處理階段:得到圖元

  • 這個階段具體指的就是影象在應用中被處理的階段,此時還處於 CPU 負責的時期。
  • 在這個階段應用可能會對影象進行一系列的操作或者改變,最終將新的影象資訊傳給下一階段
  • 這部分資訊被叫做圖元(primitives),通常是三角形、線段、頂點等。

1.2 Geometry 幾何處理階段:處理圖元

進入這個階段之後,以及之後的階段,就都主要由 GPU 負責了。此時 GPU 可以拿到上一個階段傳遞下來的圖元資訊,GPU 會對這部分圖元進行處理,之後輸出新的圖元。這一系列階段包括:

  • 頂點著色器(Vertex Shader):這個階段中會將圖元中的頂點資訊進行視角轉換、新增光照資訊、增加紋理等操作。
  • 形狀裝配(Shape Assembly):圖元中的三角形、線段、點分別對應三個 Vertex、兩個 Vertex、一個 Vertex。這個階段會將 Vertex 連線成相對應的形狀。
  • 幾何著色器(Geometry Shader):額外新增額外的Vertex,將原始圖元轉換成新圖元,以構建一個不一樣的模型。簡單來說就是基於通過三角形、線段和點構建更復雜的幾何圖形。

1.3 Rasterization 光柵化階段:圖元轉換為畫素

  • 光柵化的主要目的是將幾何渲染之後的圖元資訊,轉換為一系列的畫素,以便後續顯示在螢幕上。這個階段中會根據圖元資訊,計算出每個圖元所覆蓋的畫素資訊等,從而將畫素劃分成不同的部分。
  • 一種簡單的劃分就是根據中心點,如果畫素的中心點在圖元內部,那麼這個畫素就屬於這個圖元。如上圖所示,深紫色的線就是圖元資訊所構建出的三角形;而通過是否覆蓋中心點,可以遍歷出所有屬於該圖元的所有畫素,即淺紫色部分。

1.4 Pixel 畫素處理階段:處理畫素,得到點陣圖

經過上述光柵化階段,我們得到了圖元所對應的畫素,此時,我們需要給這些畫素填充顏色和效果。所以最後這個階段就是給畫素填充正確的內容,最終顯示在螢幕上。這些經過處理、蘊含大量資訊的畫素點集合,被稱作點陣圖(bitmap)。也就是說,Pixel 階段最終輸出的結果就是點陣圖,過程具體包含:

  • 片段著色器(Fragment Shader):也叫做 Pixel Shader,這個階段的目的是給每一個畫素 Pixel 賦予正確的顏色。
    • 顏色的來源就是之前得到的頂點、紋理、光照等資訊。
    • 由於需要處理紋理、光照等複雜資訊,所以這通常是整個系統的效能瓶頸。
  • 測試與混合(Tests and Blending):也叫做 Merging 階段,這個階段主要處理片段的前後位置以及透明度。

    • 這個階段會檢測各個著色片段的深度值 z 座標,從而判斷片段的前後位置,以及是否應該被捨棄。
    • 同時也會計算相應的透明度 alpha 值,從而進行片段的混合,得到最終的顏色。
  • 這些點可以進行不同的排列和染色以構成圖樣。

    • 當放大點陣圖時,可以看見賴以構成整個影象的無數單個方塊。
    • 只要有足夠多的不同色彩的畫素,就可以製作出色彩豐富的圖象,逼真地表現自然界的景象。
    • 縮放和旋轉容易失真,同時檔案容量較大。

2. GPU 的渲染流程:

GPU 圖形渲染流水線的主要工作可以被劃分為兩個部分:

  • 把 3D 座標轉換為 2D 座標
  • 把 2D 座標轉變為實際的有顏色的畫素

2.1 GPU 圖形渲染流水線的具體實現可分為六個階段,如下圖所示:

* 頂點著色器(Vertex Shader) * 形狀裝配(Shape Assembly),又稱 圖元裝配 * 幾何著色器(Geometry Shader) * 光柵化(Rasterization) * 片段著色器(Fragment Shader) * 測試與混合(Tests and Blending)

上圖就是一個三角形被渲染的過程中,GPU 所負責的渲染流水線。可以看到簡單的三角形繪製就需要大量的計算,如果再有更多更復雜的頂點、顏色、紋理資訊(包括 3D 紋理),那麼計算量是難以想象的。這也是為什麼 GPU 更適合於渲染流程

2.2 OpenGL ES工作過程

這個過程實際上就是圖形程式設計介面的工作過程。OpenGL ES工作過程:

  • 第①階段,頂點著色器(Vertex Shader)

    • 該階段的輸入是 頂點資料(Vertex Data) 資料,比如以陣列的形式傳遞 3 個 3D 座標用來表示一個三角形;
    • 頂點資料是一系列頂點的集合。頂點著色器主要的目的是把 3D 座標轉為另一種 3D 座標,同時頂點著色器可以對頂點屬性進行一些基本處理;
    • 在這個階段中可以對圖元中的頂點資訊進行視角轉換、新增光照資訊、增加紋理等操作
  • 第②階段,形狀(圖元)裝配(Shape Assembly)

    • 圖元(Primitive) 用於表示如何渲染頂點資料,如:點、線、三角形;
    • 圖元中的三角形、線段、點分別對應三個 Vertex、兩個 Vertex、一個 Vertex。這個階段會將 Vertex 連線成相對應的形狀。
    • 該階段將頂點著色器輸出的所有頂點作為輸入,並將所有的點裝配成指定圖元的形狀
    • 圖中則是一個三角形
  • 第③階段,幾何著色器 (Geometry Shader)

    • 該階段把圖元形式的一系列頂點的集合作為輸入,通過新增額外的Vertex或者轉換原來的頂點資料,通過產生新頂點構造出新的(或是其它的)圖元來生成其他形狀。
    • 簡而言之就是基於通過三角形、線段和點構建更復雜的幾何圖形。
    • 例子中,它生成了另一個三角形。
  • 第④階段,光柵化

    • 該階段會把圖元對映為最終螢幕上相應的畫素,生成片段;
    • 片段(Fragment) 是渲染一個畫素所需要的所有資料;
  • 第⑤階段,片段著色器 (Fragment Shader)

    • 也叫做 Pixel Shader,這個階段的目的是給每一個畫素 Pixel 賦予正確的顏色。顏色的來源就是之前得到的頂點、紋理、光照等資訊。
    • 該階段首先會對輸入的片段進行 裁切(Clipping) ,裁切會丟棄超出檢視以外的所有畫素,用來提升執行效率
    • 由於需要處理紋理、光照等複雜資訊,所以這通常是整個系統的效能瓶頸
  • 第⑥階段,測試與混合 (Tests and Blending)

    • 也叫做 Merging 階段,這個階段主要處理片段的前後位置以及透明度
    • 該階段會檢測片段的對應的深度值(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) 著色器語言

三、螢幕影象顯示原理

1. CRT 顯示器原理

介紹螢幕影象顯示的原理,需要先從 CRT 顯示器原理說起,如下圖所示

  • CRT 的電子槍從上到下逐行掃描,掃描完成後顯示器就呈現一幀畫面。
  • 然後電子槍回到初始位置進行下一次掃描。
  • 為了同步顯示器的顯示過程和系統的影片控制器,顯示器會用硬體時鐘產生一系列的定時訊號。當電子槍換行進行掃描時,顯示器會發出一個水平同步訊號(horizonal synchronization),簡稱 HSync
  • 而當一幀畫面繪製完成後,電子槍回覆到原位,準備畫下一幀前,顯示器會發出一個垂直同步訊號(vertical synchronization),簡稱 VSync
  • 顯示器通常以固定頻率進行重新整理,這個重新整理率就是 VSync 訊號產生的頻率。
  • 雖然現在的顯示器基本都是液晶顯示屏了,但其原理基本一致。

2. 螢幕影象顯示原理

CPU、GPU、顯示器工作方式 下圖所示為常見的 CPU、GPU、顯示器工作方式: CPU 計算好顯示內容提交至 GPU,GPU 渲染完成後將渲染結果存入幀緩衝區,影片控制器會按照 VSync 訊號逐幀讀取幀緩衝區的資料,經過資料轉換後最終由顯示器進行顯示

  • 最簡單的情況下,幀緩衝區只有一個。
  • 此時,幀緩衝區的讀取和重新整理都都會有比較大的效率問題。為了解決效率問題,GPU 通常會引入兩個緩衝區,即 雙緩衝機制。在這種情況下,GPU 會預先渲染一幀放入一個緩衝區中,用於影片控制器的讀取。當下一幀渲染完畢後,GPU 會直接把影片控制器的指標指向第二個緩衝器。
  • 雙緩衝雖然能解決效率問題,但會引入一個新的問題。當影片控制器還未讀取完成時,即螢幕內容剛顯示一半時,GPU 將新的一幀內容提交到幀緩衝區並把兩個緩衝區進行交換後,影片控制器就會把新的一幀資料的下半段顯示到螢幕上,造成畫面撕裂現象,如下圖:
  • 為了解決這個問題,GPU 通常有一個機制叫做垂直同步(簡寫也是 V-Sync),當開啟垂直同步後,GPU 會等待顯示器的 VSync 訊號發出後,才進行新的一幀渲染和緩衝區更新。這樣能解決畫面撕裂現象,也增加了畫面流暢度,但需要消費更多的計算資源,也會帶來部分延遲。

四、其它GPU知識

1. GPU 儲存系統

  • 早期的 GPU,不同的著色器對應有著不同的硬體單元。
  • 如今,GPU 流水線則使用一個統一的硬體來執行所有的著色器。
  • 此外,nVidia 還提出了 CUDA(Compute Unified Device Architecture) 程式設計模型,可以允許開發者通過編寫 C 程式碼來訪問 GPU 中所有的處理器核,從而深度挖掘 GPU 的平行計算能力。
  • 下圖所示為 GPU 內部的層級結構。最底層是計算機的系統記憶體,其次是 GPU 的內部儲存,然後依次是兩級 cache:L2 和 L1,每個 L1 cache 連線至一個 流處理器(SM,stream processor)
    • SM L1 Cache 的儲存容量大約為 16 至 64KB。
    • GPU L2 Cache 的儲存容量大約為幾百 KB。
    • GPU 的記憶體最大為 12GB。
  • GPU 上的各級儲存系統與對應層級的計算機儲存系統相比要小不少。
  • 此外,GPU 記憶體並不具有一致性,也就意味著並不支援併發讀取和併發寫入

2. GPU 流處理器

下圖所示為 GPU 中每個流處理器的內部結構示意圖。每個流處理器集成了一個 L1 Cache。頂部是處理器核共享的暫存器堆。

3. CPU-GPU 異構系統

至此,我們大致瞭解了 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 傳送命令、程式、資料。

4. GPU 資源管理模型

下圖所示為分離式異構系統中 GPU 的資源管理模型示意圖

  • MMIO(Memory-Mapped I/O)
    • CPU 通過 MMIO 訪問 GPU 的暫存器狀態
    • 通過 MMIO 傳送資料塊傳輸命令,支援 DMA 的硬體可以實現塊資料傳輸
  • GPU Context
    • 上下文表示 GPU 的計算狀態,在 GPU 中佔據部分虛擬地址空間。多個活躍態下的上下文可以在 GPU 中並存
  • CPU Channel
    • 來自 CPU 操作 GPU 的命令儲存在記憶體中,並提交至 GPU channel 硬體單元。
    • 每個 GPU 上下文可擁有多個 GPU Channel。每個 GPU 上下文都包含 GPU channel 描述符(GPU 記憶體中的記憶體物件)。
    • 每個 GPU Channel 描述符儲存了channel 的配置,如:其所在的頁表。
    • 每個 GPU Channel 都有一個專用的命令緩衝區,該緩衝區分配在 GPU 記憶體中,通過 MMIO 對 CPU 可見
  • GPU 頁表
    • GPU 上下文使用 GPU 頁表進行分配,該表將虛擬地址空間與其他地址空間隔離開來。
    • GPU 頁表與 CPU 頁表分離,其駐留在 GPU 記憶體中,實體地址位於 GPU 通道描述符中。
    • 通過 GPU channel 提交的所有命令和程式都在對應的 GPU 虛擬地址空間中執行。
    • GPU 頁表將 GPU 虛擬地址不僅轉換為 GPU 裝置實體地址,還轉換為主機實體地址。這使得 GPU 頁面表能夠將 GPU 儲存器和主儲存器統一到統一的 GPU 虛擬地址空間中,從而構成一個完成的虛擬地址空間
  • PFIFO Engine
    • PFIFO 是一個提交 GPU 命令的特殊引擎。
    • PFIFO 維護多個獨立的命令佇列,即 channel。
    • 命令佇列是帶有 put 和 get 指標的環形緩衝器。
    • PFIFO 引擎會攔截多有對通道控制區域的訪問以供執行。
    • GPU 驅動使用一個通道描述符來儲存關聯通道的設定
  • BO
    • 緩衝物件(Buffer Object)。一塊記憶體,可以用來儲存紋理,渲染物件,著色器程式碼等等。

5. CPU-GPU 工作流

下圖所示為 CPU-GPU 異構系統的工作流,當 CPU 遇到影象處理的需求時,會呼叫 GPU 進行處理,主要流程可以分為以下四步:

  • 將主存的處理資料複製到視訊記憶體中
  • CPU 指令驅動 GPU
  • GPU 中的每個運算單元並行處理
  • GPU 將視訊記憶體結果傳回主存

[TOC]