http://www.manew.com/thread-92382-1-1.html
從版本5
開始,
Unity
包含了一個全新的可視化幀調試工具,
Frame Debugger
。該工具能幫你解決很多圖形方面的問題,
Z-fighting
,
GPU
狀態不正常,渲染隊列錯誤、混合操作錯誤,過多的
draw call
,效率低下等等。相比游戲視圖中的狀態列表,它提供了更加詳盡的信息,通過與渲染事件
/
步驟的交互和檢查,你也能學習到大量
GPU
管線的相關知識。真地,每個開發人員都應該了解這個工具。
本期內容中,我會簡要的敘述Frame Debugger
的使用方法,並在之后指出其不足之處和其他可以彌補這些問題的工具。我不打算面面俱到,只是對這些內容做一個總體概覽。
1. 使用Frame debugger
使用Window-> Frame Debugger
菜單打開其主窗口,我建議同時打開
frame debugger
和
game view
(游戲視圖)。為此你可能需要對窗口布局做些調整,本人用來調試圖形顯示時的界面如下圖:

Recommended layout (except for the Asset Store tab)
建議的窗口布局(除了資源商店標簽頁)
使游戲進入播放狀態,來到讓你頭痛腦熱、心情不爽的位置,在frame debugger
窗口中,點擊
enable
就能得到最近渲染幀的細節信息。
其中包含了兩個主要部分:事件,以及在其右側的詳細信息。事件中基本上都是CPU
發送給
GPU
的命令,相當的簡單,畢竟在此無法看到真正被調用的
API
函數,只是將整個過程按照時間順序打包顯示出來。其中大部分是清除和繪制(不透明
/
透明)幾何體的命令,但是你也可以注意到更多的信息,如:
GUI
渲染,陰影處理,圖形效果等以及相應的回調次數。
在選擇一個事件后,你可以立即在details
面板中得到關於該事件的詳細信息。包括:着色器范圍和其標志,渲染輸出事件和着色器屬性信息。同時,游戲視圖會顯示在該事件發生前已被渲染的對象(包括當前事件哦)。下面是一個例子
<ignore_js_op>
Event example

Event example
2. Frame Debugger干嘛用?
Frame debugger
為我們提供了一些有價值的信息。繪制事件的調用順序(也即是:渲染隊列)對渲染的正確性十分重要,如果游戲中的元素未能正確的顯示粗來,在此你就可能發現它未被正確的放入渲染隊列中,從而導致過早或延遲渲染;特別是在進行自定義着色器開發或Z write
(深度緩沖寫入)參數調整和測試時。通過這個工具為我解決了不少問題:查找出由於忘掉設置標志位和渲染次序而導致天空盒子覆蓋了俺着色器輸出的問題。
其次,相對的,你也可以在此查看draw call
的調用次數,並通過頂點
/
索引數量來間接衡量場景的渲染代價。當然,着色器
pass
數也有幫助意義,但其復雜度在此沒有顯示,這就要求你得有些基本常識了,所有這些信息都能幫助你提高場景的性能,比如:你會發現出於某些原因一些網格繪制未能進行靜態
/
動態批處理。在我的例子中,你會發現通過使用材質圖集來將兩個圖片精靈的
2
次
draw call
操作合並為
1
個。
第三,通過與Frame Debugger
交互,你可以快速學習
GPU
的體系架構知識並了解
Unity
渲染處理的流程。可以用鍵盤在不同的事件中跳轉一步步查看場景的渲染過程。在之前的例子中,可以看到場景渲染開始於對三個緩存的清理(顏色、
z
、
stencil),接着是不透明幾何體(從前面到背面),天空盒子和透明幾何體(從背面到前面)的渲染。
最后,你可以訪問着色器屬性以獲得關於材質和着色器的更多信息,也可以得到被對象使用的數據的引用,如:材質。大多數人都沒用過Unity
中的
advanced views,你可以在inspector中通過點擊右上部的paragraph圖標,並選擇“Debug”來訪問他們。
<ignore_js_op>

Shader properties
着色器屬性
2. Caveats
在為一個客戶工作期間,我也碰到了由於Frame Debugger
過於簡潔的界面而導致的問題。我在
Unite 2016 Europe中與Unity的一些開發做了交流,他們也清楚這種情況,但針對未來是否打算對其進行擴展時他們並沒表態。從我的經驗看,主要的問題有:
- 無法獲得事件之后API調用的底層信息。就這個問題來說,Unity將事件作為一個黑盒處理了,因此,在特定的條件下你將很難對這個問題作出改進。
- 無法獲得每個事件中GPU管線的總體狀態,只能得到關於幾何體和材質的信息,而缺少在不同階段,像頂點/幾何體/片段着色器和柵格化器中的數據。
- 無法真正的調試着色器。要么采用修改輸出顏色來進行代碼的可視化調試(比打印還遭罪)或者依賴像GLSL-Debugge類的外部軟件。
- 難以建立結果像素與寫出這些像素的相應事件的關聯。你得一步步檢查究竟是哪個事件在像素中寫入了錯誤的信息,在有大量的draw call調用時,這過程會相當耗時。
- 缺少能夠衡量每個draw call效率的精確指示器,頂點/索引的數量並不是表征渲染效率的最好手段。盡管更多的頂點需要更大的帶寬,但是在幾何體上執行一個復雜的頂點着色器或代價高昂的狀態轉換性能可能會更差。
如果至此你還願意繼續看下去,可能你也在尋找針對以上問題的解決方案,使用一些補充性的其他工具怎么樣呢?
4. RenderDoc
在進行了一些軟件的測試后,我鐵定可以為大家推薦幾款。你工具箱里的首選必須是
RenderDoc,Crytek開發的一款免費工具,用於解決底層調用信息的問題,該工具使用了網絡攻擊者的經典做法:它建立一個虛擬的圖形驅動作為中間人進行(MITM)捕獲應用中的DX/GL/Vulkan調用並提供詳細的調試信息。好消息是:該工具已經支持在Unity中原生集成,你只需在windows機器上安裝RenderDoc並重啟unity的編輯器即可。
本節俺的目標是為大家概述該工具的用法,讓你充滿興趣的開始,使用時更加順暢,而且少花電費。
我們開始吧,如果安裝順利,在游戲中你應該可以右鍵單擊並在菜單中選擇Load RenderDoc:
<ignore_js_op>

幾秒鍾后,進入播放模式,如果你對剛渲染的那幀感興趣,點擊最大化播放按鈕左側光頭佬一樣的新圖標:
<ignore_js_op>

接着切換到新打開的RenderDoc
窗口,雙擊自動捕捉的日志開始分析它:
<ignore_js_op>

RenderDoc captured log
Rendoc捕捉日志
哇哦,這就是信息的大海啊,我會淹死的。冷靜下來,一切都會順利。現在你會在幾千個窗口中得到所有幀的數據,表擔心,下面我會繼續講解真正有趣的內容。
4.1. Event browser + API calls.
4.1 事件瀏覽器+API調用
該工具的優點就是能夠獲得每個事件的底層信息,事件瀏覽器就像Unity
中
Frame Debugger
的一個擴展,其中還包括耗時(按毫秒計)和其他提示信息,能為衡量效率提供大量幫助。
RenderDoc
具有上下文敏感特征,也就是說,不論何時你選擇一個事件,大部分其他窗口和區域將會顯示僅與該事件相關的信息,其下的
API
調用窗口則列出了為處理該事件而調用的函數。
<ignore_js_op>

RenderDoc: Event Browser + APIcalls
能得到這些耗時數據真的很棒!雖然它們可能不是絕對精確,但是具有相對准確性,也就是說,通過比較耗時你就能推斷出哪些操作的draw call
需要優化。
4.2. Pipeline state.
管線狀態
我經常懷疑渲染管線的工作模式,精巧的幾何體是如何進入它的內部並轉換為50
寸屏幕上像素的?好吧,不幸的是,在這個工具中你也無法得到一個用戶友好的動畫以展示其工作過程。
但確實有些
很好的免費工具可以做到。不過,不管怎樣,RenderDoc能讓你訪問事件中的大量管線狀態信息:input assembler,頂點/體/域/幾何體/片段/計算着色器,柵格化器和輸出合並器。相信俺的判斷,RenderDoc不虛其名。
<ignore_js_op>

RenderDoc: Pipelinestate for a pixel shader
<ignore_js_op>

RenderDoc: Output Merger example
<ignore_js_op>

RenderDoc: Rasterizer state
4.3. Mesh output
網格輸出
RenderDoc
的另一項特征是可以通過頂點着色器的:輸入輸出位置,法線,紋理坐標等信息獲取事件中渲染的網格數據。實際上,你可以用它抓取並保存3D
程序或游戲中正在渲染的網格,但在這邊文章中我就不結合案例講解了。
4.4. Texture viewer
紋理觀察器
這是我的菜,將事件中的輸入和輸出紋理進行可視化展示。包括渲染到紋理或其他中間緩沖數據,在你使用中間緩沖進行圖像效果處理時會非常有趣,此外,這也也能能節省紋理空間。
<ignore_js_op>

RenderDoc: Texture viewer
4.5. Pixel debugging
像素級調試
在幀緩沖中,錯誤渲染的壞像素覆蓋正確像素的情況並不少見,大多數開發者都碰到過這種噩夢,壞像素在場景中留下的奇怪感受讓我們意識到有問題發生了,但卻不知原因何在,就是趕腳不對!如何確認我們的猜想並找出問題所在…
是的,以下是該問題的解法:選擇一個像素,點擊histroy
(得到一個在幀緩沖中向那個像素寫入信息的事件序列)或者
debug
(調試它的像素着色器)。調試這些問題需要真正的技術大牛,你需要匯編知識,並對像素着色器的反匯編后的版本有一定了解。
<ignore_js_op>

RenderDoc: Pixel selection
RenderDoc:像素選擇
<ignore_js_op>

RenderDoc: Pixel’s history
RenderDoc: 像素歷史
<ignore_js_op>

RenderDoc: shader debugger
5. Example
示例
有些同學會懷疑在實際工作中搞出性能分析器意義何在。我這就告訴你:非常必要。在不確定某項特性的性能影響時,使用性能分析器做下測試,不論是frame debugger
,或者必要的話,動用
RenderDoc
等類似工具都會對你的工作大有裨益。
我們來看個示例。不是所有人都知道在腳本中訪問材質和訪問渲染器的shaderMaterial
是有差別的,但只有少數人確切知道這會對性能產生影響,雖然大多數情形下是一樣的。不過既然我們手邊有工具,何不一探究竟?
我們的測試場景只有一個精靈,反復使用相同的transform
實例化
10000
次后,它占據了幾乎全部的視野,因此可以想見,其中有許多無需繪制的部分,代碼如下:
void Start () {
for(int i = 0; i < NumberSprites; ++i)
{
varsprite = Instantiate(Sprite);
sprite.transform.parent= transform;
varsharedMaterial = sprite.sharedMaterial;
// Uncomment me for creating amaterial copy per sprite.
//varmat = sprite.material;
}
}
訪問sharedMaterial
時很流暢,該材質僅有一個,並在所有精靈實例中共享;而訪問
material
時,就需要為每個精靈創建一個單獨的材質,這樣就無法對
Draw call
進行批處理,使用
renderDoc
查看
Render.TransparentGeometry
函數的分析結果如下:
<ignore_js_op>

我覺得RenerDoc
給出的時間開銷可信度不高。材質的讀取好像根本不准確,我也不知道這個示例中真實的硬件循環調用次數,我也查看了
Unity
的性能測試器。但無論哪種情況下,共享材質對比實例化新的材質,第二個實現方式都因為無法將
draw call
合並處理導致了更高的驅動開銷和帶寬開銷。
真正代價高昂的是drawcall
中的狀態轉換,而不是
drawcall
本身的數量。在使用
Material.SetColor
為每個精靈設置了不同的隨機顏色后,我注意到又掉了
2
個
FPS
。這個問題可以通過最近發布的
Vulkan API予以解決。
6. Conclusions
結論
RenderDoc
既不是一個完善的工具更無法替代Unity
的
frame debugger
,它只是一個完善性工具,你可以方便的使用它獲取需要的底層信息,能用到什么程度也是很有趣的,但你走的越遠,問題就越復雜,此時你必須對
GPU
的硬件體系有充分的了解。
我得承認,使用renderDoc
是個耗時的活,因此要記住:先做性能測試,然后再進行針對性的優化。
95%
的情況下你都可以用
Unity
自帶的
framedebugger
完成調試,但為了那可能使你生活立即變得不幸福的
5%
你也得做好萬全准備。
請將這些工具和性能測試器結合在一起使用,實際中總會有些瑣碎的情況,不知道瓶頸源自何處,特別是在一個缺少調試手段的平台進行開發時。
一如既往,感謝您反饋並提出問題!
原文鏈接:
http://www.gamasutra.com/blogs/R ... nough_RenderDoc.php