为半浮优化 GLSL

Optimize GLSL for half float

在阅读 Physically Based Rendering in Filament I found a few interesting paragraphs in section 4.4.1 about optimizing the implementation of a GGX NDF approximation for half precision floats. I understood that the calculation of 1 - dot(n, h) * dot(n,h) can cause so called catastrophic cancellation 以及为什么使用叉积可以解决问题时,但是我不明白这与半精度有何关系 floats。

It seems that GLSL does not have any half specifier, unlike HLSL (which simply maps it to float since D3D10 most likely because modern desktop hardware doesn't support it anyway; though it seems that with the newest hardware its back again)。 Filament 的问题在于它主要是为 Android 等移动平台开发的,其中硬件支持半精度 floats。

我知道使用半精度 floats 对于移动和最现代的桌面目标的性能很重要。因此,我想了解以下代码是如何针对半精度 floats 进行优化的,因为我看不到 half 说明符或类似说明符,而只有一些常量和一个宏:

#define MEDIUMP_FLT_MAX    65504.0
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)

float D_GGX(float roughness, float NoH, const vec3 n, const vec3 h) {
    vec3 NxH = cross(n, h);
    float a = NoH * roughness;
    float k = roughness / (dot(NxH, NxH) + a * a);
    float d = k * k * (1.0 / PI);
    return saturateMediump(d);
}

为了完整起见,这里是未优化的代码:

float D_GGX(float NoH, float roughness) {
    float a = NoH * roughness;
    float k = roughness / (1.0 - NoH * NoH + a * a);
    return k * k * (1.0 / PI);
}

虽然 GLSL 没有 half 类型,但它确实有 precision qualifiers 其效果是专有且依赖于移动平台的。我假设您的示例中的(完整)优化着色器代码包含默认限定符设置浮动到 mediump,如下所示: precision mediump float; 请注意,尽管实际精度仍未指定,但 mediump 浮点数在一个平台上可能有 16 位,而在另一个平台上有 24 位。

这里有问题:如链接文章和 GLSL specification precision qualifiers are only supported for portability and ought to have no effect on desktop platforms. That means that even desktop GPUs with float16 support would break with the specification if they honored the precision qualifier. On desktop platforms you'll have to enable and use the appropriate extension(e.g. GL_AMD_gpu_shader_half_float 中所述)及其特定语法(例如类型)以利用 float16 功能。