逐步解說:因管線設定錯誤而遺漏的物件
本逐步解說示範如何使用 Visual Studio 圖形診斷工具檢查因為未設定的像素著色器而遺失的物件。
這個逐步解說將說明這些工作:
使用 [圖形事件清單] 尋找此問題的潛在來源。
使用 [圖形管線階段] 視窗檢查 DrawIndexed Direct3D 應用程式開發介面呼叫的作用。
檢查裝置內容以確認著色器階段未設定。
使用 [圖形事件呼叫堆疊] 與 [圖形管線階段] 視窗幫助尋找未設定的像素著色器的來源。
情節
當物件在 3D 應用程式遺失時,有時是因為在物件轉譯前其中一個著色器階段未設定。 在需要簡單轉換的應用程式,發生這個錯誤的來源通常位於某物件的繪製呼叫的呼叫堆疊。 不過,為了最佳化,某些應用程式一起批次處理有同樣著色器程式、材質,或其他資料的物件以最小化階段轉換的運作時間。 在這些應用程式,錯誤的來源可能會埋沒在批次處理系統,而不是位於繪製呼叫的呼叫堆疊。 在這個逐步解說的案例示範需要簡單的轉換的應用程式,因此錯誤的來源可以於呼叫堆疊上找到。
在這個情節中,當執行應用程式以進行測試時,背景會依預期轉譯,但是其中一個物件沒有出現: 使用圖形診斷,就可以將問題擷取至圖形記錄檔,讓您可以偵錯應用程式。 這個問題在應用程式中如下所示:
調查
使用圖形診斷工具,您可以載入圖形記錄文件,檢查測試期間所擷取的框架。
檢查圖形記錄中的框架
在 Visual Studio 中,載入包含顯現遺漏物件之框架的圖形記錄文件。 新的圖形記錄索引標籤隨即出現在 Visual Studio 中。 這個索引標籤的上半部是所選取框架的轉譯目標輸出。 下半部是 [框架清單],顯示每個擷取的框架為縮圖影像。
在 [框架清單] 中,選取示範未顯示物件的框架。 更新呈現目標反映選取的框架。 在這個情節中,圖形記錄索引標籤如下所示:
在選取示範問題的框架後,您可以使用 [圖形事件清單] 開始進行診斷。 [圖形事件清單] 包含每個會呈現使用中框架的 Direct3D 應用程式開發介面呼叫,例如設定裝置狀態,建立和更新緩衝區的應用程式開發介面呼叫和繪製顯示在框架 (Frame) 中的物件的應用程式開發介面呼叫。 許多類型的呼叫都很有趣,因為當應用程式如預期般運作 (例如,繪製、分派、複製或清除呼叫) 時,轉譯目標中通常 (但不一定) 會有對應的變更。 繪製呼叫特別值得注意,因為每一個表示應用程式呈現的幾何圖形。
因為您知道轉譯目標不包括遺漏的物件,但是並沒有發生其他錯誤,您可以使用 [圖形事件清單] 搭配 [圖形管線階段] 工具來判斷哪一個繪製呼叫對應至遺漏的幾何。 不論呈現目標的效果如何, [圖形管線階段] 視窗顯示已傳送至每個繪製呼叫的幾何資料。 當您瀏覽繪製呼叫時,管線階段會在依序執行每一個作用的階段時更新以顯示與每次呼叫相關聯的幾何,而轉譯目標輸出會更新以顯示轉譯目標在呼叫完成後的狀態。
尋找遺失幾何的繪製呼叫
開啟 [圖形事件清單] 視窗。 在 [圖形診斷] 工具列上,選擇 [事件清單]。
開啟 [圖形管線階段] 視窗。 在 [圖形診斷] 工具列上,選擇 [管線階段]。
當您瀏覽 [圖形事件清單] 視窗中的每個繪製呼叫時,請在 [圖形管線階段] 視窗中注意查看遺漏的物件。 若要使這項工作更容易執行,請在 [圖形事件清單] 視窗右上角的 [搜尋] 方塊中輸入「繪製」。 這會篩選清單,使其只包含標題中含有「繪製」的事件。
在 [圖形管線階段] 視窗中,[輸入組合語言] 階段會顯示物件在轉換之前的幾何,而 [端點著色器] 階段顯示在轉換之後的同一個物件。 在這個案例中,請注意 [圖形管線階段] 視窗顯示 [輸入組件] 和 [ 頂點著色器] 階段,不過,其中一個不是 [像素著色器] 階段繪製呼叫。
注意事項 如果其他管道階段,例如輪廓著色器、網域著色器或幾何著色器階段,會處理物件,任一階段可能就是問題的原因。一般而言,問題與初期階段相關,在該階段中不會顯示結果,或者會以非預期的方式顯示結果。
當您到了對應到遺失物件的繪製呼叫時請停止。 在這個情節中, [圖形管線階段] 視窗顯示幾何圖形發給 GPU (由 [輸入組件] 階段表示) 並已轉換 (由 [頂點著色器] 階段表示),但未在轉譯目標出現,因為看起來似乎是沒有作用中的像素著色器 (由缺少 [像素著色器] 階段表示)。 在這個案例中,您甚至可以在 [輸出合併] 階段中看到遺漏物件的黑色輪廓:
在您確認應用程式發出遺漏的幾何物件的繪製呼叫且發現像素著色器階段非作用中,您可以檢查裝置狀態確認您的發現。 您可以使用 [圖形物件表] 檢查裝置內容和其他 Direct3D 物件資料。
檢查裝置內容。
開啟 [d3d11 裝置內容]。 在 [圖形管線階段] 視窗中,選取是顯示在視窗頂端的 DrawIndexed 呼叫的一部分的 [ID3D11DeviceContext] 連結。
檢查於 [d3d11 裝置內容。] 索引標籤顯示的裝置狀態以確認像素著色器在繪製呼叫期間為非使用中。 在這個案例中, [著色器一般資訊]—顯示在 [像素著色器狀態] 底下—表示著色器是 [NULL]:
在您檢查像素著色器是由您的應用程式設定為 NULL 之後,下一步是尋找在應用程式的原始程式碼中著色器設定的位置。 您可以搭配使用 [圖形事件呼叫堆疊] 及 [圖形事件清單] 尋找這個位置。
尋找應用程式原始程式碼中的像素著色器位置
尋找對應至遺漏的物件的 PSSetShader 呼叫。 在 [圖形事件清單] 視窗中,輸入 "Draw;PSSetShader" 至 [圖形事件清單] 視窗右上角的 [搜尋] 方塊。 這會篩選清單,使其只包含「PSSetShader」事件中含有「繪製」的事件。 選取遺漏物件的繪製呼叫之前的第一個 PSSetShader 呼叫。
注意事項 如果在這個框架期間未設定PSSetShader,它不會出現在 [圖形事件清單] 視窗中。通常發生這件事時,只在如果一個像素著色器用於所有物件才會發生,或者如果 PSSetShader 呼叫在這個框架期間不小心略過。在任何情況下,我們建議您在應用程式的原始程式碼搜尋 PSSetShader 呼叫,並使用傳統的偵錯技術檢查這些呼叫行為。
開啟 [圖形事件呼叫堆疊] 視窗。 在 [圖形診斷] 工具列上,選擇 [圖形事件呼叫堆疊]。
使用呼叫堆疊於應用程式的原始程式碼找出 PSSetShader 呼叫。 在 [圖形事件呼叫堆疊] 視窗中,選取最上方的呼叫並檢查值像素著色器設定。 像素著色器可能直接設定為 null,或是 null 值可能因為傳入函式或其他狀態的引數而產生。 如果不是直接設定,您可能可以某處呼叫堆疊偵測到 null 值的來源。 在這個案例中,您會發現像素著色器直接設定至最上層函式的 nullptr ,名為 CubeRenderer::Render:
注意事項 如果您無法透過檢查呼叫堆疊偵測 null 值的來源,建議您在 PSSetShader 呼叫設立條件式中斷點,如此在像素著色器將設定為 null 時程式執行中斷。然後以偵錯模式重新開始執行應用程式並使用傳統的偵錯技術偵測 null 值的來源。
若要解決這個問題,使用 ID3D11DeviceContext::PSSetShader API 呼叫的第一個參數指派正確的像素著色器。
修正程式碼之後,您可以重建並再次執行應用程式來確認轉譯問題已解決: