本文轉自:字符無限科技
玩游戲時遇到畫面掉幀、操作延遲,大概率和一個叫Draw Call的指標有關。它是游戲渲染的核心環節,也是性能優化繞不開的坎,哪怕是Unity、UE 引擎的資深開發者,也得在它身上下功夫。
什么是Draw Call?
Draw Call僅僅是一條指令!Draw Call指令從CPU傳到GPU,渲染一個網格。指令只指向一個被渲染的網格并且不包含任何材質信息。
在發出指令后,GPU的渲染狀態值(材質、紋理、shader等)和所有的頂點數據通過神奇的代碼轉化為以一個信息,然后再在你的屏幕上呈現出美麗的畫面。

渲染就是在做一個巨大數量的小任務,比如計算成千上萬的頂點和在屏幕上繪制以百萬計的像素。
Draw Call 本身的含義很簡單,就是CPU調用圖像編程接口,如OpenGL 中的glDrawElements 命令或者DirectX 中的DrawlndexedPrimitive命令,以命令GPU 進行渲染的操作。
其核心流程包含三個階段:
數據準備:CPU將網格數據、紋理、材質屬性等資源從內存(RAM)傳輸至GPU顯存(VRAM);
狀態配置:設置渲染管線狀態(如著色器、混合模式、深度測試)和全局參數(如光照、投影矩陣);
指令提交:調用glDrawElements或DrawIndexedPrimitive等API觸發GPU渲染。
★關鍵特性:
命令緩沖區機制:CPU與GPU通過Command Buffer實現異步通信,CPU寫入指令,GPU按隊列順序執行;
渲染狀態切換成本:每次材質、紋理或著色器變更需重新配置全局狀態,產生額外開銷。
你在游戲里看到的每棵樹、每個角色、每道特效,背后都需要 CPU 發一次(或多次)命令,告訴 GPU “該畫這個東西了”。比如屏幕上有 100 棵樹,默認情況下可能就有 100 個 Draw Call,GPU 收到命令后才會執行渲染操作。
這里要明確一個關鍵點:Draw Call就是一個命令,它的發起方是CPU,接收方是GPU。這個命令僅僅會指向一個需要被渲染的圖元(primitives)列表,而不會再包含任何材質信息,這是因為我們已經在上一個階段中完成了!

一個常見的誤區是, Draw Call 中造成性能問題的元兇是GPU,認為GPU 上的狀態切換是耗時的,其實不是的,真正“拖后腿”其實的是CPU。
為什么Draw Call 多了會影響幀率?
我們先來做一個實驗:先創建10000個小文件,每個文件的大小為1KB,然后把它們從一個文件夾復制到另一個文件夾。你會發現,盡管這些文件的空間總和不超過10MB ,但要花費很長時間。
現在,我們再來創建一個單獨的文件,它的大小是10MB,然后也把它從一個文件夾復制到另一個文件夾。而這次復制的時間卻少很多!
這是為什么呢?明明它們所包含的內容大小是一樣的。原因在于,每一個復制動作需要很多額外的操作,例如分配內存、創建各種元數據等。
如你所見,這些操作將造成很多額外的性能開銷,如果我們復制了很多小文件,那么這個開銷將會很大。
渲染的過程雖然和上面的實驗有很大不同,但從感性角度上是很類似的。在每次調用Draw Call 之前, CPU 需要向GPU 發送很多內容,包括數據、狀態和命令等。

在這一階段, CPU 需要完成很多工作,例如檢查渲染狀態等。而一旦CPU 完成了這些準備工作, GPU 就可以開始本次的渲染。
GPU 的渲染能力是很強的,渲染200 個還是2000 個三角網格通常沒有什么區別,因此渲染速度往往快于CPU 提交命令的速度。
如果Draw Call 的數量太多, CPU 就會把大量時間花費在提交Draw Call 上,造成CPU 的過載。

如何減少Draw Call?
盡管減少Draw Call 的方法有很多,但我們這里僅討論使用批處理(Batching )的方法。
我們講過,提交大量很小的Draw Call 會造成CPU 的性能瓶頸,即CPU 把時間都花費在準備Draw Call 的工作上了。
那么,一個很顯然的優化想法就是把很多小的DrawCall 合并成一個大的Draw Call ,這就是批處理的思想。
需要注意的是,由于我們需要在CPU 的內存中合并網格,而合并的過程是需要消耗時間的。因此,批處理技術更加適合于那些靜態的物體,例如不會移動的大地、石頭等,對于這些靜態物體我們只需要合并一次即可。
當然,我們也可以對動態物體進行批處理。但是,由于這些物體是不斷運動的,因此每一幀都需要重新進行合并然后再發送給GPU,這對空間和時間都會造成一定的影響。

在游戲開發過程中,為了減少Draw Call 的開銷,需要注意:
避免使用大量很小的網格。當不可避免地需要使用很小的網格結構時,考慮是否可以合并它們。
避免使用過多的材質。盡量在不同的網格之間共用同一個材質。
合并的網格會在一次渲染任務中進行繪制,他們的渲染數據,渲染狀態和shader都是一樣的,因此合并的條件至少是:同材質、同貼圖、同shader。最好網格頂點格式也一致。
合并本身有消耗,因此盡量在編輯器下進行合并。
確實需要在運行時合并的,將靜態的物體和動態的物體分開合并:靜態的合并一次就可以,動態的只要有物體發生變換就要重新合并。
Draw Call 作為游戲性能的關鍵指標,優化的核心從來不是讓GPU 少畫,而是讓 CPU 少發命令。掌握批處理技巧,再注意開發中的細節,就能有效減少卡頓,讓游戲畫面更流暢。
-
cpu
+關注
關注
68文章
11229瀏覽量
223226 -
gpu
+關注
關注
28文章
5118瀏覽量
134556
發布評論請先 登錄

游戲卡頓元兇竟然是 Draw Call!
評論