轉載請註明出處為KlayGE遊戲引擎,本文地址為http://www.klayge.org/2011/07/26/%e4%b8%8d%e4%ba%89%e6%b0%94%e7%9a%84geometry-shader/
上周末實現了打算在KlayGE 4.0中使用的render to texture array功能。於是自然而然想到在ShadowCubeMap這個例子中使用,用來在1個pass內直接產生cubemap。結果,不比不知道,一比嚇一跳。在不同GPU上FPS如下:
|
NV 9800GT |
NV 480GTX |
AMD 5870 |
| 6 pass Cubemap |
158.63 |
312.82 |
241.10 |
| Dual Paraboloid |
5.77 |
375.32 |
211.91 |
| 1 pass Cubemap |
66.08 |
288.77 |
228.44 |
| 1 pass Cubemap with instance |
105.37 |
281.34 |
224.10 |
| 1 pass Cubemap with instance GS |
NA |
287.80 |
211.01 |
9800GT所在機器的CPU比後兩套系統差得多,沒法橫向比較,只能縱向比較。後兩套系統只有GPU不同,可以橫向和縱向比較。
5種做法
- 6 pass Cubemap是個基準,實現方法就是最通俗的,在光源的位置向6個方向各渲一遍情境,得到cubemap的shadow map。
- Dual Paraboloid需要渲染2個pass,正反面各一次,把經過tessellation的情境參數化到拋物面座標繫上。這種方法看似不錯,但實際上省 不了多少draw(因為在高層就做了視錐剪裁),而且因為需要tessellation,開銷會增加很多。在9800GT上,因為沒有硬體的 tessellator,這裡用的是簡單的instanced tessellation,每個三角形不管大小都固定切分5次,所以效能超級低下。
- 1 pass Cubemap的做法是,把每個頂點在VS裡面分別乘上6個model view projection,得到了6個position全都傳給GS。在GS裡把每個三角形根據不同的position產生6份,然後通過 SV_RenderTargetArrayIndex傳到不同的rt上,一個pass完成render to texture array。原先也試過在VS裡面只是簡單地把輸入的position傳給GS,而在GS裡面完成乘矩陣的事情,結果更慢。
- 1 pass Cubemap with instance的做法是用instance來產生6個頂點。這樣在VS裡面只需要根據SV_InstanceID選擇乘上哪一個model view projection,在GS裡面也不用產生6份。
- 1 pass Cubemap with instance GS用到了D3D11新增的instance GS功能,讓GS自己instance多個,而不用IA來進行instance的操作。
資料分析
- Dual Paraboloid需要做很細的tessellation,而tessellation正是NV Fermi的強項,所以DP在NV 480GTX上比AMD 5870快得多,甚至快於不必做tessellation的方法。
- 1 pass Cubemap with instance比1 pass Cubemap快一些,說明用IA來拆頂點的效率比GS高得多(因為GS更通用)。
- 1 pass Cubemap with instance GS和 pass Cubemap with instance速度差不多。instance GS的出現就是為了加速這種狀況,但它似乎沒有做到。
- 1 pass Cubemap比6 pass Cubemap慢了不少。而且如果沒有在GS裡面做frustum culling,會慢相當多!
對於3和4,有兩種可能。第一是cubemap只有6個面,太少了,不夠GPU發揮;這種可能性存在,但機率很低。更有可能是,GS太垃圾了,沒法很有效地執行預期的操作。
結論
這樣一測試,基本上可以認為,在render to cubemap的情況下,只要經過了GS,哪怕很簡單的GS,都會對效能大打折扣。所以除非是類似於stochastic rendering、motion blur,其他時候用GS往往得不償失。好在,D3D11的GPU上GS已經有所最佳化。比起D3D10的GPU來說,新一代GPU的GS效能損失少了很 多。但本來寄希望於instance GS,結果沒有太多的好處,不知道以後的GPU和驅動會不會有所改進。
另外,如果實在要做render to texture array,別忘了在GS裡面自己做frustum culling以及back face culling。這樣能極大地減少輸出三角形的個數,從而提升GS的效能。