[GDC16] Optimizing the Graphics Pipeline with Compute
Optimizing the Graphics Pipeline with Compute
DX12雖然帶來了CPU的low overhead,但是依然GPU會卡在tiny draw上。主要是GBuffer的後面半段dp會因為Hi-Z cull掉很多pixel(從近到遠draw),而且遠處的物體會有大量細節物體。
可以看到後半段,大部分只有vs,沒有ps invoked
樂觀點假設rasterizer每個cycle能處理2個triangles,實際上xb1是0.9個。因為從VGT->PA使用FIFO隊列,所以在4096個cycle中處理完vs填滿FIFO隊列,才不會使得rasterizer飢餓(怎麼算出來的)。在shadow pass的表現最差,因為有部分特別大的triangles,coarse rasterizer對於1個tile只用一個cycle就完成了,所以對於大於32*32像素的三角形需要多個cycle
大部分引擎在CPU做coarse culling,再在GPU上refine,因為CPU和GPU之間的延遲,很多優化無法達成。為此會做一些GPU上的culling,包括Depth-aware culling,late-latch culling以及本篇講的cluster/triangle culling
CS處理mesh帶來了一系列好處,最主要的思想就是把drawcall看做data,這些GPU生成的數據可以pre-built,cached以及reused
Overview 之前Ubi的GPU-Driven差不多,略過
相互交叉vb的各個attribute會帶來更好的性能。對於compute cull,不用關心其他數據(uv,tbn等),這樣stride就是一個固定值沒有任何狀態切換。對於vertex數據的cache也更加友好,之前cacheline需要保存很大一塊數據,現在用完相應的數據可以直接invalid cacheline。另一方面,對於CPU的數據也由SoA變為了AoS,更加適合SSE。而且能區分容易改變的和固定的數據,比如pos+uv(畫shadow),skin data等。分開的好處還有一個就是可以自由重建indexbuffer,對於depth only pass,只需要pos數據,可以re-use更多的頂點(正常繪製的時候,pos一樣但是uv和normal可能不一樣 )
Cluster Culling。事先使用貪心演算法把mesh分成256個triangle一組的clusters,對於每個cluster預烘焙一個bounding cone,做法是對這256個法線投到球上,做一個最小閉包的椎體。4個8bit channel的SNorm的精度足夠了,只需要判斷視線和cone normal的夾角是否小於cone的張角。為了避免false reject張角可以大一些,還可以對normal進行GBuffer常用的encode
Occlusion用的是bounding sphere vs bounding box(HiZ),注意在透視投影中sphere會變成ellipsoid
在133us開始,因為index為空所以遇到了empty draw,151us連續10us空閑,實際因為流水打斷需要重新填滿,所以性能損失超過10us。從中可以看出compact index的重要性
DX12新潮做法
通用做法,在cs中做parallel reduction,在GCN架構中還有其他奇技淫巧
Parallel prefix sum
Ballot 64bit代表每個thread的狀態
利用這個
MBCNT計算之前幾個thread的bit累加
兩個結合起來,得到了prefix sum
看代碼好懂
每個thread處理一個triangle,通過的culling的會使用剛才的技巧來獲得當前triangle的compact index。對於wavefront之間如果想做半透排序等,需要使用ds_ordered_count來保證wavefront之間的輸出順序。
對於if branch編譯器會做優化來進行divergence execute,實際上沒什麼必要,只有在需要rw的時候才進行branch,比如Hi-Z cull
理論上50%的triangle會被back-face culling,所以需要用效率最高的演算法
Tessellation patch back face cull略,沒接觸過
越小的triangle對於raster來說效率越低,因為卡在primitive setup上,raster一直飢餓
這個挺有用的,對於raster的效率可以寫段可視化代碼測試,對於每一個triangle會對應一個wavefront來處理其raster之後的pixel,那麼輸出每個wavefront中真正有效pixel的thread佔比,就可以看出是否有太多的瑣碎triangle,另外也可以測試LOD的設置是否合理
Small primitive cull的幾種情況
Frustum culling,只做4個plane保守cull
一種方法是如果當前triangle或者cluster的bb完全在tile內,那麼判斷是否reject,否則保守保留
在做16*16的depth reduction,各種優化41us,對於其他的比如light tile也使用同樣的代碼
另外一種方法,也是frostbite用的就是hierarchical Z pyramid,根據triangle的bb去選擇mipLevel,直接對比depth
AMD HTILE主要就是可以8*8的depth只用32bit,一個test可以reject64個pixel。因為是console spec的,後面略
可以使用CPU的軟光柵化ZBuffer
使用固定大小的buffer,每次生成大概3MB的indexbuffer,錯開dispatch&draw,更好利用非同步來並發流水
起步並不是太好
可以做一些其他計算
44w面->9w面
GPU 性能提升15%-30%
Tessellation提升更多40-80%
推薦閱讀:
※[Siggraph15] GPU-Driven Rendering Pipelines
※從零開始手敲次世代遊戲引擎(四十二)
※Matrix and Transform Conversion 1/3
TAG:遊戲引擎 |