Metal: re-enable fastmath (#8911)

This commit is contained in:
Ben Doherty
2025-07-23 13:46:43 -07:00
committed by GitHub
parent 04f7837439
commit 1f8248ba99
3 changed files with 30 additions and 28 deletions

View File

@@ -113,13 +113,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::MSL: {
// By default, Metal uses the most recent language version.
MTLCompileOptions* options = [MTLCompileOptions new];
// Disable Fast Math optimizations.
// This ensures that operations adhere to IEEE standards for floating-point
// arithmetic, which is crucial for half precision floats in scenarios where fast
// math optimizations lead to inaccuracies, such as in handling special values like
// NaN or Infinity.
options.fastMathEnabled = NO;
options.fastMathEnabled = YES;
assert_invariant(source[source.size() - 1] == '\0');
// the shader string is null terminated and the length includes the null character

View File

@@ -3,22 +3,26 @@
//------------------------------------------------------------------------------
/** @public-api */
#define PI 3.14159265359
#define PI 3.14159265359
/** @public-api */
#define HALF_PI 1.570796327
#define HALF_PI 1.570796327
#define MEDIUMP_FLT_MAX 65504.0
#define MEDIUMP_FLT_MIN 0.00006103515625
#define MEDIUMP_FLT_MAX 65504.0
#define MEDIUMP_FLT_MIN 0.00006103515625
#ifdef TARGET_MOBILE
#define FLT_EPS MEDIUMP_FLT_MIN
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)
#define FLT_EPS MEDIUMP_FLT_MIN
#else
#define FLT_EPS 1e-5
#define saturateMediump(x) x
#define FLT_EPS 1e-5
#endif
#define saturate(x) clamp(x, 0.0, 1.0)
#define saturate(x) clamp(x, 0.0, 1.0)
#ifdef TARGET_MOBILE
#define PREVENT_DIV0(n, d, magic) ((n) / max(d, magic))
#else
#define PREVENT_DIV0(n, d, magic) ((n) / (d))
#endif
//------------------------------------------------------------------------------
// Scalar operations

View File

@@ -73,9 +73,9 @@ float D_GGX(float roughness, float NoH, const vec3 h) {
#endif
float a = NoH * roughness;
float k = roughness / (oneMinusNoHSquared + a * a);
float d = k * k * (1.0 / PI);
return saturateMediump(d);
float k = min(roughness / (oneMinusNoHSquared + a * a), 453.5); // 453.5 prevents fp16 overflow
float d = k * (k * (1.0 / PI));
return d;
}
float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) {
@@ -105,17 +105,18 @@ float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
// TODO: lambdaV can be pre-computed for all the lights, it should be moved out of this function
float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
float v = 0.5 / (lambdaV + lambdaL);
// 0.0000077 = nextafter(0.5 / MEDIUMP_FLT_MAX, 1.0) in fp16, so we don't overflow
float v = PREVENT_DIV0(0.5, lambdaV + lambdaL, 0.0000077);
// a2=0 => v = 1 / 4*NoL*NoV => min=1/4, max=+inf
// a2=1 => v = 1 / 2*(NoL+NoV) => min=1/4, max=+inf
// clamp to the maximum value representable in mediump
return saturateMediump(v);
return v;
}
float V_SmithGGXCorrelated_Fast(float roughness, float NoV, float NoL) {
// Hammon 2017, "PBR Diffuse Lighting for GGX+Smith Microsurfaces"
float v = 0.5 / mix(2.0 * NoL * NoV, NoL + NoV, roughness);
return saturateMediump(v);
// 0.0000077 = nextafter(0.5 / MEDIUMP_FLT_MAX, 1.0) in fp16, so we don't overflow
float v = PREVENT_DIV0(0.5, mix(2.0 * NoL * NoV, NoL + NoV, roughness), 0.0000077);
return v;
}
float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
@@ -124,18 +125,21 @@ float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
// TODO: lambdaV can be pre-computed for all the lights, it should be moved out of this function
float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
float v = 0.5 / (lambdaV + lambdaL);
return saturateMediump(v);
// 0.0000077 = nextafter(0.5 / MEDIUMP_FLT_MAX, 1.0) in fp16, so we don't overflow
float v = PREVENT_DIV0(0.5, lambdaV + lambdaL, 0.0000077);
return v;
}
float V_Kelemen(float LoH) {
// Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling"
return saturateMediump(0.25 / (LoH * LoH));
// 0.0000039 = nextafter(0.25 / MEDIUMP_FLT_MAX, 1.0) in fp16, so we don't overflow
return PREVENT_DIV0(0.25, LoH * LoH, 0.0000039);
}
float V_Neubelt(float NoV, float NoL) {
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
return saturateMediump(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));
// 0.00001532 = nextafter(1.0 / MEDIUMP_FLT_MAX, 1.0) in fp16, so we don't overflow
return PREVENT_DIV0(1.0, 4.0 * (NoL + NoV - NoL * NoV), 0.00001532);
}
vec3 F_Schlick(const vec3 f0, float f90, float VoH) {