OpenGL/GLSL 运行时错误处理

OpenGL/GLSL Runtime Error Handling

这是一个一般性问题。我没有在网上找到任何关于此的资源,但我认为值得在某处进行记录。

Shader Runtime(不是编译错误)究竟是如何处理的,比如是否试图访问当前值为 Null 的制服?

线程是否正在中止?会产生垃圾值吗?是否依赖于驱动程序/硬件?

对于 "normal" 统一变量,我看不出它们会导致运行时错误的任何可能性。由于它们都是原始类型,因此不可能有像 NULL 这样的值。所有这些变量都在 link 时间初始化。规范告诉:

2.15.3.1

All uniform variables are read-only and are initialized externally either at link time or through the API. The link-time initial value is either the value of the variable's initializer, if present, or 0 if no initializer is present.

但是这里还有两种其他类型的制服可能会引起您的兴趣:

采样器制服

采样器也被初始化为零,这意味着它们指向纹理 0。根据 OpenGL 规范:

8.1

The name space for texture objects is the unsigned integers, with zero reserved by the GL to represent the default texture object. The default texture object is bound to each of the TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_1D_ARRAY, TEXTURE_- 2D_ARRAY, TEXTURE_RECTANGLE, TEXTURE_BUFFER, TEXTURE_CUBE_MAP, TEXTURE_CUBE_MAP_ARRAY, TEXTURE_2D_MULTISAMPLE, and TEXTURE_- 2D_MULTISAMPLE_ARRAY targets during context initialization.

这基本上意味着,这里也不可能产生任何运行时异常。答案 here 表示从纹理 0 读取时会发生什么。

统一块

意思是由 UBO 或 SSBO 存储支持的制服。这似乎是根据:

If any active uniform block is not backed by a sufficiently large buffer object, the results of shader execution are undefined, and may result in GL interruption or termination.

依赖于实现。

结论

在着色器中获得运行时异常的唯一方法似乎是当一个统一块试图访问一个不受缓冲区支持的变量时。据我所知,在着色器内部无法处理这种情况。

GLSL 4.5 Specification

OpenGL 4.5 Specification

GLSL 没有指针,所以 uniform 不能为 NULL。每个 uniform 都有一个值。

如果着色器尝试从没有绑定到相应绑定点的对象的缓冲区(统一、SSBO、原子计数器)读取,那么……好吧,规范对此有点不清楚。 4.5 规范说:

When executing shaders that access uniform blocks, the binding point corresponding to each active uniform block must be populated with a buffer object ...

违反 "must" 时会发生什么尚不清楚。它可能只是 "undefined behavior"。可能是 GL_INVALID_OPERATION 错误。

该段的后半部分说:

If any active uniform block is not backed by a sufficiently large buffer object, the results of shader execution may be undefined or modified, as described in section 6.4.

SSBO 和原子计数器说的类似,也参考第 6.4 节。但尚不清楚不绑定任何东西与不绑定 "sufficiently large buffer object" 是一样的。

第 6.4 节提到 任何东西 可以访问缓冲区对象的边界范围之外。它说:

Any command which does not detect these attempts, and performs such an invalid read or write, has undefined results, and may result in GL interruption or termination.

Shader 处理是其中似乎没有的事情 "detect these attempts"。所以我们必须假设至少有可能遇到可怕的 "GPU reset".

但是,如果您打开稳健性,那么这一切都会消失。您可以保证任何此类访问只会 return 未定义的值,并且它明确表示:

will not result in GL interruption or termination.

太好了。

对于采样器,情况有所不同。您不能真正解除绑定纹理;你只绑定纹理对象 0。纹理对象 0 是一个功能纹理对象(从技术上讲,它是 many 功能纹理)。所以你的着色器可以尝试从中读取。

但是,如果您从未在纹理对象 0 中分配图像,那么它们也是 不完整 纹理。规范说:

If a sampler is used in a shader and the sampler’s associated texture is not complete, as defined in section 8.17, (0.0,0.0,0.0,1.0), in floating-point, will be returned for a non-shadow sampler and 0 for a shadow sampler. In this case, if the sampler is declared in the shader as a signed or unsigned integer sampler type, undefined values are returned as specified in section 9.9(“Texture Functions”) of the OpenGL Shading Language Specification when the texture format and sampler type are unsupported combinations.

所以你得到了明确定义的浮点采样器值,但未定义整数采样器的值。此外,采样器查询函数(查询 mipmap 的大小或数量)return 未定义值,如 GLSL 中所述。

但是其中 none 说程序终止是可能的,所以 不是 可能。所以你安全了。

Images 有点复杂。图片没有"image object 0";真的 "not an object".

然而,GLSL 4.50 说:

If the image target type does not match the bound image in this manner, if the data type does not match the bound image, or if the format layout qualifier does not match the image unit format as described in section 8.25 “Texture Image Loads and Stores” of the OpenGL Specification, the results of image accesses are undefined but cannot include program termination.

我们必须假定 "not an image" 与图像的数据类型不匹配,因此虽然您会得到未定义的值,但您 不会 程序终止。

着色器可以做的唯一可以被视为硬错误的事情是无限循环。这是以最简单的方式检测到的:每个着色器预计执行不超过 X 条指令(其中 X 由硬件决定)。如果它尝试执行超过X次,则认为进入了无限循环,因此GPU被重置。

另一个可能会杀死你的 GPU 的东西是:

int i[5] = ...;

for( int j = 0; j < 10; ++j)
    i[j] = 10;

它是否存在取决于实现。这还包括动态访问向量或矩阵的元素。请注意,稳健性再次确保这些访问不会使 GPU(或 read/write 其他内存)崩溃。

以下是一些会导致未定义值但不会炸毁 GPU 的着色器进程:

  • Array Texture 访问,如果您提供的层索引大于纹理中的层数。

  • 在计算中使用浮点 Inf 或 NaN。