Jak debugować shader GLSL?

Muszę debugować program GLSL, ale nie wiem, jak uzyskać pośredni wynik. Czy jest możliwe, aby niektóre ślady debugowania (jak z printf) z GLSL ?

Author: Franck Freiburger, 2010-03-24

9 answers

Nie można łatwo komunikować się z powrotem do procesora z poziomu GLSL. Korzystanie z glslDevil lub innych narzędzi jest najlepszym rozwiązaniem.

Printf wymagałby próby powrotu do CPU z GPU z kodem GLSL. Zamiast tego możesz spróbować popchnąć do przodu do wyświetlacza. Zamiast próbować wypisywać tekst, wypisz coś wizualnie wyróżniającego się na ekranie. Na przykład możesz namalować coś określonego koloru tylko wtedy, gdy osiągniesz punkt kodu, w którym chcesz dodać printf. Jeśli potrzebujesz aby wydrukować wartość, możesz ustawić kolor zgodnie z tą wartością.

 94
Author: Mr. Berna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-24 17:14:44
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;
}
 52
Author: ste3e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-13 08:59:53

Znalazłem Transform Feedback jako przydatne narzędzie do debugowania shaderów wierzchołków. Możesz użyć tego do przechwycenia wartości wyjść VS i odczytania ich po stronie procesora, bez konieczności przechodzenia przez rasteryzator.

Tutaj {[2] } znajduje się kolejny link do samouczka na temat sprzężenia zwrotnego z transformacją.

 12
Author: nullspace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-23 21:11:59

Jeśli chcesz wizualizować zmiany wartości na ekranie, możesz użyć funkcji heatmap podobnej do tej (napisałem ją w hlsl, ale łatwo jest dostosować do 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);
}

Następnie w Twoim pixel shader wypisujesz coś w stylu:

return HeatMapColor(myValue, 0.00, 50.00);

I może zorientować się, jak to zmienia się na Twoich pikselach:

Tutaj wpisz opis obrazka

Oczywiście możesz użyć dowolnego zestawu kolorów, które lubisz.

 8
Author: wil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-23 07:13:24

Piaskownica GLSL była mi bardzo przydatna dla shaderów.

Nie debugowanie per se (co zostało odebrane jako niezdolne), ale przydatne, aby szybko zobaczyć zmiany w wyjściu.

 5
Author: Nick Devereaux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 01:28:02

Renderuj teksturę w trybie offline i oceń dane tekstury. Możesz znaleźć pokrewny kod, wyszukując w Google" render to texture " opengl Następnie użyj glReadPixels, aby odczytać wyjście do tablicy i wykonać na niej asercje (ponieważ przeglądanie tak ogromnej tablicy w debuggerze zwykle nie jest zbyt użyteczne).

Możesz również wyłączyć mocowanie do wartości wyjściowych, które nie mieszczą się w przedziale od 0 do 1, co jest obsługiwane tylko dla tekstur zmiennoprzecinkowych.

I osobiście przez jakiś czas niepokoił mnie problem prawidłowego debugowania shaderów. Wydaje się, że nie ma dobrego sposobu-jeśli ktoś znajdzie dobry (a nie przestarzały/przestarzały) debugger, proszę dać mi znać.

 2
Author: Domi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-01 16:45:03

Dzielę się fragmentem shader przykład, jak faktycznie debugować.

#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;

}

Tutaj wpisz opis obrazka

 2
Author: user1767754,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-15 09:11:58

Istniejące odpowiedzi są dobre, ale chciałem podzielić się jeszcze jednym małym klejnotem, który był cenny w debugowaniu trudnych problemów z precyzją w shaderze GLSL. Z bardzo dużymi liczbami int reprezentowanymi jako zmiennoprzecinkowe, należy zadbać o właściwe użycie floor(n) I floor(n + 0.5), aby zaimplementować round() do dokładnej int. Następnie można renderować wartość zmiennoprzecinkową, która jest dokładną wartością int, za pomocą poniższej logiki, aby spakować składniki bajtu do wartości wyjściowych R, G I 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;
 1
Author: MoDJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 18:33:21

Na dole tej odpowiedzi znajduje się przykład kodu GLSL, który pozwala wypisać pełną wartość float jako kolor, kodując IEEE 754 binary32. Używam go w następujący sposób (ten fragment podaje yy Składnik macierzy 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);

Po wyświetleniu tego na ekranie, możesz po prostu wybrać dowolny próbnik kolorów, sformatować Kolor jako HTML (dodając {[5] } do wartości rgb, Jeśli nie potrzebujesz większej precyzji, i wykonując drugi przebieg, aby uzyskać niższy bajt, jeśli to zrobisz), i otrzymasz szesnastkowy kod reprezentacja float jako IEEE 754 binary32.

Oto faktyczna implementacja 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.;
}
 1
Author: Ruslan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 19:24:53