NVIDIA OptiX 光線追蹤引擎是用於在 GPU 上實現最佳光線追蹤效能的應用程式框架。使用 OptiX 的應用程式可能以難以診斷的方式失敗:無效的 API 引數、黑畫面,或是埋藏在數千個並行執行緒之下的 GPU 端錯誤。

NVIDIA OptiX Toolkit (OTK) 中的偵錯功能可以提供協助。OTK 是一個包含支援 GPU 光線追蹤應用程式常見工作流程的工具集的 GitHub 儲存庫。OTK 採用 BSD 3-clause 風格的授權,因此您可以自由複製和修改任何程式碼。

本文涵蓋 OTK 的兩項偵錯功能:一致性地檢查 OptiX 和 CUDA API 錯誤碼,以及針對性的裝置端偵錯列印。OTK 包含一個範例程式 DemandPbrtScene,展示在實際情境中使用裝置端偵錯列印。

OptiX 記錄與驗證如何運作?

在探討 OTK 提供的協助之前,了解 OptiX 記錄的一些背景知識會很有幫助。當您建立 OptiX 裝置情境時,會提供一個選項結構,允許您設定記錄回呼和驗證模式。當驗證模式設定為 OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL 時,OptiX 可以協助驗證 API 函式的輸入。

OptiX 會將驗證錯誤的可讀訊息寫入記錄。記錄輸出應該是您尋找 API 錯誤(如 OPTIX_ERROR_INVALID_VALUE)的第一個地方。驗證確實會對 API 帶來一些額外的成本。建議在偵錯和測試版本中啟用所有驗證,並在發行版本中省略驗證。如需更多詳細資訊,請參閱 OptiX SDK 範例

如何一致性地檢查回傳碼的錯誤

如本節詳述,最好盡早偵測錯誤,以免後續的失敗掩蓋原始問題。

API 機制

OptiX 中的大多數函式都會回傳 OptixResult 錯誤碼,當非零時表示發生錯誤。CUDA 執行階段 APICUDA 驅動程式 API 遵循類似的模式。這三個 API 都支援以下功能:

  • 用於錯誤碼的獨立列舉型別;例如 OptixResult
  • 用於將錯誤碼回傳為字串符號名稱的函式;例如 OPTIX_ERROR_INVALID_VALUE
  • 用於將錯誤碼回傳為可讀錯誤訊息的函式;例如 Invalid value

這些函式的簽章在三個 API 中略有不同,但機制相同。

錯誤檢查原則

在每個 API 呼叫位置手動處理這些錯誤碼既繁瑣又容易出錯。最好使用巨集或函式呼叫來強制執行一致的錯誤處理原則。OTK 提供在偵測到錯誤時實作兩種原則的巨集:

其他原則可以透過重用提供的機制並建立適當的巨集來輕鬆實作。

巨集機制的簡要使用

此錯誤檢查機制將巨集的使用降到最低,並委派內嵌函式來執行實際工作。您可以在內嵌函式定義中設定中斷點,讓偵錯工具在函式偵測到錯誤時停止執行。

巨集的存在是為了提供有關導致錯誤的程式碼的診斷資訊:

  • expr:巨集提供引數的字串形式。這是評估為錯誤碼的運算式。
  • __FILE__:巨集被呼叫的原始碼檔案名稱。
  • __LINE__:巨集被呼叫的原始碼檔案中的行號。

這些來自巨集呼叫位置的資訊會傳遞給執行實際錯誤檢查的內嵌函式。

跨 API 的統一錯誤檢查

內嵌範本函式 checkError 會檢查狀態碼是否有錯誤,並在偵測到失敗時建立診斷訊息。對於這三個 API,將錯誤碼轉型為 bool 就足以表示錯誤。這三個 API 都使用零狀態碼表示成功,非零表示失敗。

錯誤訊息的格式如下:

file(line): expr failed with error nnn (name): message

其中 expr 是評估後的運算式,nnn 是狀態碼轉型為 int 的結果,name 是狀態碼的符號名稱,message 是可讀的錯誤訊息。如果名稱或訊息為空,函式會省略它們。

內嵌範本函式 makeErrorString 負責建立此訊息。它會呼叫內嵌範本函式 getErrorNamegetErrorMessage 來建立合併的訊息。

每個 API 都有不同的 API 狀態碼型別,因此您可以將範本函式特化,以執行適當的 API 呼叫來取得延伸的錯誤資訊。

使用方式

OTK 為每個 API 提供一個標頭,包含所述範本函式的必要特化(表 1)。

表 1. 錯誤檢查標頭

只要包含您正在使用的 API 的標頭,並在所有呼叫位置使用單一巨集 OTK_ERROR_CHECK。以下範例使用所有三個 API。

OTK_ERROR_CHECK( cudaSetDevice( m_deviceIndex ) );
OTK_ERROR_CHECK( cuCtxGetCurrent( &m_cudaContext ) );
OTK_ERROR_CHECK( cuStreamCreate( &m_stream, CU_STREAM_DEFAULT ) );
OTK_ERROR_CHECK( optixInit() );

如何執行針對性的裝置端偵錯列印

圖形應用程式的問題在於有太多方法可以編寫出黑畫面。

若要偵錯 OptiX 裝置程式碼中的問題,您可以採用幾種方法。有幾個明顯的選擇:

  • 對裝置程式碼的偵錯版本使用 CUDA 偵錯工具
  • 使用 printf 從裝置程式碼的發行版本取得資訊

許多應用程式在偵錯模式下編譯時執行速度太慢,影響互動式偵錯工具的使用。

printf 風格偵錯的主要困難在於 GPU 上有太多執行緒同時執行,您可能會淹沒在輸出洪流中。此外,問題可能只在與應用程式進行一定程度的互動後才會出現。問題在視覺上顯現之前的偵錯輸出只是噪音,會妨礙找到所需的資訊。

DebugLocation

標頭 <OptiXToolkit/ShaderUtil/DebugLocation.h> 提供可重複使用的偵錯輸出機制。結構 DebugLocation 控制行為:

struct DebugLocation
{
    bool  enabled;
    bool  dumpSuppressed;
    bool  debugIndexSet;
    uint3 debugIndex;
};

enabled 成員開啟或關閉整個機制。dumpSuppressed 成員即使機制已啟用,也會關閉偵錯輸出。debugIndexSet 成員表示已將有效的啟動索引儲存在 debugIndex 中。

當以下條件為真時,機制會輸出偵錯資訊:

  • enabled 為 true
  • dumpSuppressed 為 false
  • debugIndexSet 為 true,以及
  • 目前的啟動索引符合 debugIndex

在 OptiX 管線的啟動參數中包含 DebugLocation 結構的實例,以進行偵錯輸出的互動式控制。

debugInfoDump 函式

範本函式 debugInfoDump 提供發出偵錯資訊的介面:

template <typename Callback>
static __forceinline__ __device__
bool debugInfoDump( const DebugLocation& debug,
                    const Callback &callback )

Callback 範本參數應該是符合以下條件的 structclass

struct Callback
{
    void setColor( float red, float green, float blue );
    void dump( const uint3& index );
};

setColor 方法用於在偵錯位置周圍繪製視覺方塊,以便輕鬆識別畫面上要傾印資訊的點。典型的用法是為對應目前啟動索引的輸出像素設定顏色。如果不需要偵錯位置的視覺指示,方法可以留空。

dump 方法用於在提供的啟動索引列印應用程式認為相關的任何資訊。

顯示偵錯位置

啟用時,回呼結構上的 setColor 方法會在畫面上繪製方塊以指示目前的偵錯位置,即使傾印輸出被抑制也是如此。停用時方塊會隱藏。

偵錯位置的紅色像素位於單一像素寬的黑色方塊內部,該方塊本身位於單一像素寬的白色方塊內部。這提供了傾印訊息發生位置的高對比指示器。如果輸出緩衝區不是傳統色彩緩衝區,您可以自由將提供的紅、綠、藍值對應到某些獨特的值以進行視覺化。

單次模式

為了避免淹沒在偵錯輸出中,擁有單次模式的偵錯輸出會很有用,其中輸出會根據使用者控制傾印一次。您可以按以下方式排列此模式:

  1. 啟用 DebugLocation 機制
  2. 照常啟動
  3. 當使用者互動式選擇偵錯位置時,將 dumpSuppressed 設為 truedebugIndexSet 設為 true,並將 debugIndex 設為所選位置
  4. 後續啟動會顯示偵錯位置,但機制不提供傾印
  5. 使用者與應用程式互動以將其操作至適當狀態,可能在此過程中移動偵錯位置
  6. 當使用者表示想要目前位置的偵錯資訊時,將 dumpSuppressed 設為 false
  7. 啟動以取得偵錯輸出
  8. 啟動後將 dumpSuppressed 設回 true

DemandPbrtScene 範例

OTK 中的 DemandPbrtScene 範例 示範 pbrt 版本 3 場景 的需求載入幾何。它使用 DebugLocation 機制,包括單次行為以及偵錯輸出和偵錯位置的互動式切換。它使用 ImGui 作為 UI 框架。

顯示偵錯輸出控制項和影像右側反白偵錯像素的螢幕截圖。
圖 1. DemandPbrtScene 偵錯控制項與反白偵錯像素

若要從 OTK 執行此範例,您需要 pbrt-v3 的場景檔案

OptiX Toolkit 為常見的 OptiX 開發問題提供可重複使用的偵錯和測試工具:一致的 API 錯誤檢查和針對性的裝置端偵錯輸出。程式碼可透過 NVIDIA/optix-toolkit GitHub 儲存庫取得。

準備好開始了嗎?從 GitHub 下載 OptiX Toolkit,並從在偵錯版本中啟用 OptiX 驗證、用 OTK_ERROR_CHECK 包裝 CUDA 和 OptiX 呼叫,以及使用 DebugLocation 在開發早期隔離 GPU 端錯誤開始。OTK 採用寬鬆的 BSD 3-clause 風格授權,因此您可以直接複製、調整和整合這些工具到您自己的 OptiX 應用程式中。