J'ai besoin de déboguer un programme GLSL mais je ne sais pas comment produire un résultat intermédiaire. Est-il possible de faire des traces de débogage (comme avec printf) avec GLSL?
J'ai besoin de déboguer un programme GLSL mais je ne sais pas comment produire un résultat intermédiaire. Est-il possible de faire des traces de débogage (comme avec printf) avec GLSL?
Réponses:
Vous ne pouvez pas facilement communiquer avec le CPU depuis GLSL. L'utilisation de glslDevil ou d'autres outils est votre meilleur choix.
Un printf nécessiterait d'essayer de revenir au CPU à partir du GPU exécutant le code GLSL. Au lieu de cela, vous pouvez essayer d'aller de l'avant à l'écran. Au lieu d'essayer de produire du texte, affichez quelque chose de visuellement distinctif à l'écran. Par exemple, vous ne pouvez peindre quelque chose d'une couleur spécifique que si vous atteignez le point de votre code où vous souhaitez ajouter un printf. Si vous devez imprimer une valeur, vous pouvez définir la couleur en fonction de cette valeur.
void main(){
float bug=0.0;
vec3 tile=texture2D(colMap, coords.st).xyz;
vec4 col=vec4(tile, 1.0);
if(something) bug=1.0;
col.x+=bug;
gl_FragColor=col;
}
J'ai trouvé que Transform Feedback était un outil utile pour le débogage des vertex shaders. Vous pouvez l'utiliser pour capturer les valeurs des sorties VS et les relire côté CPU, sans avoir à passer par le rastériseur.
Voici un autre lien vers un didacticiel sur Transform Feedback.
Si vous souhaitez visualiser les variations d'une valeur sur l'écran, vous pouvez utiliser une fonction de heatmap similaire à celle-ci (je l'ai écrite en hlsl, mais il est facile de l'adapter à glsl):
float4 HeatMapColor(float value, float minValue, float maxValue)
{
#define HEATMAP_COLORS_COUNT 6
float4 colors[HEATMAP_COLORS_COUNT] =
{
float4(0.32, 0.00, 0.32, 1.00),
float4(0.00, 0.00, 1.00, 1.00),
float4(0.00, 1.00, 0.00, 1.00),
float4(1.00, 1.00, 0.00, 1.00),
float4(1.00, 0.60, 0.00, 1.00),
float4(1.00, 0.00, 0.00, 1.00),
};
float ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
float indexMin=floor(ratio);
float indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
}
Ensuite, dans votre pixel shader, vous sortez simplement quelque chose comme:
return HeatMapColor(myValue, 0.00, 50.00);
Et pouvez avoir une idée de la façon dont cela varie à travers vos pixels:
Bien sûr, vous pouvez utiliser n'importe quel jeu de couleurs que vous aimez.
GLSL Sandbox m'a été très utile pour les shaders.
Pas de débogage en soi (qui a été répondu comme incapable) mais pratique pour voir rapidement les changements de sortie.
Vous pouvez essayer ceci: https://github.com/msqrt/shader-printf qui est une implémentation appelée à juste titre «Fonctionnalité printf simple pour GLSL».
Vous pouvez également essayer ShaderToy, et peut-être regarder une vidéo comme celle-ci ( https://youtu.be/EBrAdahFtuo ) de la chaîne YouTube "The Art of Code" où vous pouvez voir certaines des techniques qui fonctionnent bien pour le débogage et visualisation. Je peux fortement recommander sa chaîne car il écrit de très bonnes choses et il a également le don de présenter des idées complexes dans des formats novateurs, très attrayants et faciles à digérer (sa vidéo de Mandelbrot en est un superbe exemple: https: // youtu.be/6IWXkV82oyY )
J'espère que personne ne se soucie de cette réponse tardive, mais la question occupe une place importante dans les recherches de Google pour le débogage GLSL et beaucoup de choses ont bien sûr changé en 9 ans :-)
PS: D'autres alternatives pourraient également être NVIDIA nSight et AMD ShaderAnalyzer qui offrent un débogueur complet pour les shaders.
Je partage un exemple de fragment shader, comment je débogue réellement.
#version 410 core
uniform sampler2D samp;
in VS_OUT
{
vec4 color;
vec2 texcoord;
} fs_in;
out vec4 color;
void main(void)
{
vec4 sampColor;
if( texture2D(samp, fs_in.texcoord).x > 0.8f) //Check if Color contains red
sampColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); //If yes, set it to white
else
sampColor = texture2D(samp, fs_in.texcoord); //else sample from original
color = sampColor;
}
Au bas de cette réponse se trouve un exemple de code GLSL qui permet de sortir la float
valeur complète en couleur, en codant IEEE 754 binary32
. Je l'utilise comme suit (cet extrait donne un yy
composant de la matrice modelview):
vec4 xAsColor=toColor(gl_ModelViewMatrix[1][1]);
if(bool(1)) // put 0 here to get lowest byte instead of three highest
gl_FrontColor=vec4(xAsColor.rgb,1);
else
gl_FrontColor=vec4(xAsColor.a,0,0,1);
Une fois que vous l'avez affiché à l'écran, vous pouvez simplement prendre n'importe quel sélecteur de couleur, formater la couleur au format HTML (en ajoutant 00
à la rgb
valeur si vous n'avez pas besoin d'une précision plus élevée, et en effectuant une deuxième passe pour obtenir l'octet inférieur si vous le faites), et vous obtenez la représentation hexadécimale du float
comme IEEE 754 binary32
.
Voici la mise en œuvre réelle de toColor()
:
const int emax=127;
// Input: x>=0
// Output: base 2 exponent of x if (x!=0 && !isnan(x) && !isinf(x))
// -emax if x==0
// emax+1 otherwise
int floorLog2(float x)
{
if(x==0.) return -emax;
// NOTE: there exist values of x, for which floor(log2(x)) will give wrong
// (off by one) result as compared to the one calculated with infinite precision.
// Thus we do it in a brute-force way.
for(int e=emax;e>=1-emax;--e)
if(x>=exp2(float(e))) return e;
// If we are here, x must be infinity or NaN
return emax+1;
}
// Input: any x
// Output: IEEE 754 biased exponent with bias=emax
int biasedExp(float x) { return emax+floorLog2(abs(x)); }
// Input: any x such that (!isnan(x) && !isinf(x))
// Output: significand AKA mantissa of x if !isnan(x) && !isinf(x)
// undefined otherwise
float significand(float x)
{
// converting int to float so that exp2(genType) gets correctly-typed value
float expo=float(floorLog2(abs(x)));
return abs(x)/exp2(expo);
}
// Input: x\in[0,1)
// N>=0
// Output: Nth byte as counted from the highest byte in the fraction
int part(float x,int N)
{
// All comments about exactness here assume that underflow and overflow don't occur
const float byteShift=256.;
// Multiplication is exact since it's just an increase of exponent by 8
for(int n=0;n<N;++n)
x*=byteShift;
// Cut higher bits away.
// $q \in [0,1) \cap \mathbb Q'.$
float q=fract(x);
// Shift and cut lower bits away. Cutting lower bits prevents potentially unexpected
// results of rounding by the GPU later in the pipeline when transforming to TrueColor
// the resulting subpixel value.
// $c \in [0,255] \cap \mathbb Z.$
// Multiplication is exact since it's just and increase of exponent by 8
float c=floor(byteShift*q);
return int(c);
}
// Input: any x acceptable to significand()
// Output: significand of x split to (8,8,8)-bit data vector
ivec3 significandAsIVec3(float x)
{
ivec3 result;
float sig=significand(x)/2.; // shift all bits to fractional part
result.x=part(sig,0);
result.y=part(sig,1);
result.z=part(sig,2);
return result;
}
// Input: any x such that !isnan(x)
// Output: IEEE 754 defined binary32 number, packed as ivec4(byte3,byte2,byte1,byte0)
ivec4 packIEEE754binary32(float x)
{
int e = biasedExp(x);
// sign to bit 7
int s = x<0. ? 128 : 0;
ivec4 binary32;
binary32.yzw=significandAsIVec3(x);
// clear the implicit integer bit of significand
if(binary32.y>=128) binary32.y-=128;
// put lowest bit of exponent into its position, replacing just cleared integer bit
binary32.y+=128*int(mod(float(e),2.));
// prepare high bits of exponent for fitting into their positions
e/=2;
// pack highest byte
binary32.x=e+s;
return binary32;
}
vec4 toColor(float x)
{
ivec4 binary32=packIEEE754binary32(x);
// Transform color components to [0,1] range.
// Division is inexact, but works reliably for all integers from 0 to 255 if
// the transformation to TrueColor by GPU uses rounding to nearest or upwards.
// The result will be multiplied by 255 back when transformed
// to TrueColor subpixel value by OpenGL.
return vec4(binary32)/255.;
}
Effectuez un rendu hors ligne sur une texture et évaluez les données de la texture. Vous pouvez trouver le code associé en recherchant sur Google "render to texture" opengl Ensuite, utilisez glReadPixels pour lire la sortie dans un tableau et effectuer des assertions dessus (car regarder à travers un si grand tableau dans le débogueur n'est généralement pas vraiment utile).
Vous pouvez également désactiver le verrouillage pour afficher des valeurs qui ne sont pas comprises entre 0 et 1, ce qui n'est pris en charge que pour les textures à virgule flottante .
J'ai personnellement été gêné par le problème du débogage correct des shaders pendant un certain temps. Il ne semble pas y avoir de bon moyen - Si quelqu'un trouve un bon débogueur (et non obsolète / obsolète), veuillez me le faire savoir.
Les réponses existantes sont toutes bonnes, mais je voulais partager un autre petit bijou qui a été précieux dans le débogage de problèmes de précision délicats dans un shader GLSL. Avec de très grands nombres int représentés sous forme de virgule flottante, il faut prendre soin d'utiliser correctement floor (n) et floor (n + 0.5) pour implémenter round () à un int exact. Il est alors possible de restituer une valeur flottante qui est un entier exact par la logique suivante pour regrouper les composants d'octets en valeurs de sortie R, V et B.
// Break components out of 24 bit float with rounded int value
// scaledWOB = (offset >> 8) & 0xFFFF
float scaledWOB = floor(offset / 256.0);
// c2 = (scaledWOB >> 8) & 0xFF
float c2 = floor(scaledWOB / 256.0);
// c0 = offset - (scaledWOB << 8)
float c0 = offset - floor(scaledWOB * 256.0);
// c1 = scaledWOB - (c2 << 8)
float c1 = scaledWOB - floor(c2 * 256.0);
// Normalize to byte range
vec4 pix;
pix.r = c0 / 255.0;
pix.g = c1 / 255.0;
pix.b = c2 / 255.0;
pix.a = 1.0;
gl_FragColor = pix;