NVIDIA OptiX 光线追踪引擎是一个可在 GPU 上实现最优光线追踪性能的应用框架。使用 OptiX 的应用可能会以难以诊断的方式失败:无效的 API 参数、黑屏,或埋藏在数千并发线程下的 GPU 端错误。
NVIDIA OptiX 工具包 (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 运行时 API 和 CUDA 驱动 API 遵循类似的模式。这三个 API 都支持以下功能:
- 用于错误码的独立枚举类型;例如
OptixResult - 返回错误码符号名称的字符串的函数;例如
OPTIX_ERROR_INVALID_VALUE - 返回错误码易读错误消息的函数;例如
Invalid value
这三个 API 的函数签名略有不同,但机制相同。
错误检查策略
在每个 API 调用点手动处理这些错误码既繁琐又容易出错。最好使用宏或函数调用来强制执行一致的错误处理策略。OTK 提供了在检测到错误时实现两种策略的宏:
OTK_ERROR_CHECK( expr ):抛出异常OTK_ERROR_CHECK_NOTHROW( expr ):向std::cerr打印消息并继续
通过重用提供的部分机制并创建适当的宏,很容易实现其他策略。
宏机制的最小化使用
此错误检查机制仅在最小程度上使用宏,并委托内联函数执行实际工作。您可以在内联函数定义中设置断点,以便在函数检测到错误时让调试器停止执行。
宏的存在是为了提供导致错误代码的诊断信息:
expr:宏提供的参数的字符串形式。这是计算得出错误码的表达式。__FILE__:宏被调用的源文件名。__LINE__:宏被调用的源文件中的行号。
来自宏调用点的信息将被传递给执行实际错误检查的内联函数。
跨 API 的统一错误检查
内联模板函数 checkError 检查状态码以发现错误,并在检测到故障时创建诊断消息。对于这三个 API,将错误码简单地强制转换为 bool 就足以指示错误。这三个 API 都使用零状态码表示成功,非零表示失败。
错误消息的格式如下:
file(line): expr failed with error nnn (name): message
其中 expr 是已计算的表达式,nnn 是状态码强制转换为 int 的结果,name 是状态码的符号名称,message 是易读的错误消息。如果名称或消息为空,函数将省略它们。
内联模板函数 makeErrorString 负责构建此消息。它调用内联模板函数 getErrorName 和 getErrorMessage 来构建组合消息。
每个 API 都有不同的 API 状态码类型,因此您可以特化模板函数以执行适当的 API 调用来获取扩展的错误信息。
用法
OTK 为每个 API 提供一个头文件,其中包含所描述模板函数的必要特化(表 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为真dumpSuppressed为假debugIndexSet为真,并且- 当前启动索引与
debugIndex匹配
在 OptiX 管线的启动参数中包含 DebugLocation 结构体实例,以实现调试输出的交互式控制。
debugInfoDump 函数
模板函数 debugInfoDump 提供了发出调试信息的接口:
template <typename Callback>
static __forceinline__ __device__
bool debugInfoDump( const DebugLocation& debug,
const Callback &callback )
Callback 模板参数应为符合以下条件的 struct 或 class:
struct Callback
{
void setColor( float red, float green, float blue );
void dump( const uint3& index );
};
setColor 方法用于在调试位置周围绘制一个可视框,以便于识别屏幕上转储信息的点。典型用法是为与当前启动索引对应的输出像素设置颜色。如果不需要调试位置的可视指示,该方法可以为空。
dump 方法用于在提供的启动索引处打印应用认为相关的任何信息。
显示调试位置
启用时,回调结构上的 setColor 方法会在屏幕上绘制一个框以指示当前调试位置,即使在转储输出被抑制时也是如此。禁用时框会被隐藏。
调试位置的红色像素位于一个像素宽的黑色框内,该黑色框本身位于一个像素宽的白色框内。这提供了一个高对比度的指示器,指示转储消息发生的位置。如果输出缓冲区不是传统的颜色缓冲区,您可以自由地将提供的红、绿、蓝值映射到某些不同的值进行可视化。
单次触发模式
为了避免淹没在调试输出中,最好让调试输出处于 单次触发 模式,即在用户控制下转储一次输出。您可以按如下方式排序此模式:
- 启用
DebugLocation机制 - 正常启动
- 当用户交互式选择调试位置时,将
dumpSuppressed设置为true,debugIndexSet设置为true,并将debugIndex设置为所选位置 - 后续启动会显示调试位置,但机制不提供转储
- 用户与应用交互以将其操作到适当状态,可能在此过程中移动调试位置
- 当用户指示想要当前位置的调试信息时,将
dumpSuppressed设置为false - 启动以获取调试输出
- 启动后将
dumpSuppressed恢复为true
DemandPbrtScene 示例
OTK 中的 DemandPbrtScene 示例 展示了 pbrt 版本 3 场景 的按需加载几何体。它使用了 DebugLocation 机制,包括单次触发行为以及调试输出和调试位置的交互式切换。它使用 ImGui 作为 UI 框架。

要从 OTK 运行此示例,您需要 pbrt-v3 的场景文件。
OptiX 工具包为常见的 OptiX 开发问题提供了可重用的调试和测试实用程序:一致的 API 错误检查和有针对性的设备端调试输出。代码可通过 NVIDIA/optix-toolkit GitHub 仓库获取。
准备好开始了吗?从 GitHub 下载 OptiX 工具包,首先在调试构建中启用 OptiX 验证,使用 OTK_ERROR_CHECK 包装您的 CUDA 和 OptiX 调用,并使用 DebugLocation 在开发早期隔离 GPU 端错误。OTK 采用宽松的 BSD 3-clause 风格许可证,因此您可以直接将这些实用程序复制、改编并集成到您自己的 OptiX 应用中。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.