Move Frexp and Ldexp to ShaderCore
These belong together with other non-trivial GLSL.std.450 extended
instructions that are equivalent to or approximate math.h functions.
Also move the Interpolate() helper function closer to where it is
needed.
Bug: b/243791551
Bug: b/214588983
Change-Id: Ibb77f9f5455702c3422bc329bac3486aea622daa
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/67788
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/ShaderCore.cpp b/src/Pipeline/ShaderCore.cpp
index c1a21c1..780f4f8 100644
--- a/src/Pipeline/ShaderCore.cpp
+++ b/src/Pipeline/ShaderCore.cpp
@@ -513,6 +513,117 @@
return Sqrt(x); // TODO(b/222218659): Optimize for relaxed precision.
}
+std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val)
+{
+ // Assumes IEEE 754
+ auto v = As<SIMD::UInt>(val);
+ auto isNotZero = CmpNEQ(v & 0x7FFFFFFF, 0);
+ auto zeroSign = v & 0x80000000 & ~isNotZero;
+ auto significand = As<SIMD::Float>((((v & 0x807FFFFF) | 0x3F000000) & isNotZero) | zeroSign);
+ auto exponent = Exponent(val) & SIMD::Int(isNotZero);
+
+ return std::make_pair(significand, exponent);
+}
+
+RValue<SIMD::Float> Ldexp(RValue<SIMD::Float> significand, RValue<SIMD::Int> exponent)
+{
+ // "load exponent"
+ // Ldexp(significand,exponent) computes
+ // significand * 2**exponent
+ // with edge case handling as permitted by the spec.
+ //
+ // The interesting cases are:
+ // - significand is subnormal and the exponent is positive. The mantissa
+ // bits of the significand shift left. The result *may* be normal, and
+ // in that case the leading 1 bit in the mantissa is no longer explicitly
+ // represented. Computing the result with bit operations would be quite
+ // complex.
+ // - significand has very small magnitude, and exponent is large.
+ // Example: significand = 0x1p-125, exponent = 250, result 0x1p125
+ // If we compute the result directly with the reference formula, then
+ // the intermediate value 2.0**exponent overflows, and then the result
+ // would overflow. Instead, it is sufficient to split the exponent
+ // and use two multiplies:
+ // (significand * 2**(exponent/2)) * (2**(exponent - exponent/2))
+ // In this formulation, the intermediates will not overflow when the
+ // correct result does not overflow. Also, this method naturally handles
+ // underflows, infinities, and NaNs.
+ //
+ // This implementation uses the two-multiplies approach described above,
+ // and also used by Mesa.
+ //
+ // The SPIR-V GLSL.std.450 extended instruction spec says:
+ //
+ // if exponent < -126 the result may be flushed to zero
+ // if exponent > 128 the result may be undefined
+ //
+ // Clamping exponent to [-254,254] allows us implement well beyond
+ // what is required by the spec, but still use simple algorithms.
+ //
+ // We decompose as follows:
+ // 2 ** exponent = powA * powB
+ // where
+ // powA = 2 ** (exponent / 2)
+ // powB = 2 ** (exponent - exponent / 2)
+ //
+ // We use a helper expression to compute these powers of two as float
+ // numbers using bit shifts, where X is an unbiased integer exponent
+ // in range [-127,127]:
+ //
+ // pow2i(X) = As<SIMD::Float>((X + 127)<<23)
+ //
+ // This places the biased exponent into position, and places all
+ // zeroes in the mantissa bit positions. The implicit 1 bit in the
+ // mantissa is hidden. When X = -127, the result is float 0.0, as
+ // if the value was flushed to zero. Otherwise X is in [-126,127]
+ // and the biased exponent is in [1,254] and the result is a normal
+ // float number with value 2**X.
+ //
+ // So we have:
+ //
+ // powA = pow2i(exponent/2)
+ // powB = pow2i(exponent - exponent/2)
+ //
+ // With exponent in [-254,254], we split into cases:
+ //
+ // exponent = -254:
+ // exponent/2 = -127
+ // exponent - exponent/2 = -127
+ // powA = pow2i(exponent/2) = pow2i(-127) = 0.0
+ // powA * powB is 0.0, which is a permitted flush-to-zero case.
+ //
+ // exponent = -253:
+ // exponent/2 = -126
+ // (exponent - exponent/2) = -127
+ // powB = pow2i(exponent - exponent/2) = pow2i(-127) = 0.0
+ // powA * powB is 0.0, which is a permitted flush-to-zero case.
+ //
+ // exponent in [-252,254]:
+ // exponent/2 is in [-126, 127]
+ // (exponent - exponent/2) is in [-126, 127]
+ //
+ // powA = pow2i(exponent/2), a normal number
+ // powB = pow2i(exponent - exponent/2), a normal number
+ //
+ // For the Mesa implementation, see
+ // https://gitlab.freedesktop.org/mesa/mesa/-/blob/1eb7a85b55f0c7c2de6f5dac7b5f6209a6eb401c/src/compiler/nir/nir_opt_algebraic.py#L2241
+
+ // Clamp exponent to limits
+ auto exp = Min(Max(exponent, -254), 254);
+
+ // Split exponent into two terms
+ auto expA = exp >> 1;
+ auto expB = exp - expA;
+ // Construct two powers of 2 with the exponents above
+ auto powA = As<SIMD::Float>((expA + 127) << 23);
+ auto powB = As<SIMD::Float>((expB + 127) << 23);
+
+ // Multiply the input value by the two powers to get the final value.
+ // Note that multiplying powA and powB together may result in an overflow,
+ // so ensure that significand is multiplied by powA, *then* the result of that with powB.
+ return (significand * powA) * powB;
+}
+
UInt4 halfToFloatBits(RValue<UInt4> halfBits)
{
auto magic = UInt4(126 << 23);
diff --git a/src/Pipeline/ShaderCore.hpp b/src/Pipeline/ShaderCore.hpp
index f7937ff..01993d0 100644
--- a/src/Pipeline/ShaderCore.hpp
+++ b/src/Pipeline/ShaderCore.hpp
@@ -119,6 +119,14 @@
RValue<SIMD::Float> Atanh(RValue<SIMD::Float> x, bool relaxedPrecision);
RValue<SIMD::Float> Sqrt(RValue<SIMD::Float> x, bool relaxedPrecision);
+// Splits x into a floating-point significand in the range [0.5, 1.0)
+// and an integral exponent of two, such that:
+// x = significand * 2^exponent
+// Returns the pair <significand, exponent>
+std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val);
+
+RValue<SIMD::Float> Ldexp(RValue<SIMD::Float> significand, RValue<SIMD::Int> exponent);
+
// Math functions with uses outside of shaders can be invoked using a verbose template argument instead
// of a Boolean argument to indicate precision. For example Sqrt<Mediump>(x) equals Sqrt(x, true).
enum Precision
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 0e09821..0c67b5f 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -1470,12 +1470,6 @@
static SIMD::Int AddSat(RValue<SIMD::Int> a, RValue<SIMD::Int> b);
static SIMD::UInt AddSat(RValue<SIMD::UInt> a, RValue<SIMD::UInt> b);
- // Splits x into a floating-point significand in the range [0.5, 1.0)
- // and an integral exponent of two, such that:
- // x = significand * 2^exponent
- // Returns the pair <significand, exponent>
- std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val) const;
-
static ImageSampler *getImageSampler(const vk::Device *device, uint32_t signature, uint32_t samplerId, uint32_t imageViewId);
static std::shared_ptr<rr::Routine> emitSamplerRoutine(ImageInstructionSignature instruction, const Sampler &samplerState);
static std::shared_ptr<rr::Routine> emitWriteRoutine(ImageInstructionSignature instruction, const Sampler &samplerState);
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index cc31ede..6c92602 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -774,15 +774,4 @@
return CmpLT(sum, a) | sum;
}
-std::pair<SIMD::Float, SIMD::Int> SpirvShader::Frexp(RValue<SIMD::Float> val) const
-{
- // Assumes IEEE 754
- auto v = As<SIMD::UInt>(val);
- auto isNotZero = CmpNEQ(v & SIMD::UInt(0x7FFFFFFF), SIMD::UInt(0));
- auto zeroSign = v & SIMD::UInt(0x80000000) & ~isNotZero;
- auto significand = As<SIMD::Float>((((v & SIMD::UInt(0x807FFFFF)) | SIMD::UInt(0x3F000000)) & isNotZero) | zeroSign);
- auto exponent = Exponent(val) & SIMD::Int(isNotZero);
- return std::make_pair(significand, exponent);
-}
-
} // namespace sw
\ No newline at end of file
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
index 752a8bc..c69c001 100644
--- a/src/Pipeline/SpirvShaderGLSLstd450.cpp
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -25,25 +25,6 @@
static constexpr float PI = 3.141592653589793f;
-static SIMD::Float Interpolate(const SIMD::Float &x, const SIMD::Float &y, const SIMD::Float &rhw,
- const SIMD::Float &A, const SIMD::Float &B, const SIMD::Float &C,
- SpirvRoutine::Interpolation interpolation)
-{
- SIMD::Float interpolant = C;
-
- if(interpolation != SpirvRoutine::Flat)
- {
- interpolant += x * A + y * B;
-
- if(interpolation == SpirvRoutine::Perspective)
- {
- interpolant *= rhw;
- }
- }
-
- return interpolant;
-}
-
SpirvShader::EmitResult SpirvShader::EmitExtGLSLstd450(InsnIterator insn, EmitState *state) const
{
auto &type = getType(insn.resultTypeId());
@@ -539,106 +520,12 @@
break;
case GLSLstd450Ldexp:
{
- // "load exponent"
- // Ldexp(significand,exponent) computes
- // significand * 2**exponent
- // with edge case handling as permitted by the spec.
- //
- // The interesting cases are:
- // - significand is subnormal and the exponent is positive. The mantissa
- // bits of the significand shift left. The result *may* be normal, and
- // in that case the leading 1 bit in the mantissa is no longer explicitly
- // represented. Computing the result with bit operations would be quite
- // complex.
- // - significand has very small magnitude, and exponent is large.
- // Example: significand = 0x1p-125, exponent = 250, result 0x1p125
- // If we compute the result directly with the reference formula, then
- // the intermediate value 2.0**exponent overflows, and then the result
- // would overflow. Instead, it is sufficient to split the exponent
- // and use two multiplies:
- // (significand * 2**(exponent/2)) * (2**(exponent - exponent/2))
- // In this formulation, the intermediates will not overflow when the
- // correct result does not overflow. Also, this method naturally handles
- // underflows, infinities, and NaNs.
- //
- // This implementation uses the two-multiplies approach described above,
- // and also used by Mesa.
- //
- // The SPIR-V GLSL.std.450 extended instruction spec says:
- //
- // if exponent < -126 the result may be flushed to zero
- // if exponent > 128 the result may be undefined
- //
- // Clamping exponent to [-254,254] allows us implement well beyond
- // what is required by the spec, but still use simple algorithms.
- //
- // We decompose as follows:
- // 2 ** exponent = fltA * fltB
- // where
- // fltA = 2 ** (exponent / 2)
- // fltB = 2 ** (exponent - exponent / 2)
- //
- // We use a helper expression to compute these powers of two as float
- // numbers using bit shifts, where X is an unbiased integer exponent
- // in range [-127,127]:
- //
- // pow2i(X) = As<SIMD::Float>((X + 127)<<23)
- //
- // This places the biased exponent into position, and places all
- // zeroes in the mantissa bit positions. The implicit 1 bit in the
- // mantissa is hidden. When X = -127, the result is float 0.0, as
- // if the value was flushed to zero. Otherwise X is in [-126,127]
- // and the biased exponent is in [1,254] and the result is a normal
- // float number with value 2**X.
- //
- // So we have:
- //
- // fltA = pow2i(exponent/2)
- // fltB = pow2i(exponent - exponent/2)
- //
- // With exponent in [-254,254], we split into cases:
- //
- // exponent = -254:
- // exponent/2 = -127
- // exponent - exponent/2 = -127
- // fltA = pow2i(exponent/2) = pow2i(-127) = 0.0
- // fltA * fltB is 0.0, which is a permitted flush-to-zero case.
- //
- // exponent = -253:
- // exponent/2 = -126
- // (exponent - exponent/2) = -127
- // fltB = pow2i(exponent - exponent/2) = pow2i(-127) = 0.0
- // fltA * fltB is 0.0, which is a permitted flush-to-zero case.
- //
- // exponent in [-252,254]:
- // exponent/2 is in [-126, 127]
- // (exponent - exponent/2) is in [-126, 127]
- //
- // fltA = pow2i(exponent/2), a normal number
- // fltB = pow2i(exponent - exponent/2), a normal number
- //
- // For the Mesa implementation, see
- // https://gitlab.freedesktop.org/mesa/mesa/-/blob/1eb7a85b55f0c7c2de6f5dac7b5f6209a6eb401c/src/compiler/nir/nir_opt_algebraic.py#L2241
- const auto minExp = SIMD::Int(-254);
- const auto maxExp = SIMD::Int(254);
- auto significand = Operand(this, state, insn.word(5));
- auto exponent = Operand(this, state, insn.word(6));
+ auto x = Operand(this, state, insn.word(5));
+ auto exp = Operand(this, state, insn.word(6));
+
for(auto i = 0u; i < type.componentCount; i++)
{
- // Clamp exponent to limits
- auto exp = Min(Max(exponent.Int(i), minExp), maxExp);
- // Split exponent into two parts
- auto expA = exp >> 1;
- auto expB = exp - expA;
- // Construct two floats with the exponents above
- auto fltA = As<SIMD::Float>((expA + 127) << 23);
- auto fltB = As<SIMD::Float>((expB + 127) << 23);
- // Multiply the input value by the two floats to get the final
- // number.
- // Note that multiplying fltA and fltB together may result in an
- // overflow, so ensure that significand is multiplied by fltA,
- // *then* the result of that with fltB.
- dst.move(i, (significand.Float(i) * fltA) * fltB);
+ dst.move(i, Ldexp(x.Float(i), exp.Int(i)));
}
}
break;
@@ -1072,6 +959,25 @@
return EmitResult::Continue;
}
+static SIMD::Float Interpolate(const SIMD::Float &x, const SIMD::Float &y, const SIMD::Float &rhw,
+ const SIMD::Float &A, const SIMD::Float &B, const SIMD::Float &C,
+ SpirvRoutine::Interpolation interpolation)
+{
+ SIMD::Float interpolant = C;
+
+ if(interpolation != SpirvRoutine::Flat)
+ {
+ interpolant += x * A + y * B;
+
+ if(interpolation == SpirvRoutine::Perspective)
+ {
+ interpolant *= rhw;
+ }
+ }
+
+ return interpolant;
+}
+
SIMD::Float SpirvShader::EmitInterpolate(SIMD::Pointer const &ptr, int32_t location, Object::ID paramId,
uint32_t component, EmitState *state, InterpolationType type) const
{