Por que apps OptiX falham em silêncio
Quem já colocou um ray tracer pra rodar em GPU NVIDIA conhece a dor: argumento inválido na API, frame preto, ou um bug device-side enterrado embaixo de milhares de threads concorrentes. O erro que você vê quase nunca é o que causou o problema. 😩
O NVIDIA OptiX Toolkit (OTK) é um repositório no GitHub com licença BSD 3-clause cheio de utilitários pra workflows comuns de ray tracing em GPU. Pode copiar, modificar e mandar pra produção sem medo. Neste post a gente cobre as duas ferramentas de debug que se pagam no primeiro dia: checagem consistente de erros de API e debug printing direcionado no device.
Baseado no post oficial do blog de developers da NVIDIA.

Checagem unificada de erros com OTK_ERROR_CHECK
Quase toda função do OptiX retorna um OptixResult. A CUDA runtime API e a CUDA driver API seguem o mesmo padrão: zero é sucesso, não-zero é falha. Tratar isso em cada call site é chato e propenso a erro, então o OTK te dá macros com duas políticas:
OTK_ERROR_CHECK(expr)— lança exceçãoOTK_ERROR_CHECK_NOTHROW(expr)— imprime emstd::cerre segue o baile
A macro captura expr, __FILE__ e __LINE__, e delega pra um template inline checkError que monta o diagnóstico:
// Inclua o header de cada API que você usa (CUDA driver, CUDA runtime, OptiX)
#include <otk/cuda_driver/error_check.h>
#include <otk/cuda_runtime/error_check.h>
#include <otk/optix/error_check.h>
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() );
Quando falha, você vê algo assim:
render.cpp(142): cudaSetDevice( m_deviceIndex ) failed with error 1 (CUDA_ERROR_INVALID_VALUE): invalid argument
Ativa OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL em builds de debug e teste — validação tem custo, então deixa desligada em release.

Debug printing direcionado no device com DebugLocation
Debug com printf na GPU é uma mangueira de incêndio: milhares de threads cuspindo output. A struct DebugLocation do OTK resolve isso filtrando output por um launch index específico:
struct DebugLocation
{
bool enabled;
bool dumpSuppressed;
bool debugIndexSet;
uint3 debugIndex;
};
O output só sai quando enabled == true, dumpSuppressed == false, debugIndexSet == true e o launch index atual bate com debugIndex. Joga uma instância nos launch parameters da pipeline e você ganha controle interativo.
O template debugInfoDump recebe um Callback com setColor(r,g,b) (desenha uma caixa de alto contraste em volta do pixel de debug — pixel vermelho, anel preto, anel branco) e dump(index) (seu payload de debug de verdade).
Workflow one-shot — o padrão que realmente economiza tempo:
- Ativa
DebugLocation, launch normal. - Usuário escolhe a posição de debug → seta
dumpSuppressed=true,debugIndexSet=true,debugIndex=selecionado. - Interage com o app até chegar no estado bugado (move o marcador conforme precisar).
- Vira
dumpSuppressed=false, um launch, captura o output, volta pratrue.
É exatamente isso que o exemplo DemandPbrtScene (geometria demand-loaded do pbrt-v3, UI com ImGui) demonstra ponta a ponta.

Limitações e o que ficar de olho
- Validação não é de graça.
OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALLdeixa a API mais lenta. Deixa desligada em release — a ideia é pegar bug antes de subir. - DebugLocation é single-index. Se o bug é race condition entre milhares de threads, isso não pega — você ainda vai precisar do compute-sanitizer ou cuda-gdb.
- printf no kernel continua sendo printf. O OTK reduz o ruído, não elimina o custo fundamental de I/O device-side.
Próximos passos
- Clona o NVIDIA/optix-toolkit e builda o exemplo
DemandPbrtScene. - Envolve todo call site de CUDA/OptiX com
OTK_ERROR_CHECKhoje — é um refactor de 30 minutos que se paga pra sempre. - Quando estiver confortável com OTK, sobe o nível da tua toolchain de GPU com a nova API single-call do CUB pra programação GPU simplificada.
- Se você também entrega frontend web pro teu renderer, vale dar uma olhada no StyleX pra CSS em escala.