標籤:

[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:遊戲引擎 |