Làm cách nào để gỡ lỗi trình đổ bóng GLSL?


193

Tôi cần gỡ lỗi chương trình GLSL nhưng tôi không biết cách tạo kết quả trung gian. Có thể tạo một số dấu vết gỡ lỗi (như với printf) với GLSL không?


6
... mà không sử dụng phần mềm bên ngoài như glslDevil.
Franck Freiburger

hãy xem bản in gỡ lỗi này của các biến và văn bản nổi từ trình tạo bóng mảnh vỡ GLSL, bạn chỉ cần một đơn vị kết cấu dự phòng duy nhất cho phông chữ và trạng thái không đổi của giá trị đầu ra trong vùng in
Spektre

Câu trả lời:


117

Bạn không thể dễ dàng liên lạc lại với CPU từ bên trong GLSL. Sử dụng glslDevil hoặc các công cụ khác là cách tốt nhất của bạn.

Một printf sẽ yêu cầu cố gắng lấy lại CPU từ GPU chạy mã GLSL. Thay vào đó, bạn có thể thử đẩy về phía trước màn hình. Thay vì cố gắng xuất văn bản, hãy xuất một cái gì đó đặc biệt trực quan lên màn hình. Ví dụ, bạn có thể vẽ một cái gì đó một màu cụ thể chỉ khi bạn đạt đến điểm mã của bạn, nơi bạn muốn thêm một printf. Nếu bạn cần in một giá trị, bạn có thể đặt màu theo giá trị đó.


61
Điều gì xảy ra nếu lý do chính xác mà bạn muốn gỡ lỗi shader của bạn là vì không có gì xuất hiện trên màn hình?
Jeroen

11
Tại sao bạn muốn gỡ lỗi bất cứ điều gì? Bởi vì mã của nó và anh ta muốn kiểm tra giá trị thời gian chạy, tôi sẽ gặp nguy hiểm ....
RichieHH

3
GLSL-Debugger là một ngã ba mã nguồn mở của glslDevil.
Magnus

@Magnus nó không còn được duy trì tích cực và chỉ hỗ trợ GLSL tối đa 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;
}

8
Nó là một thiết bị gỡ lỗi. Ví dụ, nếu bạn muốn biết vị trí ánh sáng trong cảnh, hãy đi: if (lpose.x> 100) bug = 1.0. Nếu vị trí ánh sáng lớn hơn 100, cảnh sẽ chuyển sang màu đỏ.
ste3e

12

Tôi đã tìm thấy Transform Feedback là một công cụ hữu ích để gỡ lỗi các shader đỉnh. Bạn có thể sử dụng điều này để nắm bắt các giá trị của đầu ra VS và đọc lại chúng ở phía CPU mà không cần phải đi qua trình rasterizer.

Đây là một liên kết đến một hướng dẫn về Transform Feedback.


8

Nếu bạn muốn trực quan hóa các biến thể của một giá trị trên màn hình, bạn có thể sử dụng chức năng bản đồ nhiệt tương tự như thế này (tôi đã viết nó trong hlsl, nhưng nó rất dễ thích ứng với 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);
}

Sau đó, trong pixel shader của bạn, bạn chỉ cần xuất ra một cái gì đó như:

return HeatMapColor(myValue, 0.00, 50.00);

Và có thể có ý tưởng về cách nó thay đổi trên các pixel của bạn:

nhập mô tả hình ảnh ở đây

Tất nhiên bạn có thể sử dụng bất kỳ bộ màu nào bạn thích.


6

GLSL Sandbox đã khá tiện dụng đối với tôi đối với các shader.

Không gỡ lỗi trên mỗi se (đã được trả lời là không có khả năng) nhưng tiện dụng để xem các thay đổi trong đầu ra một cách nhanh chóng.


4

Bạn có thể thử điều này: https://github.com/msqrt/shader-printf , đây là một triển khai được gọi một cách thích hợp là "Chức năng printf đơn giản cho GLSL."

Bạn cũng có thể muốn dùng thử ShaderToy và có thể xem video như thế này ( https://youtu.be/EBrAdahFtuo ) từ kênh YouTube "The Art of Code" nơi bạn có thể xem một số kỹ thuật hoạt động tốt để gỡ lỗi và hình dung. Tôi hoàn toàn có thể giới thiệu kênh của anh ấy khi anh ấy viết một số nội dung thực sự hay và anh ấy cũng có sở trường trình bày các ý tưởng phức tạp trong các định dạng mới lạ, hấp dẫn và dễ tiêu hóa (video Mandelbrot của anh ấy là một ví dụ tuyệt vời về chính xác: https: // youtu.be / 6IWXkV82oyY )

Tôi hy vọng không ai bận tâm đến câu trả lời muộn này, nhưng câu hỏi xếp hạng cao trên các tìm kiếm của Google về gỡ lỗi GLSL và tất nhiên đã có nhiều thay đổi trong 9 năm :-)

PS: Các lựa chọn thay thế khác cũng có thể là NVIDIA nSight và AMD ShaderAnalyzer cung cấp trình gỡ lỗi đầy đủ cho các shader.


2

Tôi đang chia sẻ một ví dụ shader mảnh, cách tôi thực sự gỡ lỗi.

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

}

nhập mô tả hình ảnh ở đây


2

Ở dưới cùng của câu trả lời này là một ví dụ về mã GLSL cho phép xuất toàn bộ floatgiá trị dưới dạng màu, mã hóa IEEE 754 binary32. Tôi sử dụng nó như sau (đoạn trích này đưa ra yythành phần của ma trận 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);

Sau khi bạn có được điều này trên màn hình, bạn chỉ có thể lấy bất kỳ chọn màu, định dạng màu dưới dạng HTML (phụ thêm 00vào rgbgiá trị nếu bạn không cần độ chính xác cao hơn, và thực hiện một đường chuyền thứ hai để có được byte thấp hơn nếu bạn làm), và bạn có được đại diện thập lục phân của floatIEEE 754 binary32.

Đây là triển khai thực tế của 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

Thực hiện kết xuất ngoại tuyến thành một kết cấu và đánh giá dữ liệu của kết cấu. Bạn có thể tìm mã liên quan bằng cách googling cho "render to texture" opengl Sau đó sử dụng glReadPixels để đọc đầu ra thành một mảng và thực hiện các xác nhận trên nó (vì việc xem qua một mảng lớn như vậy trong trình gỡ lỗi thường không thực sự hữu ích).

Ngoài ra, bạn có thể muốn vô hiệu hóa kẹp vào các giá trị đầu ra không nằm trong khoảng từ 0 đến 1, chỉ được hỗ trợ cho kết cấu dấu phẩy động .

Cá nhân tôi đã bị làm phiền bởi vấn đề gỡ lỗi shader đúng cách trong một thời gian. Dường như không có một cách tốt - Nếu bất cứ ai tìm thấy một trình gỡ lỗi tốt (và không lỗi thời / không dùng nữa), vui lòng cho tôi biết.


3
Bất kỳ câu trả lời hoặc nhận xét nào cho biết "google xyz" nên bị cấm hoặc bỏ phiếu từ Stackoverflow.
gregoiregentil

1

Các câu trả lời hiện có đều là những thứ tốt, nhưng tôi muốn chia sẻ thêm một viên ngọc nhỏ có giá trị trong việc gỡ lỗi các vấn đề chính xác khó hiểu trong trình tạo bóng GLSL. Với số int rất lớn được biểu diễn dưới dạng dấu phẩy động, người ta cần lưu ý sử dụng sàn (n) và sàn (n + 0,5) đúng cách để thực hiện vòng () cho một int chính xác. Sau đó, có thể kết xuất một giá trị float là một int chính xác theo logic sau để đóng gói các thành phần byte thành các giá trị đầu ra R, G và 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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.