Comment déboguer un shader GLSL?

193

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?

Franck Freiburger
la source
6
... sans utiliser de logiciel externe comme glslDevil.
Franck Freiburger
jetez un oeil à cette impression de débogage des variables flottantes et des textes de GLSL Fragment shader vous avez juste besoin d'une seule unité de texture de rechange pour la police et l'état constant de la valeur sortie dans la zone imprimée
Spektre

Réponses:

118

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.

M. Berna
la source
62
Que faire si la raison exacte pour laquelle vous souhaitez déboguer votre shader est que rien n'apparaît à l'écran?
Jeroen
11
Pourquoi voudriez-vous déboguer quoi que ce soit? Parce que son code et qu'il veut examiner les valeurs d'exécution, je risquerais ....
RichieHH
3
GLSL-Debugger est un fork open source de glslDevil.
Magnus
@Magnus il n'est plus activement maintenu et ne prend en charge que GLSL jusqu'à 1.20.
Ruslan
57
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;
}
ste3e
la source
8
C'est un périphérique de débogage. Si vous voulez savoir où se trouve la position de la lumière dans la scène, par exemple, allez: if (lpos.x> 100) bug = 1.0. Si la position de la lumière est supérieure à 100, la scène deviendra rouge.
ste3e
13

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.

nullspace
la source
8

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:

entrez la description de l'image ici

Bien sûr, vous pouvez utiliser n'importe quel jeu de couleurs que vous aimez.

essuyer
la source
7

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.

Nick Devereaux
la source
5

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.

Ian Macintosh
la source
2

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;

}

entrez la description de l'image ici

user1767754
la source
2

Au bas de cette réponse se trouve un exemple de code GLSL qui permet de sortir la floatvaleur complète en couleur, en codant IEEE 754 binary32. Je l'utilise comme suit (cet extrait donne un yycomposant 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 rgbvaleur 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 floatcomme 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.;
}
Ruslan
la source
1

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.

Domi
la source
3
Toute réponse ou tout commentaire indiquant "google xyz" doit être banni ou rejeté de Stackoverflow.
gregoiregentil
1

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;
MoDJ
la source