2011年2月15日 星期二

Early-Z

介紹 Early-Z 之前先來回顧一下 Z-Buffer 的歷史. 並且會以 DirectX 為例.

最早還沒有 3D 加速卡之前, 為了解決深度排序的問題, 因此發展出了各式各樣的演算法, 但是對於太複雜的物件時, 深度排序依然無法完美的解決問題, 因此後來就發展出來了 Z-Buffer, 最早的 Z-Buffer 是屬於軟體 Z-Buffer, 後來才成為 3D 加速卡的標準規格.

Z-Buffer 解決了複雜的深度排序卻也帶來了另一個問題, 就是 3D 加速卡才有的問題-像素填充率, 簡單的說明像素填充率就是顯示卡每秒能畫在螢幕上的像素的數量是有限制的, 而每顆 不同等級GPU 所能承載的像素填充率也不相同.

但是隨著遊戲畫面越來越精細, 物件越來越多, 這個問題就越來越嚴重, 畫面上同一個像素的位置被重覆畫了太多次, 我們稱之為 Overdraw, 因此後來才會有建議在 Render 時儘量由前畫到後, 利用 Z-Buffer 來過濾 Overdraw 的問題, 這方法就和軟體 3D 為了解決深度排序的問題由後畫到前不同了.

只是演算法沒有完美的, 硬體也沒有完美的, 在硬體的限制下, DrawPrimitive SetRenderState 呼叫越多次, 速度就會越慢, 所以在複雜的畫面之下該用什麼方式 Render 一直是被討論的話題. 由前畫到後, 可以解決 Overdraw 的問題, SetRenderState 卻有可能成了效能的瓶頸; 使用 Texture 優先 Render, 可以解決頻繁切換 SetRenderState 的問題, 但是又有 Overdraw 的問題, 哪個方法好是見人見智的問題, 每套不同的 3D Graphics Engine 的處理方式也不一樣.

幾年前 nVidia 發表了一項技術 – Early-Z, Early-Z 的做法就是先將畫面的物件(所有或部份物件)的 Z 值先寫入到 Z-Buffer 中, 如此一來就可以解決一些問題, 但是結果並非如此完美. 為了讓物件 Z 值先寫入 Z-Buffer 中, 物件必須渲染兩次, 第一次先將 Z 值寫入, 第二次才真的將物件繪製出來. 這個方法帶來了極大的代價, 而且並不是每種狀況都適用.

1. 渲染過程變得更複雜化, 因為第一次只寫入 Z 值不繪出物件, 遊戲中幾百甚至幾千個物件的 RenderState 都必須設過, 即使是在最優化的情況下, 一定數量的 RenderState 設定還是無法避免, 更不用說你還要使用什麼樣的 Render 順序, 以及將物件給提取出來...等等, 這些都是效能的消耗.

RenderState 就像一個個的開關, 在 DirectX 9 以前, 一次只能開關一個, 所以要設幾個就必須呼叫幾次, 到了 DirectX 10之後, 新的硬體架構允許一次設定多個 RenderState, 也就是一次就可以開關好幾個 RenderState.

那使用 Shader 呢? Engine 裡某些基本事情還是必須做的, 更不用提 SetShader Code還要再做一次的問題了.

2. 由於物件要繪製兩次, 因此呼叫 DrawPrimitive 的次數也會變成兩倍, 所有的 DirectX 教學都再三的警告我們要減少呼叫 DrawPrimitive 的次數, 因為 DrawPrimitive 是很非常耗效能的.

3. 物件的頂點全部都要計算兩次 (Vertex Shader).

4. Alpha 物件無法參與 Early-Z.

那麼難道 Early-Z 真的沒有用嗎? 當然是有用, 但是要看情況, 就以 MMORPG 來說應該是不適用的. 第一個使用 Early-Z 技術的就是 Doom3, 但是它使用是有原因的, 因為它的 Pixel Shader 使用量非常的吃重, 因此 Early-Z 可以來幫助它減輕 PS 的負擔.

隨著技術的演進, 硬體也跟著演進,

這是 DirectX 9 管線的一角, 圖中可以看出, Depth Test 是在 PS 之後, 因此 Early-Z 的卻是有幫助的.


下圖則是 DirectX 10 管線的一角, 圖中可以看出, Depth Test 是在 PS 之前, 是的, Early-Z 已和 Z-Buffer 一樣變成由硬體加速的標準規格了.


但是, DirectX 10已移除了 AlphaTest texkill 來做, 因此如果有 Alpha 物件的話就會使得 Early-Z 失效.

感覺 DirectX 9 更應該使用軟體 Early-Z 不是嗎? 但是你願意付出上面那幾點中的 1 ~ 3點來換取小小的效能提升嗎? Nvidia 說可以提高 20% 以上的效能, 這是有限制的, 在 MMORPG 這麼複雜的畫面之下根本是不可能達到這個數字, 而且動態物件很多, Alpha 物件也不少, Early-Z 能做的其實非常的有限.

在公司新的 Terrain Engine 中做過實驗, 雖然Terrain Render 是純 Shader, 但是因為分割成一個個小塊, 因此 DrawPrimitive 和其它都無法避免多次的呼叫, 在效能上不但沒有提升反而變得更糟.

另外就是渲染過程變得更複雜化就覺得很不值得, 程式會變得更難維護, 更不用說當你的效能瓶頸不是在 像素填充率 或是 PS 上時, Early-Z 就更顯得沒有意義.

DirectX 10 之後 Early-Z 已由硬體支援加速, 因此軟體 Early-Z 也更沒有必要.

想要在 DirectX 9 上使用 Early-Z 最好先確定.
  1. 效能瓶頸在像素填充率上.
  2. 效能瓶頸在 PS 上.
當瓶頸在像素填充率上時, 先看看你的 Render 順序的演算法是否有弄好, 物件的裁剪是有否做好, View Frustum Culling 是否有做好.

當瓶頸在 PS 上時, 先看你的 PS 是否處理太多的事情, 某些運算是否能夠移到 VS 就先做好. 目前公司的要求的低標配備是不允許有大量的 PS 運算的.

然後, 最後再來考慮 Early-Z 的問題, 如果你效能瓶頸不再這兩項上, 使用 Early-Z 只會增加困擾而已.

什麼時候 DirectX 9 該用 Early-Z? 以目前來說是不需要.

1. 想要減少 RenderState 的呼叫必須使用 DirectX 10以上, 以目前公司要求的低標配備是不允許的.

2. 想要減少 DrawPrimitive 的呼叫必須使用就必須使用 Hardware Instance, 這功能要 DirectX 9.0c Shader 3.0 以上才有的功能, 以目前公司要求的低標配備也是不允許的.

那麼當公司的低標配備提高了的話是否就可以使用軟體 Early-Z? 那時候就不如直接上 DirectX 10 以上吧.

沒有留言:

張貼留言