diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/glsl.meta.slang | 58 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 55 |
2 files changed, 101 insertions, 12 deletions
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index 0ba6c17aa..9715a44ce 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -461,16 +461,28 @@ __generic<T : __BuiltinFloatingPointType> [require(cpp_cuda_glsl_hlsl_spirv, GLSL_130)] public T roundEven(T x) { - T i; - if (T(0.5) <= fmod(x, i)) + __target_switch { - bool evenInteger = (fmod(i, T(2)) == T(0)); - if (!evenInteger) + case glsl: __intrinsic_asm "roundEven"; + case spirv: return spirv_asm { + OpExtInst $$T result glsl450 RoundEven $x + }; + default: + T nearest = round(x); + + // Check if the value is exactly halfway between two integers + if (abs(x - nearest) == T(0.5)) { - x += T(0.1); + // If halfway, choose the even number + if (mod(nearest, T(2)) != T(0)) + { + // If the nearest number is odd, + // move to the closest even number + nearest -= ((x < nearest) ? T(1) : T(-1)); + } } + return nearest; } - return round(x); } __generic<T : __BuiltinFloatingPointType, let N:int> @@ -479,7 +491,15 @@ __generic<T : __BuiltinFloatingPointType, let N:int> [require(cpp_cuda_glsl_hlsl_spirv, GLSL_130)] public vector<T,N> roundEven(vector<T,N> x) { - VECTOR_MAP_UNARY(T, N, roundEven, x); + __target_switch + { + case glsl: __intrinsic_asm "roundEven"; + case spirv: return spirv_asm { + OpExtInst $$vector<T,N> result glsl450 RoundEven $x + }; + default: + VECTOR_MAP_UNARY(T, N, roundEven, x); + } } __generic<T : __BuiltinFloatingPointType> @@ -506,7 +526,15 @@ __generic<T : __BuiltinFloatingPointType> [require(cpp_cuda_glsl_hlsl_spirv, sm_2_0_GLSL_140)] public T mod(T x, T y) { - return fmod(x, y); + // SPIR-V doesn't have "modulus". + // All of Op?Mod and OpFRem are "remainder". + + __target_switch + { + case glsl: __intrinsic_asm "mod"; + default: + return x - y * floor(x / y); + } } __generic<T : __BuiltinFloatingPointType, let N:int> @@ -515,7 +543,12 @@ __generic<T : __BuiltinFloatingPointType, let N:int> [require(cpp_cuda_glsl_hlsl_spirv, sm_2_0_GLSL_140)] public vector<T, N> mod(vector<T, N> x, T y) { - return fmod(x, vector<T, N>(y)); + __target_switch + { + case glsl: __intrinsic_asm "mod"; + default: + return x - y * floor(x / y); + } } __generic<T : __BuiltinFloatingPointType, let N:int> @@ -524,7 +557,12 @@ __generic<T : __BuiltinFloatingPointType, let N:int> [require(cpp_cuda_glsl_hlsl_spirv, sm_2_0_GLSL_140)] public vector<T, N> mod(vector<T, N> x, vector<T, N> y) { - return fmod(x, y); + __target_switch + { + case glsl: __intrinsic_asm "mod"; + default: + return x - y * floor(x / y); + } } __generic<T : __BuiltinFloatingPointType, let N : int> diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 7cafe764f..ae81289d1 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -6504,13 +6504,61 @@ __generic<T : __BuiltinFloatingPointType> [require(cpp_cuda_glsl_hlsl_spirv, sm_2_0_GLSL_140)] T fmod(T x, T y) { + // In HLSL, fmod returns a remainder. + // Definition of `fmod` in HLSL is, + // "The floating-point remainder is calculated such that x = i * y + f, + // where i is an integer, f has the same sign as x, and the absolute value + // of f is less than the absolute value of y." + // + // In GLSL, mod is a Modulus function. + // OpenGL document defines "Modulus" as "Returns x - y * floor(x / y)". + // The use of "Floor()" makes the difference. + // + // The tricky ones are when x or y is a negative value. + // + // | Remainder | Modulus + // x y | x= i*y +f | x-y*floor(x/y) + // ------+-----------+------------------------------ + // 4 3 | 4= 1*3 +1 | 4-3*floor( 4/3) = 4-3* 1 = 1 + // 3 3 | 3= 1*3 +0 | 3-3*floor( 3/3) = 3-3* 1 = 0 + // 2 3 | 2= 0*3 +2 | 2-3*floor( 2/3) = 2-3* 0 = 2 + // 1 3 | 1= 0*3 +1 | 1-3*floor( 1/3) = 1-3* 0 = 1 + // 0 3 | 0= 0*3 +0 | 0-3*floor( 0/3) = 0-3* 0 = 0 + // -1 3 |-1= 0*3 -1 |-1-3*floor(-1/3) =-1-3*-1 = 2 + // -2 3 |-2= 0*3 -2 |-2-3*floor(-2/3) =-2-3*-1 = 1 + // -3 3 |-3=-1*3 0 |-3-3*floor(-3/3) =-3-3*-1 = 0 + // -4 3 |-4=-1*3 -1 |-4-3*floor(-4/3) =-4-3*-2 = 2 + // + // When y is a negative value, + // + // | Remainder | Modulus + // x y | x= i*y +f | x-y*floor(x/y) + // ------+-----------+------------------------------ + // 4 -3 | 4=-1*-3+1 | 4+3*floor( 4/-3) = 4+3*-2 =-2 + // 3 -3 | 3=-1*-3+0 | 3+3*floor( 3/-3) = 3+3*-1 = 0 + // 2 -3 | 2= 0*-3+2 | 2+3*floor( 2/-3) = 2+3*-1 =-1 + // 1 -3 | 1= 0*-3+1 | 1+3*floor( 1/-3) = 1+3*-1 =-2 + // 0 -3 | 0= 0*-3+0 | 0+3*floor( 0/-3) = 0+3* 0 = 0 + // -1 -3 |-1= 0*-3-1 |-1+3*floor(-1/-3) =-1+3* 0 =-1 + // -2 -3 |-2= 0*-3-2 |-2+3*floor(-2/-3) =-2+3* 0 =-2 + // -3 -3 |-3= 1*-3 0 |-3+3*floor(-3/-3) =-3+3* 1 = 0 + // -4 -3 |-4= 1*-3-1 |-4+3*floor(-4/-3) =-4+3* 1 =-1 + __target_switch { case cpp: __intrinsic_asm "$P_fmod($0, $1)"; case cuda: __intrinsic_asm "$P_fmod($0, $1)"; case hlsl: __intrinsic_asm "fmod"; - default: - return x - y * trunc(x/y); + case glsl: + // GLSL doesn't have a function for remainder. + __intrinsic_asm "(($0 < 0) ? -mod(-$0,abs($1)) : mod($0,abs($1)))"; + case spirv: + // OpFRem return "The floating-point remainder whose sign + // matches the sign of Operand 1", where Operand 1 is "x". + return spirv_asm + { + result:$$T = OpFRem $x $y + }; } } @@ -6522,6 +6570,9 @@ vector<T, N> fmod(vector<T, N> x, vector<T, N> y) __target_switch { case hlsl: __intrinsic_asm "fmod"; + case spirv: return spirv_asm { + result:$$vector<T,N> = OpFRem $x $y + }; default: VECTOR_MAP_BINARY(T, N, fmod, x, y); } |
