当调试断言失败时在 GLSL 中抛出运行时错误

Throw runtime error in GLSL when assertion fails for debugging

在函数 distSigned 中,我假设 l.p != vec2(0)。如果不是这种情况,它可能会用一个很难发现的错误来破坏整个着色器。有没有办法验证这一点,如果参数无效则抛出运行时错误?这将使调试变得容易得多。我知道 GLSL 没有像 Java 这样的 throw,但也许有某种 hack?

struct parallel {
    vec2 p;
    vec2 d;
};

struct pt {vec2 v;};

float distSigned(pt p, parallel l) {
    vec2 l2o = fromOrigo(l);
    float d = length(l2o);
    return dot(p.v, l2o/d) - d;
}

编辑:我查看了 GLSL 规范并发现:

“必须为词法或返回编译时错误 语法不正确的着色器。其他错误在编译时或 link 时报告为 表示。

我想这意味着 GLSL 没有运行时错误?如果是这样,也许我可以自己实现异常?是否有某种设计模式?

抛出一个运行时间错误给谁?什么时候扔?将抛出多少次着色器调用,接收代码将如何接收它们?

着色器会在 GPU 到达它时执行,而不是在您 运行 绘制调用时执行。因此,此类错误出现 "thrown" 的时间可能是 "when you issue the draw call" 到任意遥远的未来。那么你将如何接收它们,谁来接收?

此外,您正在执行多个着色器调用;每个顶点和每个片段都有自己的单独调用。因此,每个顶点和光栅化片段都可能 "throw" 某种 运行 时间错误。对于规模和复杂性的场景,可能会出现数百万个此类错误。你会如何处理?

不,如果你想调试着色器,你要么需要专门的工具,要么你需要执行与 printf 调试等效的着色器。也就是说,将您的场景减少到它可以减少到的最小复杂性,然后根据某些错误条件是否以某种方式解决来写入输出值。

正如我在编辑中所说,GLSL 没有运行时错误。但是可以实现。对于类似 try-catch 的东西,可以这样做:

#define assert(array, index, line, condition) if (!(condition)) array[index] = exception(line);

struct exception {int line;};

// In use
float distSigned(pt p, parallel l, out exception[1] errors) {
    assert(errors, 0, __LINE__, l.p != vec2(0));
    // rest of method ..
}

要记录未捕获的错误,可以使用特定的像素颜色,每个像素颜色对应于特定的错误代码,也可以使用隐藏这些错误的像素调色板。当发生致命错误时,会输出它的颜色。 在调试模式下,程序可以查找具有这些颜色的像素。

此方法的问题在于,如果着色器包含在另一个着色器中,__LINE__ 可能会发生偏移,并且在 webGL 中,CPU 无法访问着色器输出。

也许有具有此功能的 GLSL 调试器,我没有尝试过。

感谢 Nicol Bolas 和 LJᛃ 对其中大部分内容的解释。

像素实现错误,Webgl2:

//:ES2_AA2_MAC_DEB: My debug macro, true if positive.
//:        pix_err: PIXel ERRor    ( Stores Hex Error Code )
//:        pix_fra: PIXel FRAgment ( fragment color        )

#if( ES2_AA2_MAC_DEB > 0 ) //:///////////////////////////://

    //:ASSIGN_TO_ZERO_AT_TOP_OF_FUNCTION!!!!!!!!!!!!!!!!!!!!
    uint pix_err =( 0x00000000 ); 

#endif //:///////////////////////////////////////////////://


//: ...Later when you want to error check....


#if( ES2_AA2_MAC_DEB > 0 ) //:///////////////////////////://

    if( Ax1-Ax0+uint(1) != ES2_zoo_wid ||
        Ay1-Ay0+uint(1) != ES2_zoo_hig ){

        //: The first error encountered puts program     ://
        //: into invalid state, so we want to make sure  ://
        //: we never override the first error tripped.   ://
        //: So always guard with comparison to           ://
        //: 0x00000000 before setting to the error color.://
        if( uint(0x00000000) == pix_err ){
            pix_err=( uint(0xFF9000FF) );
        };; 
    };;

#endif //:///////////////////////////////////////////////://


... Do more code.......................................
... DONT TRY TO EXIT EARLY if error is tripped ........
... that will just turn your code into spaghetti ......


#if( ES2_AA2_MAC_DEB > 0 )  //://////////////////////////://

    if( uint(0x000000) != pix_err ){

        #define F_F uint(0xFF)" //://////////////////////://
        pix_fra=( vec4(  //://///////////////////////////:// 
                                                      
         float( ( pix_err >> 24  ) & F_F ) / 255.0    
        ,float( ( pix_err >> 16  ) & F_F ) / 255.0    
        ,float( ( pix_err >>  8  ) & F_F ) / 255.0    
        ,float( ( pix_err >>  0  ) & F_F ) / 255.0    
                                                      
        ));; //://///////////////////////////////////////:// 
        #undef F_F  //://////////////////////////////////://         

    };;

#endif //:///////////////////////////////////////////////://


//: Now in debug mode, you can color sample the pixel    ://
//: and type in the hex code to find the exact error.    ://
//: Might not be helpful for minor edge cases, but       ://
//: should be very helpful for gross mistakes in         ://
//: your programming logic.                              ://
return( pix_fra );