Preciso depurar um programa GLSL, mas não sei como gerar resultado intermediário. É possível fazer alguns rastreamentos de depuração (como no printf) com o GLSL?
Preciso depurar um programa GLSL, mas não sei como gerar resultado intermediário. É possível fazer alguns rastreamentos de depuração (como no printf) com o GLSL?
Respostas:
Você não pode se comunicar facilmente com a CPU de dentro do GLSL. Usar o glslDevil ou outras ferramentas é a sua melhor aposta.
Um printf exigiria tentar retornar à CPU a partir da GPU executando o código GLSL. Em vez disso, você pode tentar avançar para a tela. Em vez de tentar produzir texto, produza algo visualmente distinto na tela. Por exemplo, você pode pintar algo de uma cor específica apenas se atingir o ponto do seu código em que deseja adicionar um printf. Se você precisar imprimir um valor, poderá definir a cor de acordo com esse valor.
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;
}
Eu achei o Transform Feedback uma ferramenta útil para depurar shaders de vértices. Você pode usar isso para capturar os valores das saídas do VS e lê-los novamente no lado da CPU, sem precisar passar pelo rasterizador.
Aqui está outro link para um tutorial sobre o Transform Feedback.
Se você deseja visualizar as variações de um valor na tela, pode usar uma função de mapa de calor semelhante a esta (escrevi em hlsl, mas é fácil adaptar-se ao 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);
}
Então, no seu pixel shader, você apenas gera algo como:
return HeatMapColor(myValue, 0.00, 50.00);
E pode ter uma idéia de como isso varia entre os pixels:
Claro que você pode usar qualquer conjunto de cores que desejar.
O GLSL Sandbox tem sido bastante útil para mim para shaders.
Não é a depuração propriamente dita (que foi respondida como incapaz), mas útil para ver as alterações na saída rapidamente.
Você pode tentar o seguinte: https://github.com/msqrt/shader-printf, que é uma implementação chamada apropriadamente "Funcionalidade simples de impressão para GLSL".
Você também pode experimentar o ShaderToy e assistir a um vídeo como este ( https://youtu.be/EBrAdahFtuo ) no canal do YouTube "The Art of Code", onde você pode ver algumas das técnicas que funcionam bem para depuração e visualizando. Eu recomendo fortemente o canal dele, pois ele escreve algumas coisas realmente boas e ele também tem a habilidade de apresentar idéias complexas em formatos inovadores, altamente envolventes e fáceis de digerir (o vídeo de Mandelbrot é um excelente exemplo disso: https: // youtu.be/6IWXkV82oyY )
Espero que ninguém se importe com essa resposta tardia, mas a pergunta está no alto das pesquisas no Google por depuração do GLSL e, é claro, muita coisa mudou em 9 anos :-)
PS: Outras alternativas também podem ser o NVIDIA nSight e o AMD ShaderAnalyzer, que oferecem um depurador completo para shaders.
Estou compartilhando um exemplo de shader de fragmento, como eu realmente depuro.
#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;
}
Na parte inferior desta resposta, há um exemplo de código GLSL que permite gerar o float
valor completo como cor, codificando IEEE 754 binary32
. Eu o uso da seguinte forma (este trecho fornece o yy
componente da matriz 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);
Depois de obtê-lo na tela, você pode escolher qualquer seletor de cores, formatar a cor como HTML (acrescentando 00
ao rgb
valor se você não precisar de maior precisão e fazendo uma segunda passagem para obter o byte mais baixo, se necessário), e você obtém a representação hexadecimal do float
IEEE 754 binary32
.
Aqui está a implementação real 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.;
}
Faça a renderização offline de uma textura e avalie os dados da textura. Você pode encontrar código relacionado pesquisando no opengl "renderizar textura". Em seguida, use o glReadPixels para ler a saída em uma matriz e executar asserções (uma vez que analisar uma matriz tão grande no depurador geralmente não é realmente útil).
Além disso, convém desativar a fixação para valores de saída que não estão entre 0 e 1, que são suportados apenas para texturas de ponto flutuante .
Pessoalmente, fiquei incomodado com o problema de depurar shaders corretamente por um tempo. Não parece ser uma boa maneira - Se alguém encontrar um bom depurador (e não desatualizado / obsoleto), informe-me.
As respostas existentes são boas, mas eu queria compartilhar mais uma pequena gema que foi valiosa na depuração de problemas complicados de precisão em um shader GLSL. Com números int muito grandes representados como um ponto flutuante, é necessário ter o cuidado de usar o piso (n) e o piso (n + 0,5) adequadamente para implementar round () com um int exato. É então possível renderizar um valor flutuante que é um int exato pela lógica a seguir para compactar os componentes de bytes nos valores de saída R, G e 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;