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?
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.
É um dispositivo de depuração. Se você quiser saber onde está a posição da luz na cena, por exemplo, vá: if (lpos.x> 100) bug = 1.0. Se a posição da luz for maior que 100, a cena ficará vermelha.
ste3e
12
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):
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 whiteelse
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 floatvalor completo como cor, codificando IEEE 754 binary32. Eu o uso da seguinte forma (este trecho fornece o yycomponente 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 00ao rgbvalor 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 floatIEEE 754 binary32.
Aqui está a implementação real de toColor():
constint emax=127;// Input: x>=0// Output: base 2 exponent of x if (x!=0 && !isnan(x) && !isinf(x))// -emax if x==0// emax+1 otherwiseint 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 NaNreturn emax+1;}// Input: any x// Output: IEEE 754 biased exponent with bias=emaxint 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 otherwisefloat significand(float x){// converting int to float so that exp2(genType) gets correctly-typed valuefloat 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 fractionint part(float x,int N){// All comments about exactness here assume that underflow and overflow don't occurconstfloat byteShift=256.;// Multiplication is exact since it's just an increase of exponent by 8for(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 8float c=floor(byteShift*q);returnint(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 7int s = x<0.?128:0;
ivec4 binary32;
binary32.yzw=significandAsIVec3(x);// clear the implicit integer bit of significandif(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.
Qualquer resposta ou comentário que diga "google xyz" deve ser banido ou recusado pelo Stackoverflow.
gregoiregentil
1
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.
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.
fonte
fonte
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.
fonte
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):
Então, no seu pixel shader, você apenas gera algo como:
E pode ter uma idéia de como isso varia entre os pixels:
Claro que você pode usar qualquer conjunto de cores que desejar.
fonte
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.
fonte
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.
fonte
Estou compartilhando um exemplo de shader de fragmento, como eu realmente depuro.
fonte
Na parte inferior desta resposta, há um exemplo de código GLSL que permite gerar o
float
valor completo como cor, codificando IEEE 754binary32
. Eu o uso da seguinte forma (este trecho fornece oyy
componente da matriz modelview):Depois de obtê-lo na tela, você pode escolher qualquer seletor de cores, formatar a cor como HTML (acrescentando
00
aorgb
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 dofloat
IEEE 754binary32
.Aqui está a implementação real de
toColor()
:fonte
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.
fonte
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.
fonte