共用方式為


逐步解說:因端點著色而遺漏的物件

本逐步解說示範如何使用 Visual Studio 圖形診斷工具檢查因為端點著色器階段發生之錯誤而遺失的物件。

這個逐步解說將說明這些工作:

  • 使用 [圖形事件清單] 尋找此問題的潛在來源。

  • 使用 [圖形管線階段] 視窗檢查 DrawIndexed Direct3D 應用程式開發介面呼叫的作用。

  • 使用 [HLSL 偵錯工具] 檢查端著色器。

  • 使用 [圖形事件呼叫堆疊] 協助找到不正確 HLSL 常數的來源。

情節

3D 應用程式遺漏物件最常見的其中一個原因會在端點著色器以不正確或非預期方式轉換物件的頂點時發生,例如,物件可能會縮放到非常小,或是經轉換後出現在觀景窗後面而前面。

在這個情節中,當執行應用程式以進行測試時,背景會依預期轉譯,但是其中一個物件沒有出現: 使用圖形診斷,就可以將問題擷取至圖形記錄檔,讓您可以偵錯應用程式。 這個問題在應用程式中如下所示:

無法看到物件。

調查

使用圖形診斷工具,您可以載入圖形記錄檔,檢查測試期間所擷取的框架。

檢查圖形記錄中的框架

  1. 在 Visual Studio 中,載入包含顯現遺漏物件之框架的圖形記錄。 新的圖形記錄索引標籤隨即出現在 Visual Studio 中。 這個索引標籤的上半部是所選取框架的轉譯目標輸出。 下半部是 [框架清單],顯示每個擷取的框架為縮圖影像。

  2. 在 [框架清單] 中,選取示範未顯示物件的框架。 更新呈現目標反映選取的框架。 在這個情節中,圖形記錄索引標籤如下所示:

    Visual Studio 中的圖形記錄文件

在選取示範問題的框架後,您可以使用 [圖形事件清單] 開始進行診斷。 [圖形事件清單] 包含每個會呈現使用中框架的 Direct3D 應用程式開發介面呼叫,例如設定裝置狀態的應用程式開發介面呼叫,建立和更新緩衝區的應用程式開發介面呼叫和繪製顯示在框架 (Frame) 中的物件的應用程式開發介面呼叫。 許多類型的呼叫都很有趣,因為當應用程式如預期般運作 (例如,繪製、分派、複製或清除呼叫) 時,轉譯目標中通常 (但不一定) 會有對應的變更。 由於每一個繪製呼叫會表示應用程式所轉譯的幾何 (分派呼叫也可以轉譯幾何),因此繪製呼叫特別值得注意。

因為您知道遺漏的幾何沒有繪製到轉譯目標 (在此例中),但是如預期繪製場景的其餘部分,您可以使用 [圖形事件清單] 搭配 [圖形管線階段] 工具來判斷哪一個繪製呼叫對應至遺漏的幾何。 不論呈現目標的效果如何, [圖形管線階段] 視窗顯示已傳送至每個繪製呼叫的幾何資料。 當您瀏覽繪製呼叫時,管線階段會更新以顯示與該呼叫相關聯的幾何,而轉譯目標輸出會更新以顯示轉譯目標在呼叫完成後的狀態。

尋找遺失幾何的繪製呼叫

  1. 開啟 [圖形事件清單] 視窗。 在 [圖形診斷] 工具列上,選擇 [事件清單]。

  2. 開啟 [圖形管線階段] 視窗。 在 [圖形診斷] 工具列上,選擇 [管線階段]。

  3. 當您瀏覽 [圖形事件清單] 視窗中的每個繪製呼叫時,請在 [圖形管線階段] 視窗中注意查看遺漏的物件。 若要使這項工作更容易執行,請在 [圖形事件清單] 視窗右上角的 [搜尋] 方塊中輸入「繪製」。 這會篩選清單,使其只包含標題中含有「繪製」的事件。

    在 [圖形管線階段] 視窗中,[輸入組合語言] 階段會顯示物件在轉換之前的幾何,而 [端點著色器] 階段顯示在轉換之後的同一個物件。 在這個情節中,當遺漏物件顯示在 [輸入組合語言] 階段中,而沒有任何項目顯示在 [端點著色器] 階段時,您就會知道您找到了遺漏物件。

    注意事項注意事項

    如果其他幾何階段 (例如輪廓著色器、網域著色器或幾何著色器階段) 會處理物件,這些階段可能就是問題的原因。一般而言,問題與初期階段相關,在該階段中不會顯示結果,或者會以非預期的方式顯示結果。

  4. 當您到了對應到遺失物件的繪製呼叫時請停止。 在這個情節中,[圖形管線階段] 視窗顯示幾何已發給 GPU (由輸入組合語縮圖表示),但未在轉譯目標中出現,因為在端點著色器階段 (由端點著色器縮圖表示) 發生錯誤:

    DrawIndexed 事件及其對管線的影響

確認應用程式對遺漏物件的幾何發出繪製呼叫後而發現這個問題是在頂點著色器階段發生,您可以使用 HLSL 偵錯工具檢查頂點著色器,查明物件幾何發生什麼狀況。 您可以使用 HLSL 偵錯工具在執行時檢查 HLSL 變數狀態、逐步執行 HLSL 程式碼,以及設定能協助您診斷問題的中斷點。

檢查端點著色器

  1. 開始偵錯端點著色器階段。 在 [圖形管線階段] 視窗的 [端點著色器] 階段下,選擇 [開始偵錯] 按鈕。

  2. 由於 [輸入組合語言] 階段似乎會提供良好資料給頂點著色器,而 [頂點著色器] 階段顯然不會產生輸出,因此您會想要檢查頂點著色器輸出結構 output。 當您逐步執行 HLSL 程式碼時,如果 output 有修改,就可以看得更仔細。

  3. 第一次修改 output 時,則會寫入 worldPos 成員。

    "output.worldPos" 的值會合理顯示

    因為它的值似乎很合理,您繼續逐步執行程式碼,直到下一個修改 output 的程式碼行為止。

  4. 下次修改 output 時,則會寫入 pos 成員。

    "output.pos" 已變成 0

    這次,pos 成員的值 (全部為零) 看起來十分可疑。 接下來,您要判斷 output.pos 的值為何全都是零。

  5. 您會注意到 output.pos 會從名為 temp 的變數取得值。 在上一行,您會看到 temp 的值是將先前值乘以名為 projection 之常數的結果。 您懷疑 temp 的可疑值是這個乘法的結果。 當您將指標停在 projection 時,您會注意到它的值也全部為零。

    投影轉換矩陣包含不良的轉換

    在這個情節中,檢查顯示 temp 的可疑值最有可能是與 projection 的乘積造成,而且因為 projection 是視為包含投影矩陣的常數,所以您知道其不應包含全部為零。

在您判斷 HLSL 常數 projection (已由應用程式傳入著色器) 可能是問題來源之後,下一個步驟就是在應用程式的原始程式碼中尋找填入常數緩衝區的位置。 您可以使用 [圖形事件呼叫堆疊] 尋找這個位置。

尋找應用程式原始程式碼中的常數設定位置

  1. 開啟 [圖形事件呼叫堆疊] 視窗。 在 [圖形診斷] 工具列上,選擇 [圖形事件呼叫堆疊]。

  2. 在呼叫堆疊向上巡覽至應用程式的原始程式碼中。 在 [圖形事件呼叫堆疊] 視窗中,選擇最上方的呼叫查看常數緩衝區是否填入此處。 如果沒有,繼續向上查看呼叫堆疊,直到您找到其填入的位置。 在這個情節中,您會發現常數緩衝區正在進一步向上填滿 (使用 UpdateSubresource Direct3D 應用程式開發介面) 名為 MarbleMaze::Render 之函式中的呼叫堆疊,而且其值來自名為 m_marbleConstantBufferData 的常數緩衝區物件:

    設定物件之常數緩衝區的程式碼

    提示

    如果您同時偵錯應用程式,您可以在這個位置上設定中斷點,當轉譯下一個框架時就會叫用該中斷點。您可以檢查 m_marbleConstantBufferData 的成員,確認 projection 成員的值在填滿常數緩衝區時會設定為零。

在您找到填入常數緩衝區的位置而發現這是來自變數 m_marbleConstantBufferData 的值之後,下一個步驟就是找出 m_marbleConstantBufferData.projection 成員設定為全部為零的位置。 您可以使用 [尋找所有參考] 快速掃描變更 m_marbleConstantBufferData.projection值的程式碼。

尋找應用程式原始程式碼中的專案成員位置

  1. 尋找參考 m_marbleConstantBufferData.projection 開啟產品 m_marbleConstantBufferData 的捷徑功能表,然後選擇 [尋找所有參考]。

  2. 若要巡覽至應用程式原始程式碼中的行位置 (其中修改了 projection 成員),請在 [尋找符號結果] 視窗中尋找那一行。 由於修改投影成員的第一個結果可能不是問題的原因,您可能必須檢查應用程式原始程式碼的許多範圍。

找到設定 m_marbleConstantBufferData.projection 的位置後,您可以檢查周圍的原始程式碼以判斷無效值的來源。 在這個案例中,您會發現 m_marbleConstantBufferData.projection 的值被設定為區域變數,該變數在下一行的程式碼 m_camera->GetProjection(&projection); 中將被初始化為一個值之前被命名為 projection。

初始化滾珠迷宮前先設定其投影

要解決問題,需將設定 m_marbleConstantBufferData.projection 值的程式碼行移到初始化本機變數 projection 值的程式碼行之後。

修正過的 C++ 原始程式碼

修正程式碼之後,您可以重建並再次執行應用程式來確認轉譯問題已解決:

現在物件已顯示。