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

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

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

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

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

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