GLSL gölgelendiricisinde hata ayıklama nasıl yapılır?


193

Bir GLSL programında hata ayıklamak gerekiyor, ancak ara sonuç çıktısını bilmiyorum. GLSL ile bazı hata ayıklama izleri (printf gibi) yapmak mümkün müdür?


6
... glslDevil gibi harici bir yazılım kullanmadan.
Franck Freiburger

GLSL Fragment gölgelendiricisinin şamandıra değişkenleri ve metinlerinin bu hata ayıklama baskısına bir göz atın, yalnızca yazdırılan alanda yazı tipi ve çıktı değerinin sabit durumu için tek bir yedek doku birimine ihtiyacınız var
Spektre

Yanıtlar:


118

GLSL içinden CPU ile kolayca iletişim kuramazsınız. GlslDevil veya diğer araçları kullanmak en iyi seçenektir.

Bir printf, GLSL kodunu çalıştıran GPU'dan CPU'ya geri dönmeye çalışmayı gerektirir. Bunun yerine, ekrana ilerlemeyi deneyebilirsiniz. Metin çıktısını almak yerine, ekrana görsel olarak farklı bir şey çıktılayın. Örneğin, belirli bir rengi yalnızca kodunuzun printf eklemek istediğiniz noktasına ulaşırsanız boyayabilirsiniz. Bir değeri yazdırmanız gerekiyorsa, rengi bu değere göre ayarlayabilirsiniz.


62
Gölgelendiricinizde hata ayıklamak istediğiniz tam neden, ekranda hiçbir şey görünmemesidir?
Jeroen

11
Neden bir şey hata ayıklamak istesin ki? Çünkü kodu ve o çalışma süresi değerlerini incelemek istiyor çünkü ben tehlike ....
RichieHH

3
GLSL-Debugger , glslDevil'in açık kaynaklı bir çatalıdır.
Magnus

@Magnus artık aktif olarak korunmuyor ve sadece 1.20'ye kadar GLSL'yi destekliyor.
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;
}

8
Bir hata ayıklama cihazıdır. Örneğin, ışık konumunun sahnede nerede olduğunu bilmek istiyorsanız, gidin: if (lpos.x> 100) bug = 1.0. Işık konumu 100'den büyükse, sahne kırmızıya döner.
ste3e

13

Transform Feedback'ın köşe gölgelendiricilerin hata ayıklaması için yararlı bir araç olduğunu gördüm . Rasterizatörden geçmek zorunda kalmadan VS çıkışlarının değerlerini yakalamak ve CPU tarafında tekrar okumak için bunu kullanabilirsiniz.

İşte Transform Feedback ile ilgili bir eğiticiye başka bir link.


8

Ekrandaki bir değerin varyasyonlarını görselleştirmek istiyorsanız, buna benzer bir ısı haritası işlevini kullanabilirsiniz (hlsl'de yazdım, ancak glsl'ye uyum sağlamak kolaydır):

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);
}

Sonra piksel gölgelendiricinizde sadece şöyle bir şey çıkarırsınız:

return HeatMapColor(myValue, 0.00, 50.00);

Ve pikselleriniz arasında nasıl değiştiği hakkında bir fikir edinebilirsiniz:

resim açıklamasını buraya girin

Tabii ki istediğiniz herhangi bir renk setini kullanabilirsiniz.


7

GLSL Sandbox , gölgelendiriciler için benim için oldukça kullanışlı oldu.

Kendi başına hata ayıklama değil (aciz olarak cevaplandı) ama çıkıştaki değişiklikleri hızlı bir şekilde görmek için kullanışlı.


5

Bunu deneyebilirsiniz: https://github.com/msqrt/shader-printf , "GLSL için basit printf işlevselliği" adı verilen bir uygulamadır.

Ayrıca ShaderToy'u denemek ve hata ayıklama için iyi çalışan bazı teknikleri görebileceğiniz "Kod Sanatı" YouTube kanalından böyle bir video (belki de https://youtu.be/EBrAdahFtuo ) izlemek isteyebilirsiniz. görselleştirilmesi. Kanalını gerçekten iyi şeyler yazarken şiddetle tavsiye edebilirim ve aynı zamanda roman, son derece ilgi çekici ve sindirimi kolay formatlarda karmaşık fikirler sunmak için bir ustalığa sahiptir (Mandelbrot videosu tam olarak bunun mükemmel bir örneğidir: https: // youtu.be/6IWXkV82oyY )

Umarım kimse bu geç cevabı akla getirmez, ancak soru Google'ın GLSL hata ayıklama arayışlarında üst sıralardadır ve elbette 9 yıl içinde çok şey değişmiştir :-)

Not: Diğer alternatifler, gölgelendiriciler için tam bir adım ayıklayıcı sunan NVIDIA nSight ve AMD ShaderAnalyzer de olabilir.


2

Ben aslında nasıl hata ayıklama bir fragman gölgelendirici örneği paylaşıyorum.

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

}

resim açıklamasını buraya girin


2

Bu cevabın altında, floatIEEE 754'ü kodlayan , tam değerin renk olarak çıkmasına izin veren bir GLSL kodu örneği verilmiştir binary32. Ben aşağıdaki gibi kullanın (bu snippet yymodelview matris bileşeni verir ):

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

Ekranda bu aldıktan sonra, sadece (ekleme HTML olarak rengini biçimlendirmek, herhangi bir renk seçici alabilir 00için rgbdaha yüksek hassasiyet gerekmez ve bunu yaparsanız ikinci geçiş yapıyor alt byte olsun eğer değeri) ve floatIEEE 754'ün onaltılı gösterimini elde edersiniz binary32.

İşte gerçek uygulama 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

Bir dokuya çevrimdışı oluşturma yapın ve doku verilerini değerlendirin. İlgili kodu, "render to texture" opengl için googling yaparak bulabilirsiniz. Daha sonra çıktıyı bir diziye okumak ve üzerinde iddiayı gerçekleştirmek için glReadPixels kullanın (hata ayıklayıcıda böyle büyük bir diziye bakmak genellikle yararlı değildir).

Ayrıca, yalnızca kayan nokta dokuları için desteklenen 0 ile 1 arasında olmayan çıkış değerlerine sıkıştırmayı devre dışı bırakmak isteyebilirsiniz .

Şahsen bir süre düzgün gölgelendirici hata ayıklama sorunu tarafından rahatsız edildi. İyi bir yol yok gibi görünüyor - Herkes iyi (ve eski / kullanımdan kaldırılmamış) bir hata ayıklayıcı bulursa, lütfen bana bildirin.


3
"Google xyz" yazan herhangi bir yanıt veya yorum Stackoverflow tarafından yasaklanmalı veya aşağı oylanmalıdır.
gregoiregentil

1

Mevcut cevapların hepsi iyi şeyler, ama bir GLSL gölgelendiricideki zor hassas sorunları ayıklamada değerli olan bir küçük taş daha paylaşmak istedim. Kayan nokta olarak temsil edilen çok büyük int sayıları ile, yuvarlak () 'yi kesin bir int değerine uygulamak için zeminin (n) ve zeminin (n + 0.5) doğru kullanılmasına özen gösterilmelidir. Daha sonra bayt bileşenlerini R, G ve B çıkış değerlerine paketlemek için aşağıdaki mantıkla tam bir int olan bir kayan değer oluşturmak mümkündür.

  // 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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.