Al escribir sombreadores no triviales (al igual que al escribir cualquier otra pieza de código no trivial), las personas cometen errores. [cita requerida] Sin embargo, no puedo depurarlo como cualquier otro código; después de todo, no se puede simplemente adjuntar gdb o el depurador de Visual Studio. Ni siquiera puede hacer la depuración de printf, porque no hay forma de salida de la consola. Lo que suelo hacer es representar los datos que quiero ver como color, pero esa es una solución muy rudimentaria y amateur. Estoy seguro de que la gente ha encontrado mejores soluciones.
Entonces, ¿cómo puedo depurar un sombreador? ¿Hay alguna manera de pasar por un sombreador? ¿Puedo ver la ejecución del sombreador en un vértice / primitivo / fragmento específico?
(Esta pregunta es específicamente sobre cómo depurar el código del sombreador similar a cómo se depuraría el código "normal", no sobre cómo depurar cosas como los cambios de estado).
Respuestas:
Por lo que sé, no hay herramientas que le permitan pasar a través del código en un sombreador (también, en ese caso, debería poder seleccionar solo un píxel / vértice que desea "depurar", es probable que la ejecución variar dependiendo de eso).
Lo que personalmente hago es una "depuración colorida" muy hacky. Así que espolvoree un montón de ramas dinámicas con
#if DEBUG / #endif
guardias que básicamente dicenPara que pueda "observar" la información de depuración de esta manera. Usualmente hago varios trucos como saltar o mezclar entre varios "códigos de color" para probar varios eventos más complejos o cosas no binarias.
En este "marco" también me parece útil tener un conjunto de convenciones fijas para casos comunes, de modo que si no tengo que volver constantemente y verificar qué color asocie con qué. Lo importante es tener un buen soporte para la recarga en caliente del código de sombreador, por lo que puede cambiar casi interactivamente sus datos / eventos rastreados y activar / desactivar fácilmente la visualización de depuración.
Si necesita depurar algo que no puede mostrar fácilmente en la pantalla, siempre puede hacer lo mismo y usar una herramienta de análisis de cuadros para inspeccionar sus resultados. He enumerado un par de ellos como respuesta a esta otra pregunta.
Obvio, no hace falta decir que si no estoy "depurando" un sombreador de píxeles o sombreador de cálculo, paso esta información de "debugColor" a través de la tubería sin interpolarla (en GLSL con la
flat
palabra clave)Una vez más, esto es muy hacky y está lejos de la depuración adecuada, pero es lo que estoy atrapado al no conocer ninguna alternativa adecuada.
fuente
También hay GLSL-Debugger . Es un depurador que solía ser conocido como "GLSL Devil".
El depurador en sí mismo es muy útil no solo para el código GLSL, sino también para OpenGL. Tiene la capacidad de saltar entre llamadas de extracción y romper interruptores de sombreador. También le muestra mensajes de error comunicados por OpenGL a la aplicación misma.
fuente
Hay varias ofertas de proveedores de GPU como CodeXL de AMD o nSight / Linux GFX Debugger de NVIDIA que permiten pasar por sombreadores pero están vinculados al hardware del proveedor respectivo.
Permítanme señalar que, aunque están disponibles en Linux, siempre tuve muy poco éxito al usarlos allí. No puedo comentar sobre la situación en Windows.
La opción que utilicé recientemente es modularizar mi código de sombreador a través de
#includes
y restringir el código incluido a un subconjunto común de GLSL y C ++ y glm .Cuando encuentro un problema, trato de reproducirlo en otro dispositivo para ver si el problema es el mismo, lo que sugiere un error lógico (en lugar de un problema del controlador / comportamiento indefinido). También existe la posibilidad de pasar datos incorrectos a la GPU (p. Ej., Por búferes enlazados incorrectamente, etc.) que generalmente descarto ya sea por depuración de salida como en la respuesta cifz o inspeccionando los datos a través de un apitrace .
Cuando se trata de un error lógico, trato de reconstruir la situación desde la GPU en la CPU llamando al código incluido en la CPU con los mismos datos. Entonces puedo atravesarlo en la CPU.
Sobre la base de la modularidad del código, también puede intentar escribir pruebas unitarias para él y comparar los resultados entre una ejecución de GPU y una ejecución de CPU. Sin embargo, debe tener en cuenta que hay casos de esquina en los que C ++ podría comportarse de manera diferente que GLSL, lo que le da falsos positivos en estas comparaciones.
Finalmente, cuando no puede reproducir el problema en otro dispositivo, solo puede comenzar a excavar de dónde proviene la diferencia. Las pruebas unitarias pueden ayudarlo a reducir dónde sucede eso, pero al final probablemente necesite escribir información de depuración adicional desde el sombreador como en la respuesta cifz .
Y para darle una visión general aquí hay un diagrama de flujo de mi proceso de depuración:
Para completar esto, aquí hay una lista de ventajas y desventajas aleatorias:
Pro
estafa
fuente
Si bien no parece posible pasar a través de un sombreador OpenGL, es posible obtener los resultados de la compilación.
Lo siguiente está tomado de la muestra de cartón de Android .
Si su código se compila correctamente, no tiene más remedio que probar una forma diferente de comunicarle el estado del programa. Puede indicar que se alcanzó una parte del código, por ejemplo, cambiando el color de un vértice o usando una textura diferente. Lo cual es incómodo, pero parece ser la única forma por ahora.
EDITAR: para WebGL, estoy mirando este proyecto , pero lo acabo de encontrar ... no puedo dar fe de ello.
fuente
Esta es una copia y pega de mi respuesta a la misma pregunta en StackOverflow .
Al final de esta respuesta hay un ejemplo de código GLSL que permite generar el
float
valor completo como color, codificando IEEE 754binary32
. Lo uso de la siguiente manera (este fragmento da elyy
componente de la matriz de modelview):Después de obtener esto en la pantalla, puede tomar cualquier selector de color, formatear el color como HTML (agregando
00
elrgb
valor si no necesita mayor precisión y haciendo un segundo pase para obtener el byte inferior si lo necesita), y obtienes la representación hexadecimal defloat
como IEEE 754binary32
.Aquí está la implementación real de
toColor()
:fuente
La solución que funcionó para mí es la compilación de código de sombreador para C ++, como lo mencionó Nadie. Resultó muy eficiente cuando se trabaja en un código complejo a pesar de que requiere un poco de configuración.
He estado trabajando principalmente con HLSL Compute Shaders para los cuales he desarrollado una biblioteca de prueba de concepto disponible aquí:
https://github.com/cezbloch/shaderator
Demuestra en un Compute Shader de DirectX SDK Samples, cómo habilitar C ++ como la depuración HLSL y cómo configurar las Pruebas de Unidad.
La compilación del sombreador de cómputo GLSL para C ++ parece más fácil que HLSL. Principalmente debido a construcciones de sintaxis en HLSL. He agregado un ejemplo trivial de Prueba de unidad ejecutable en un trazador de rayos GLSL Compute Shader que también puede encontrar en las fuentes del proyecto Shaderator en el enlace de arriba.
fuente