From 39a4002eaadc208f7ba044c368fc19477b12c83a Mon Sep 17 00:00:00 2001 From: Shaobo Date: Wed, 9 Feb 2022 14:28:17 +0800 Subject: [PATCH 1/3] Update isnan implementation in WebGL backend to follow IEEE 754-1985 Previous implementation of isNaN in WebGL relies on the platform behaviour. And it might generate incorrect results(See #5800). This PR implements isnan based on the rules in IEEE 754-1985 to restrict the rules. Issue:#5800 --- tfjs-backend-webgl/src/glsl_version.ts | 51 +++++++++++++------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/tfjs-backend-webgl/src/glsl_version.ts b/tfjs-backend-webgl/src/glsl_version.ts index e37aedc3546..7083b314b07 100644 --- a/tfjs-backend-webgl/src/glsl_version.ts +++ b/tfjs-backend-webgl/src/glsl_version.ts @@ -41,6 +41,31 @@ export function getGlslDifferences(): GLSL { let defineSpecialInf: string; let defineRound: string; + // For WebGL1, it has no built in isnan so we need to define one. + // For WebGL2, use custom isnan definition to work across differences between + // implementations on various platforms. While this should happen in ANGLE + // we still see differences between android and windows (on chrome) when + // using isnan directly. + // NaN defination in IEEE 754-1985 is : + // - sign = either 0 or 1. + // - biased exponent = all 1 bits. + // - fraction = anything except all 0 bits (since all 0 bits represents + // infinity). + // https://en.wikipedia.org/wiki/IEEE_754-1985#Representation_of_non-numbers + defineSpecialNaN = ` + bool isnan_custom(float val) { + uint floatToUint = floatBitsToUint(val); + return (floatToUint & 0x7fffffffu) > 0x7f800000u; + } + + bvec4 isnan_custom(vec4 val) { + return bvec4(isnan_custom(val.x), + isnan_custom(val.y), isnan_custom(val.z), isnan_custom(val.w)); + } + + #define isnan(value) isnan_custom(value) + `; + if (env().getNumber('WEBGL_VERSION') === 2) { version = '#version 300 es'; attribute = 'in'; @@ -50,22 +75,6 @@ export function getGlslDifferences(): GLSL { output = 'outputColor'; defineOutput = 'out vec4 outputColor;'; - // Use custom isnan definition to work across differences between - // implementations on various platforms. While this should happen in ANGLE - // we still see differences between android and windows (on chrome) when - // using isnan directly. - defineSpecialNaN = ` - bool isnan_custom(float val) { - return (val > 0.0 || val < 0.0) ? false : val != 0.0; - } - - bvec4 isnan_custom(vec4 val) { - return bvec4(isnan_custom(val.x), - isnan_custom(val.y), isnan_custom(val.z), isnan_custom(val.w)); - } - - #define isnan(value) isnan_custom(value) - `; // In webgl 2 we do not need to specify a custom isinf so there is no // need for a special INFINITY constant. defineSpecialInf = ``; @@ -87,16 +96,6 @@ export function getGlslDifferences(): GLSL { texture2D = 'texture2D'; output = 'gl_FragColor'; defineOutput = ''; - // WebGL1 has no built in isnan so we define one here. - defineSpecialNaN = ` - #define isnan(value) isnan_custom(value) - bool isnan_custom(float val) { - return (val > 0. || val < 1. || val == 0.) ? false : true; - } - bvec4 isnan_custom(vec4 val) { - return bvec4(isnan(val.x), isnan(val.y), isnan(val.z), isnan(val.w)); - } - `; defineSpecialInf = ` uniform float INFINITY; From 51a80ee3f9b7b35e93a23c2564bf886a8769969c Mon Sep 17 00:00:00 2001 From: Shaobo Date: Thu, 10 Feb 2022 12:40:02 +0800 Subject: [PATCH 2/3] Only apply bit version isnan for WebGL2 --- tfjs-backend-webgl/src/glsl_version.ts | 59 +++++++++++++++----------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/tfjs-backend-webgl/src/glsl_version.ts b/tfjs-backend-webgl/src/glsl_version.ts index 7083b314b07..b34c33be3e5 100644 --- a/tfjs-backend-webgl/src/glsl_version.ts +++ b/tfjs-backend-webgl/src/glsl_version.ts @@ -41,31 +41,6 @@ export function getGlslDifferences(): GLSL { let defineSpecialInf: string; let defineRound: string; - // For WebGL1, it has no built in isnan so we need to define one. - // For WebGL2, use custom isnan definition to work across differences between - // implementations on various platforms. While this should happen in ANGLE - // we still see differences between android and windows (on chrome) when - // using isnan directly. - // NaN defination in IEEE 754-1985 is : - // - sign = either 0 or 1. - // - biased exponent = all 1 bits. - // - fraction = anything except all 0 bits (since all 0 bits represents - // infinity). - // https://en.wikipedia.org/wiki/IEEE_754-1985#Representation_of_non-numbers - defineSpecialNaN = ` - bool isnan_custom(float val) { - uint floatToUint = floatBitsToUint(val); - return (floatToUint & 0x7fffffffu) > 0x7f800000u; - } - - bvec4 isnan_custom(vec4 val) { - return bvec4(isnan_custom(val.x), - isnan_custom(val.y), isnan_custom(val.z), isnan_custom(val.w)); - } - - #define isnan(value) isnan_custom(value) - `; - if (env().getNumber('WEBGL_VERSION') === 2) { version = '#version 300 es'; attribute = 'in'; @@ -75,6 +50,30 @@ export function getGlslDifferences(): GLSL { output = 'outputColor'; defineOutput = 'out vec4 outputColor;'; + // For WebGL2, use custom isnan definition to work across differences between + // implementations on various platforms. While this should happen in ANGLE + // we still see differences between android and windows (on chrome) when + // using isnan directly. Since WebGL2 supports uint type and floatBitsToUint + // built-in function, we could implment isnan following IEEE 754. + // NaN defination in IEEE 754-1985 is : + // - sign = either 0 or 1. + // - biased exponent = all 1 bits. + // - fraction = anything except all 0 bits (since all 0 bits represents + // infinity). + // https://en.wikipedia.org/wiki/IEEE_754-1985#Representation_of_non-numbers + defineSpecialNaN = ` + bool isnan_custom(float val) { + uint floatToUint = floatBitsToUint(val); + return (floatToUint & 0x7fffffffu) > 0x7f800000u; + } + + bvec4 isnan_custom(vec4 val) { + return bvec4(isnan_custom(val.x), + isnan_custom(val.y), isnan_custom(val.z), isnan_custom(val.w)); + } + + #define isnan(value) isnan_custom(value) + `; // In webgl 2 we do not need to specify a custom isinf so there is no // need for a special INFINITY constant. defineSpecialInf = ``; @@ -96,6 +95,16 @@ export function getGlslDifferences(): GLSL { texture2D = 'texture2D'; output = 'gl_FragColor'; defineOutput = ''; + // WebGL1 has no built in isnan so we define one here. + defineSpecialNaN = ` + #define isnan(value) isnan_custom(value) + bool isnan_custom(float val) { + return (val > 0. || val < 1. || val == 0.) ? false : true; + } + bvec4 isnan_custom(vec4 val) { + return bvec4(isnan(val.x), isnan(val.y), isnan(val.z), isnan(val.w)); + } + `; defineSpecialInf = ` uniform float INFINITY; From 12ed9d5b0237dab718974f5ca8e6dede7ea44028 Mon Sep 17 00:00:00 2001 From: Shaobo Date: Thu, 10 Feb 2022 13:27:25 +0800 Subject: [PATCH 3/3] Fix 80-line exceeed issue --- tfjs-backend-webgl/src/glsl_version.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tfjs-backend-webgl/src/glsl_version.ts b/tfjs-backend-webgl/src/glsl_version.ts index b34c33be3e5..cf613f54401 100644 --- a/tfjs-backend-webgl/src/glsl_version.ts +++ b/tfjs-backend-webgl/src/glsl_version.ts @@ -50,11 +50,12 @@ export function getGlslDifferences(): GLSL { output = 'outputColor'; defineOutput = 'out vec4 outputColor;'; - // For WebGL2, use custom isnan definition to work across differences between + // Use custom isnan definition to work across differences between // implementations on various platforms. While this should happen in ANGLE // we still see differences between android and windows (on chrome) when - // using isnan directly. Since WebGL2 supports uint type and floatBitsToUint - // built-in function, we could implment isnan following IEEE 754. + // using isnan directly. Since WebGL2 supports uint type and + // floatBitsToUinT built-in function, we could implment isnan following + // IEEE 754 rules. // NaN defination in IEEE 754-1985 is : // - sign = either 0 or 1. // - biased exponent = all 1 bits.