Draw Call本身的含義很簡(jiǎn)單,就是CPU調(diào)用圖像編程接口,如OpenGL中的glDrawElements命令或者DirectX中的DrawIndexedPrimitive命令,以命令GPU進(jìn)行渲染的操作。
一個(gè)常見的誤區(qū)是坛掠,Draw Call中造成性能問題的元兇是GPU咪奖,認(rèn)為GPU上的狀態(tài)切換是耗時(shí)的盗忱,其實(shí)不是的,真正“拖后腿”其實(shí)的是CPU羊赵。
在深入理解Draw Call之前趟佃,我們先來看一下CPU和GPU之間的流水線化是怎么實(shí)現(xiàn)的,即它們是如何相互獨(dú)立一起工作的昧捷。
1.CPU和GPU是如何實(shí)現(xiàn)并行工作的闲昭?
如果沒有流水線化,那么CPU需要等到GPU完成上一個(gè)渲染任務(wù)才能再次發(fā)送渲染命令靡挥。但這種方法顯然會(huì)造成效率低下序矩。我們需要讓CPU和GPU可以并行工作。而解決方法就是使用一個(gè)命令緩沖區(qū)(Command Buffer)跋破。
命令緩沖區(qū)包含了一個(gè)命令隊(duì)列贮泞,由CPU向其中添加命令,而由GPU從中讀取命令幔烛,添加和讀取的過程是互相獨(dú)立的。命令緩沖區(qū)使得CPU和GPU可以相互獨(dú)立工作囊蓝。當(dāng)CPU需要渲染一些對(duì)象時(shí)饿悬,它可以向命令緩沖區(qū)中添加命令,而當(dāng)GPU完成了上一次的渲染任務(wù)后聚霜,它就可以從命令隊(duì)列中再取出一個(gè)命令并執(zhí)行它狡恬。
命令緩沖區(qū)中的命令有很多種類,而Draw Call是其中一種蝎宇,其他命令還有改變渲染狀態(tài)等(例如改變使用的著色器弟劲,使用不同的紋理等)
2.為什么Draw Call多了會(huì)影響幀率?
我們先來做一個(gè)實(shí)驗(yàn):請(qǐng)創(chuàng)建10 000個(gè)小文件姥芥,每個(gè)文件的大小為1KB兔乞,然后把它們從一個(gè)文件夾復(fù)制到另一個(gè)文件夾。你會(huì)發(fā)現(xiàn)凉唐,盡管這些文件的空間總和不超過10MB庸追,但要花費(fèi)很長(zhǎng)時(shí)間。現(xiàn)在台囱,我們?cè)賮韯?chuàng)建一個(gè)單獨(dú)的文件淡溯,它的大小是10MB,然后也把它從一個(gè)文件夾復(fù)制到另一個(gè)文件夾簿训。而這次復(fù)制的時(shí)間卻少很多咱娶!這是為什么呢米间?明明它們所包含的內(nèi)容大小是一樣的。原因在于膘侮,每一個(gè)復(fù)制動(dòng)作需要很多額外的操作屈糊,例如分配內(nèi)存、創(chuàng)建各種元數(shù)據(jù)等喻喳。如你所見另玖,這些操作將造成很多額外的性能開銷,如果我們復(fù)制了很多小文件表伦,那么這個(gè)開銷將會(huì)很大谦去。
渲染的過程雖然和上面的實(shí)驗(yàn)有很大不同,但從感性角度上是很類似的蹦哼。在每次調(diào)用Draw Call之前鳄哭,CPU需要向GPU發(fā)送很多內(nèi)容,包括數(shù)據(jù)纲熏、狀態(tài)和命令等妆丘。在這一階段,CPU需要完成很多工作局劲,例如檢查渲染狀態(tài)等勺拣。而一旦CPU完成了這些準(zhǔn)備工作,GPU就可以開始本次的渲染鱼填。GPU的渲染能力是很強(qiáng)的药有,渲染200個(gè)還是2 000個(gè)三角網(wǎng)格通常沒有什么區(qū)別,因此渲染速度往往快于CPU提交命令的速度苹丸。如果Draw Call的數(shù)量太多愤惰,CPU就會(huì)把大量時(shí)間花費(fèi)在提交Draw Call上,造成CPU的過載赘理。
3.如何減少Draw Call宦言?
盡管減少Draw Call的方法有很多,但這里僅討論使用批處理(Batching)的方法商模。
提交大量很小的Draw Call會(huì)造成CPU的性能瓶頸奠旺,即CPU把時(shí)間都花費(fèi)在準(zhǔn)備Draw Call的工作上了。那么施流,一個(gè)很顯然的優(yōu)化想法就是把很多小的DrawCall合并成一個(gè)大的Draw Call凉倚,這就是批處理的思想。
需要注意的是嫂沉,由于我們需要在CPU的內(nèi)存中合并網(wǎng)格稽寒,而合并的過程是需要消耗時(shí)間的。因此趟章,批處理技術(shù)更加適合于那些靜態(tài)的物體杏糙,例如不會(huì)移動(dòng)的大地慎王、石頭等,對(duì)于這些靜態(tài)物體我們只需要合并一次即可宏侍。當(dāng)然赖淤,我們也可以對(duì)動(dòng)態(tài)物體進(jìn)行批處理。但是谅河,由于這些物體是不斷運(yùn)動(dòng)的咱旱,因此每一幀都需要重新進(jìn)行合并然后再發(fā)送給GPU,這對(duì)空間和時(shí)間都會(huì)造成一定的影響绷耍。
在游戲開發(fā)過程中吐限,為了減少Draw Call的開銷,有兩點(diǎn)需要注意褂始。
(1)避免使用大量很小的網(wǎng)格诸典。當(dāng)不可避免地需要使用很小的網(wǎng)格結(jié)構(gòu)時(shí),考慮是否可以合并它們崎苗。
(2)避免使用過多的材質(zhì)狐粱。盡量在不同的網(wǎng)格之間共用同一個(gè)材質(zhì)。