From fa91d36c2688cb593e975fbe017c0a47767967cb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Nicolai=20H=C3=A4hnle?= Date: Wed, 20 Sep 2017 22:26:44 +0200 Subject: [PATCH] Allow min/max/clamp to flush subnormals only during comparison An implementation may follow the rule that subnormals are flushed to zero for all arithmetic operations (including comparisons), but never for mere copies. In this case, if the input to min/max/clamp contains more than one subnormal number (including +/-0.0), the returned value may be any of the subnormal numbers and not really the true minimum/maximum/ clamped value. This does not hurt real applications, since any subsequent use of the result will flush it to zero anyway, and it does seem to be implicitly allowed by the GLSL ES spec. Change the implementation of min/max/clamp to explicitly handle the subnormal case in the comparison. Components: AOSP VK-GL-CTS issue: 705 Affects: dEQP-GLES3.functional.shaders.builtin_functions.precision.clamp.* dEQP-GLES3.functional.shaders.builtin_functions.precision.max.* dEQP-GLES3.functional.shaders.builtin_functions.precision.min.* dEQP-GLES31.functional.shaders.builtin_functions.precision.clamp.* dEQP-GLES31.functional.shaders.builtin_functions.precision.max.* dEQP-GLES31.functional.shaders.builtin_functions.precision.min.* Change-Id: I9a5bed9d78e311f96fe03b689299d777050ea063 --- modules/glshared/glsBuiltinPrecisionTests.cpp | 93 +++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/modules/glshared/glsBuiltinPrecisionTests.cpp b/modules/glshared/glsBuiltinPrecisionTests.cpp index afe990b..7019964 100644 --- a/modules/glshared/glsBuiltinPrecisionTests.cpp +++ b/modules/glshared/glsBuiltinPrecisionTests.cpp @@ -3300,17 +3300,102 @@ protected: } }; -class Min : public PreciseFunc2 { public: Min (void) : PreciseFunc2("min", deMin) {} }; -class Max : public PreciseFunc2 { public: Max (void) : PreciseFunc2("max", deMax) {} }; +int compare(const EvalContext& ctx, double x, double y) +{ + if (ctx.format.hasSubnormal() != tcu::YES) + { + const int minExp = ctx.format.getMinExp(); + const int fractionBits = ctx.format.getFractionBits(); + const double minQuantum = deLdExp(1.0f, minExp - fractionBits); + const double minNormalized = deLdExp(1.0f, minExp); + const double maxSubnormal = minNormalized - minQuantum; + const double minSubnormal = -maxSubnormal; + + if (minSubnormal <= x && x <= maxSubnormal && + minSubnormal <= y && y <= maxSubnormal) + return 0; + } + + if (x < y) + return -1; + if (y < x) + return 1; + return 0; +} + +class MinMaxFunc : public FloatFunc2 +{ +public: + MinMaxFunc (const string& name, + int sign) + : m_name(name) + , m_sign(sign) + { + } + + string getName (void) const { return m_name; } + +protected: + Interval applyPoint(const EvalContext& ctx, double x, double y) const + { + const int cmp = compare(ctx, x, y) * m_sign; + + if (cmp > 0) + return x; + if (cmp < 0) + return y; + + // An implementation without subnormals may not be able to distinguish + // between x and y even when they're not equal in host arithmetic. + return Interval(x, y); + } + + double precision (const EvalContext&, double, double, double) const + { + return 0.0; + } + + const string m_name; + const int m_sign; +}; + +class Min : public MinMaxFunc { public: Min (void) : MinMaxFunc("min", -1) {} }; +class Max : public MinMaxFunc { public: Max (void) : MinMaxFunc("max", 1) {} }; class Clamp : public FloatFunc3 { public: string getName (void) const { return "clamp"; } - double applyExact (double x, double minVal, double maxVal) const +protected: + Interval applyPoint(const EvalContext& ctx, double x, double minVal, double maxVal) const { - return de::min(de::max(x, minVal), maxVal); + if (minVal > maxVal) + return TCU_NAN; + + const int cmpMin = compare(ctx, x, minVal); + const int cmpMax = compare(ctx, x, maxVal); + const int cmpMinMax = compare(ctx, minVal, maxVal); + + if (cmpMin < 0) { + if (cmpMinMax < 0) + return minVal; + else + return Interval(minVal, maxVal); + } + if (cmpMax > 0) { + if (cmpMinMax < 0) + return maxVal; + else + return Interval(minVal, maxVal); + } + + Interval result = x; + if (cmpMin == 0) + result |= minVal; + if (cmpMax == 0) + result |= maxVal; + return result; } double precision (const EvalContext&, double, double, double minVal, double maxVal) const -- 2.7.4