花了一些時間把EpicGameChina關於Mobile Deferred Render的演講看完並做了一些筆記,分享給大家,也推薦大家去看影片,我是覺得收獲蠻多的。
影片在此:UE5 Deferred Technology Pipeline for Mobile Games
1. Mobile Deferred Render直到5.1才算比較成熟
2. Deferred的優勢在於可以減少shader permutation,因為在base pass不用算lighting,因此相關的permutation就可以從model material直接decouple出來,省下大量的shader數量。
3. mobile deferred vs mobile forward在關掉static lighting的比較:
a. Mobile Deferred只需要建出LMP_NO_LIGHTMAP一種caseb. Mobile Forward則需要建出LMP_NO_LIGHTMAP跟LMP_MOBILE_DIRECTIONAL_LIGHT_CSM搭上bEnableLocalLights開關,總共有四種case。
c. Mobile Deferred的shader總量是Mobile Forward的1/4。
d. LMP指的是LightingMapPolicy,所有的Type參數ELightMapPolicyType這個enum
4. mobile deferred vs mobile forward在打開static lighting後大約是原本的1/5大小
5. 減少Shader大小代表我們打包出來的pak變小,而且也能降低記憶體的使用量,另外也可以減少因為run-time compiling shader所造成的卡頓。
a. 註:run-time compiling shader的卡頓可以使用PSO Caching的機制解決,只不過這個方法會要求團隊在打包之前先跑一次版本以記錄用到的材質。
6. Deferred Render因為base pass沒有複雜的光照計算,因此這階段的開銷比forward減少很多,而且光照只在可見的pixel上做計算,不會浪費GPU在不可見的pixel上,理論上對於減少GPU壓力是有幫助的。
a. 不過現在的晶片大多內建了減少OverDraw的技術,例如PowerVR GPU的Hidden Surface Removal,以及Arm GPU的Forward pixel kill,即便如此,在某些device上還是能有明顯的收益的。
7. Deferred Render可以把原本Foward要保留給光照用的Sampler讓出來給美術使用,最多能多出6~7個Sampler。大致上的數量如下
a. Reflection capture可以減少1到2個
b. Shadow texture減少1個
c. Planar reflection 1個
d. Light grid data減少3個。
8. 雖然Deferred Render需要創建多張G-Buffer,但我們可以利用tile-based GPU的特性,把GBuffer分配在Tile Memory上,這樣就可以不需要用到System memory,從而減少傳輸成本。只不過這招會讓我們無法在PostProcess存取到GBuffer的資料,這也就是為什麼目前mobile不支援screen space reflection的原因之一。
9. 以下功能是Deferred Render才有的功能:
a. Lit deferred decal:forward的decal是無光照的,而deferred render因為有了GBuffer,可以靠上面的資料來計算光照效果。
b. IES Profile
c. Light Function
d. Better EnvBRDF:Deferred的效果上會更閱近PC,不需要real-time計算,只需要做一次貼圖採樣就行了,因此性能更好;而foward因為要省下一個sampler,因此採用的是近似的版本在real-time做計算。
10. Deferred Render在Android手機上有遇到一些兼容性問題,因此在設計系統時會考慮以下幾個限制。
a. 對於OpenGLES的支援:由於Mali不支援MRT(Multiple Render Targets)的FrameBufferFetch,因此使用了Pixel Local Storage的技術,但用了這個技術的限制是,它只有128 bit。
b. 對於Vulkan的支援:調查了市面上的Android手機,發現有16%左右的機器只支援4個Input Attachment。
11. 由於GBuffer和SceneDepth都會算在InputAttachment上,為了支援16%只有4個Input Attachment的機器,引擎只能把GBuffer的數量控制在3個,再加上一個SceneDepth就到了4個上限。不像PC可以創出5張或更多的GBuffer來支援一些效果。
12. 每個平台都會有以下幾個Buffer:SceneColor、SceneDepth以及GBufferA、GBufferB、GBufferC。其中SceneDepth如果PP用不到就不需存下來以節省頻寬。
13. 在Android Mali OpenGLES上我們是把GBuffer利用Pixel Local Storage存在Tiled Memory上,再加上32bit的SceneColor,剛好佔滿Pixel Local Storage的128 bit。
14. Vulkan或Qualcommon的GLES手機,我們是用Memoryless的方式把GBuffer建立在TileMemory上。
a. 注:Memoryless代表該份資料只會存在於TileMemory而不會回存到System Memory,這也就代表PostProcess存取不到GBuffer上的資料。
15. 在IOS或其他PowerVR的Android GLES手機:因為GPU不支持DepthBufferFech,只能用MRT的FrameBufferFetch,所以只能新申請一張32bit的DepthAux來存Depth。DepthAux跟GBuffer一樣是memoryless,所以不佔回寫到System Memory的頻寬,只是需要處理一下shader fetch depth的邏輯。
16. 在UE4中不支援shading model只能支援defaultLit,要支援shading model的話,就要必須要有至少4個通道來存儲custom data,最好每個通道都有8 bit,不然精度不太足。正常情況下我們可以多申請GBuffer來達成這件事,但是因為硬體的限制只能有4個Input Attachment,所以我們只能想辦法挖東牆補西牆。
17. 前提是,在Mobile Deferred Render上我們我們只有GBufferA、GBufferB、GBufferC,每張Buffer都有32 bit,裡面各別存了下面資料:
a. GBufferA:
- Encoded Normal.xy:由於normal的z可以靠cross product算出來,因此只需要xy二個通道就行了,但由於normal在某些情況下需要比較高的精度,例如在一些反射比較好的效果下,如果只用8 bit可能會看到一些瑕疵,因此xy各別用10 bit保存,共20 bit。
- ShadingModelID:佔用4 bit。
- PerObject Data:在Mobile中沒有使用
b. GBufferB:Metallic、Specular、Roughness以及Indirect Irradiance,各別佔用8 bit。
c. GBufferC:BaseColor.RGB佔用24 bit、Precomputed Shade 8 bit。
18. 以上資料有一些東西可以省下來讓給CustomData用
a. GBufferA:PerObject Data有8 bit在Mobile中沒有使用,可以拿出來。
b. GBufferB中的Metallic在很多ShadingMode中沒有用到,可以拿出來。
c. GBufferB跟GBufferC中的Indirect Irradiance只在靜態光照下用到,限制只能開動態光的話就能拿出來給CustomData用。
d. 以上共4個通道可以在只有動態光條件下空出來給CustomData
19. 影片上有詳細講解Buffer Layout裡面各別存了什麼東西的細節,建議大家去看。
20. 目前UE5.1中在關掉靜態光的情況下已經可以支援所有的Shading Model
21. Eye shading model有一點特殊,它用到了subsurface profile shading model,所以在Mobile上它用了5個custom data,所以我們就只能把其中二個custom data壓縮到一個通道裡。所會如果看到結果感覺到精度不足,很有可能就是這個原因。
22. 由於GBuffer已經把Tiled Memory都佔用了,因此不能使用MSAA,因此目前引擎只支援FXAA跟TAA跟5.1新增由AMD研發的FSR技術。雖然FSR的效率上比不上有硬體支援的MSAA,不過效果不差。
23. 補充FSR:由官網的資料看來,他是個可以用在全平台的技術,不像NVDIA的DLSS需要用到GeForce RTX上專門的Tensor核心。
24. 關於lighting跟shadow的支援狀況:
a. Directional light pass:支援Light function material、Clustered local light(runtime toggle:r.Mobile.UseClusteredDeferredShading)、planar reflection、CSM and distance field shadow以及Reflection。
b. Reflection and sky pass:如果只有一個方向光就會合併到Directional light pass裡一起做(inline),不然為了避重覆計算會統一在這個pass做。
c. Local Light pass:Light area masked with stencil、support movable spot light shadow、support IES Profile、Support light function。Local light是分二次繪制的,第一次是用stencil 報標記燈光所覆蓋的區域,第二次是在這個區域的像素裡去著色,也是為了性能優化。
d. Simple light pass:這個是粒子裡創建的簡單光源,通常它是在clustered local light階段繪制的,如果沒有開啟clustered local light,才會有自己的pass。它的過程和local light的pass類似。
25. 在UE5.1中,mobile上也支持了Deferred render lighting channel,不過我們的方式跟PC上的不太一樣,因為PC上是把Lighting channel寫到stencil buffer裡,然後在shader裡面去讀取,這個對於mobile device來說有點困難。因為OpenGLES不支持TextureView的擴展,所以我們在硬體上就是直接在stencil test上做。
26. 在硬體上就是直接在stencil test上做的方法如下:我們只遍歷所有的燈光,只做一次lighting channel的stencil test。但由於每一盞燈只支持一個lighting channel,如果有多個lighting channel的話就會出錯。這也是美術上需要留意的地方。
27. 關於優化,我們主要是做了以下二個優化
a. 只在必須的情況下才去把數據存下來,比如DepthAux,我們直接申明成memory less,又因為我們不支持MSAA,所以GBuffe也可以弄成memory less。還有就是SceneDepth,只在需要的時候才用到,如果PP階段沒有用到Depth的訊息我們就可以不用存下來。
b. 另外我們發現開啟deferred rendering之後,半透明階段的渲染耗時比以前高,後來是發現是因為半透明階段,默認也開了Clustered local light和Clustered reflection,這個對性能有比較明顯的影響。然後我們本身對於半透明的光照和反射效果的需求並沒有那麼高,所以我們在半透明階段關掉這些計算,這樣半透明的效率就和forward持平了。
28. fornite在season 22會開啟,有做了初步的測試,發現,如果用vulkan的話性能會有普遍改善、卡頓變少,不過OpenGLES測下來發現性能會變差,後來發現是因為DXC轉譯Shader有點問題導致性能下降,並不是因為deferred rendering的問題,後續會修覆之後再針對OpenGLES打開
===================================
廣告時間
我有建立unreal discord討論區,歡迎大家加入一起討論: https://discord.gg/FanK6yc
我在Marketplace上有上架不少Plugin,歡迎大家選購: https://www.unrealengine.com/marketplace/en-US/profile/horizon-studio
Leave a Reply