Render Hell —— 史上最通俗易懂的GPU入門教程(三)

聲明:文本非原創,只是翻譯,原文鏈接如下:
https://simonschreibt.de/gat/renderhell-book3/

Render Hell – Book III

在這裏插入圖片描述
在這裏插入圖片描述本文是 “Render Hell” 系列文章的第三篇。

歡迎來到第三篇!這裏我們將檢查一些在渲染過程中可能出現的問題。但首先,我們來點小練習:

知道一個問題是有好處的,而真正去感受一個問題則更有助於理解。所以讓我們試着感覺自己像個 CPU / GPU 吧。

實驗

請創建10000個小文件(例如每個文件1 KB),並將它們從一個硬盤複製到另一個硬盤。你會看到,即使數據總量只有 9.7MB 大小,也需要花很長的時間才能拷貝完成。

在這裏插入圖片描述
現在創建一個 9.7MB 大小的文件並以同樣的方式複製它,你會發現進度條跑得飛快!
在這裏插入圖片描述爲什麼?它們的數據總量都是一樣的啊!

沒錯,但是對於每個複製操作都有一些額外的事情要做,例如:文件傳輸的準備工作、分配內存、讀寫磁頭在硬盤中來回移動,等等,這些都是是每筆寫操作的額外開銷。正如你所感受到的痛苦,如果你複製很多小文件,那麼這將是一筆龐大的開銷。渲染許多網格(也就是執行許多命令)要比這複雜得多,但其實都類似。

在這裏插入圖片描述
現在讓我們來看看在渲染過程中可能會遇到的最壞的情況。

最糟糕的情況

渲染過程中有太多的小網格這本身就不是什麼好事,如果他們還使用不同的材質參數,那情況會變得更加糟糕。但是:爲什麼呢?

1. 大量的 Draw Call

通常 GPU 渲染這些小網格要比 CPU 發送命令快得多。我們的應用程序代碼並不是把命令直接發送給 GPU 的,而是要經過圖形 API、操作系統層和驅動程序才能給到 GPU 的,這些加起來就是 CPU 的開銷。

“減少 Draw Call 的主要原因,是因爲圖形硬件變換和渲染三角形,要比你提交繪圖命令快得多。如果每次調用只提交幾個三角形,那麼你將被徹底束縛在 CPU 上,而 GPU 則大部分時間都處於空閒狀態,CPU 將無法快速地爲 GPU 供給資源。“[f05]

在這裏插入圖片描述
此外,每次 Draw Call 都會產生多種開銷(如上所述):

“每次 API 調用都會有驅動程序的開銷,減少此開銷的最佳方法就是儘可能少地調用API。”[a02]

如今控制檯 API 或新的 API(DirectX12、Vulkan、Metal)所佔的開銷已經越來越小了。與提交小任務相比,提交大任務仍然是最好的選擇,但是已經沒有像過去那麼糟糕了。

更多詳細內容,請查閱:NVIDIA OpenGL Extension API 的性能優勢

2. 大量的命令

這類開銷的一個典型例子就是 CPU 與 GPU 之間的通信。你還記得 CPU 填充命令緩衝區,GPU 從中讀取數據嗎?是的,它們必須就緩衝區內容的變化而進行通信,這也會帶來一定的開銷(它們通過更新讀/寫指針來實現這一點 —— 點擊此處閱讀更多相關信息)!
因此,驅動程序將多條命令批處理到一個被稱之爲 推送緩衝區(push buffer) 的 buffer 中,它並不會挨個挨個地提交命令,而是先把這個緩衝區填滿,然後再將一整塊命令交給 GPU。

在這裏插入圖片描述
當 CPU 正在填充新的命令緩衝區時,GPU 最好能做些什麼事情(例如處理最後一塊命令)。爲了避免 GPU 必須等到下一個新的命令塊準備就緒纔開始工作,驅動程序會在命令塊的大小上做文章,有時它們會塞進去超過一整幀的命令,直到真正開始提交任務。
您可以在顯卡驅動程序的控制面板中找到填充該 buffer 的相關設置(“最大預渲染幀數”)。提交大量幀的缺點是,我們基本上是在“過去”的時間裏渲染“未來”的幀,我們的 CPU 當前請求的幀已經有了最新的播放器數據源,但是我們的 GPU 渲染的卻是一些過去的幀。這種增加的延遲可能對某些內容(虛擬現實VR …)不利。

現代圖形 API 或控制檯 API 還允許你同時填充多個命令緩衝區,驅動程序則將它們一個接一個地提交給 GPU 硬件(串行提交隊列)。
DirectX 12 與 DirectX 11 的命令緩衝區的主要區別在於,採用並行構建命令緩衝區的這種方式,可以讓它們更快的被驅動程序所提交。在 DirectX 11 中,驅動程序仍然需要對串行提交中的內容進行更多的跟蹤,這降低了並行構建的性能。

到目前爲止,我們只討論了那些具有相同材質參數(渲染狀態)的網格。如果我們想使用不同的材質來渲染網格,會發生什麼樣的情況呢?

3. 大量的網格和材質

刷新 Pipeline。

“當我們切換 Render State 時,有時需要執行全部或部分的 Pipeline 刷新(flush)動作。因此,修改着色程序或材質參數可能需要付出昂貴的代價[…]“[b01 第711/712頁]

你以爲沒這麼糟糕?那好,如果你使用不同的材質來渲染不同的網格,你就有可能會在 CPU 和 GPU 上引入額外的啓動時間。你給第一個網格設置 Render State,然後命令 GPU 去渲染它,接着給下一個網格設置新的 Render State,再命令 GPU 渲染下一個網格,以此類推。

在這裏插入圖片描述
在這裏插入圖片描述我將上圖中 “Change State” 命令高亮爲紅色,是因爲 a) 這些操作的代價是巨大的 b) 便於閱讀

設置 Render State 有時會導致 Pipeline 的部分“刷新(flush)”動作(並不是所有的 Change State 都這樣,這取決於要修改的參數和 GPU 上可用的硬件單元)。也就是說,在(使用新的 Render State)渲染新的網格之前,必須先完成當前某些硬件單元(使用當前的 Render State)正在處理的網格,就像上面圖片演示的那樣。與一次渲染大量頂點相比(例如,將相同渲染狀態的多個網格組合在一起進行渲染 —— 我稍後會提到的一種優化方法),多次修改 Render State 卻只渲染少量的頂點將是一件多麼糟糕的事情,而原因相信大家都已經很清楚了。

如前所述,大多數狀態切換的耗時都來自於 CPU 早期的圖形 API。由於發起 Draw Call 的時間很短(與網格的複雜度無關,只取決於 API 和操作系統的執行時間),你可以認爲渲染2個和200個三角形其實沒有多大區別。GPU 的速度非常快,因此它渲染這些網格的時間,實際上要比 CPU 準備這些網格的時間還要快。

當我們討論將幾個小網格合併爲一個大網格時,這個“規則”當然會發生變化(我們稍後就討論這個問題)。

在這裏插入圖片描述
在這裏插入圖片描述我無法獲取到有關當前顯卡上“免費”渲染多少個多邊形的最新數據。如果你知道這方面數據,或者最近做過一些 benchmark 測試,請告訴我!

4. 網格和多材質

如果一個網格使用不止一種材質進行渲染,又會出現什麼情況呢?基本上,你的網格會被大卸八塊,然後一塊一塊地被送進命令緩衝區中。

在這裏插入圖片描述
當然,這種情況下會爲每個網格塊單獨創建一個 Draw Call。

在這裏插入圖片描述

5. 單個圖形命令處理器

如今,典型的 GPU 只有一個命令處理器/圖形前端。這意味着,即使 CPU 以並行的方式提交命令塊,所有與圖形相關的命令在分發給 GPU 並行處理單元之前,都將以串行的方式被處理。更多關於 GPU 工作細節,請參閱 Book II 深入詳解

6. 瘦長的三角形(Thin Triangle)

光柵化過程也可能存在與性能相關的細節問題(這取決於硬件),我在 Book II “3.16 光柵化” 中已經對此梳理過了:

目前大多數圖形硬件都是以2×2像素四邊形爲單位進行三角形渲染的(在 NVIDA 硬件上,是以8個這樣的四邊形爲一組的,即一組32個線程)。

在這裏插入圖片描述
如果其中某些片元(fragment)沒有覆蓋到三角形,那麼它們的輸出將會被忽略掉。

在這裏插入圖片描述
你可以想象一下,爲什麼那些又瘦又長的三角形會對硬件很不利?因爲這4個線程中只有一個線程可以計算像素。甚至到目前爲止,對於非常燒錢的 全屏後處理特效技術,我們都不是將它們當作兩個三角形來渲染,而是當作一個被放大的巨型三角形來渲染,因此只有沒有任何斜線穿過屏幕的區域纔可見。

7. 多餘的過度渲染(Useless Overdraw)

當我們渲染一個多邊形時,如果它使用軟件 Alpha 並且它的紋理大部分區域爲100%透明,則很可能會浪費掉大量的性能。這種場景可能會發生在使用樹枝或樹葉作爲紋理進行渲染的場景,或者使用一個全屏大小的四邊形來做 暗角效果 (只會使圖像的角落變暗)渲染的場景。

Book IV “解決方法” 中,將給出一種解決該問題的方法。

8. 移動平臺 vs PC平臺

許多移動設備能很好地處理顏色混合和抗鋸齒,而更多的挑戰則在於大量的幾何模型。相比之下,桌面/控制檯 GPU 則剛好相反。原因就在於移動平臺的 GPU 使用 片上存儲器(on-die/on-chip memory,那些小緩存)作爲中間 framebuffer(Xbox360 也有這個功能)。因此,它們可以快速地進行顏色混合,並在相對較低的性能損耗下進行抗鋸齒操作。
然而在 FHD 分辨率下,要想把渲染所需的全部內存都裝進芯片裏,所需費用是極其昂貴的。因此移動端的 GPU 在渲染時並不是一次性地渲染一整幀,而是以一個 tile(小塊) 爲單位進行渲染的。一個場景每次只渲染一個 tile,當該 tile 被渲染完成時,就會將其從 tile cache 複製到最終的 FrameBuffer 中。這要比桌面端的 GPU 直接複製一整幀圖像到 FrameBuffer 上更加省電。
而這種設計的缺點是 GPU 不得不多次處理同一幾何圖形,因爲該幾何圖形有可能會與多個 tile 重疊,這也意味着大量的頂點會造成 GPU 性能的下降。
不過,該設計對於 UI 和文本渲染(帶紋理的四邊形)以及需要大量顏色混合的場景來說是非常有效的,這也正是移動設備的主要用途。

我希望在瞭解了關於過多的網格和材質會帶來哪些壞處的問題之後,能夠給你帶來一些啓發。接下來讓我們來看看關於這些問題都有哪些解決方法,因爲即使所有這些問題聽起來都很糟糕,卻仍然有許多漂亮的遊戲橫空出世,換句話說他們早就以某種方式解決了上述問題。


本篇到此結束。

在這裏插入圖片描述繼續閱讀下一篇:解決方法,或返回目錄索引

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章