Fix float-to-half conversion
The Reactor code in floatToHalfBits() did not match the reference
implementation of the sw::half type, leading to failures in dEQP tests
which are enabled by the shaderStorageImageExtendedFormats feature.
The previous code was based on https://gist.github.com/rygorous/2156668.
It doesn't round to nearest even in case of a tie, but that's not
demanded by Vulkan. It merits a closer look to see whether there was a
bug, or whether dEQP-VK is too strict. Dawn also found the previous
implementation to not handle infinity correctly, so a bug seems likely.
In any case, it's good to use a reference implementation for now, and
look for optimization opportunities later.
Bug: b/147900455
Bug: swiftshader:147
Tests: dEQP-VK.image.format_reinterpret.buffer.*_b10g11r11_ufloat_pack32
Change-Id: Id817a012ff38af814907c2de2914ec24565622f3
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/46148
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Pipeline/ShaderCore.cpp b/src/Pipeline/ShaderCore.cpp
index 83f8126..735248a 100644
--- a/src/Pipeline/ShaderCore.cpp
+++ b/src/Pipeline/ShaderCore.cpp
@@ -569,29 +569,27 @@
SIMD::UInt floatToHalfBits(SIMD::UInt floatBits, bool storeInUpperBits)
{
- static const uint32_t mask_sign = 0x80000000u;
- static const uint32_t mask_round = ~0xfffu;
- static const uint32_t c_f32infty = 255 << 23;
- static const uint32_t c_magic = 15 << 23;
- static const uint32_t c_nanbit = 0x200;
- static const uint32_t c_infty_as_fp16 = 0x7c00;
- static const uint32_t c_clamp = (31 << 23) - 0x1000;
+ SIMD::UInt sign = floatBits & SIMD::UInt(0x80000000);
+ SIMD::UInt abs = floatBits & SIMD::UInt(0x7FFFFFFF);
- SIMD::UInt justsign = SIMD::UInt(mask_sign) & floatBits;
- SIMD::UInt absf = floatBits ^ justsign;
- SIMD::UInt b_isnormal = CmpNLE(SIMD::UInt(c_f32infty), absf);
+ SIMD::UInt normal = CmpNLE(abs, SIMD::UInt(0x38800000));
- // Note: this version doesn't round to the nearest even in case of a tie as defined by IEEE 754-2008, it rounds to +inf
- // instead of nearest even, since that's fine for GLSL ES 3.0's needs (see section 2.1.1 Floating-Point Computation)
- SIMD::UInt joined = ((((As<SIMD::UInt>(Min(As<SIMD::Float>(absf & SIMD::UInt(mask_round)) * As<SIMD::Float>(SIMD::UInt(c_magic)),
- As<SIMD::Float>(SIMD::UInt(c_clamp))))) -
- SIMD::UInt(mask_round)) >>
- 13) &
- b_isnormal) |
- ((b_isnormal ^ SIMD::UInt(0xFFFFFFFF)) &
- ((CmpNLE(absf, SIMD::UInt(c_f32infty)) & SIMD::UInt(c_nanbit)) | SIMD::UInt(c_infty_as_fp16)));
+ SIMD::UInt mantissa = (abs & SIMD::UInt(0x007FFFFF)) | SIMD::UInt(0x00800000);
+ SIMD::UInt e = SIMD::UInt(113) - (abs >> 23);
+ SIMD::UInt denormal = CmpLT(e, SIMD::UInt(24)) & (mantissa >> e);
- return storeInUpperBits ? ((joined << 16) | justsign) : joined | (justsign >> 16);
+ SIMD::UInt base = (normal & abs) | (~normal & denormal); // TODO: IfThenElse()
+
+ // float exponent bias is 127, half bias is 15, so adjust by -112
+ SIMD::UInt bias = normal & SIMD::UInt(0xC8000000);
+
+ SIMD::UInt rounded = base + bias + SIMD::UInt(0x00000FFF) + ((base >> 13) & SIMD::UInt(1));
+ SIMD::UInt fp16u = rounded >> 13;
+
+ // Infinity
+ fp16u |= CmpNLE(abs, SIMD::UInt(0x47FFEFFF)) & SIMD::UInt(0x7FFF);
+
+ return storeInUpperBits ? (sign | (fp16u << 16)) : ((sign >> 16) | fp16u);
}
Float4 r11g11b10Unpack(UInt r11g11b10bits)
diff --git a/src/System/Half.cpp b/src/System/Half.cpp
index ea66920..fd04a31 100644
--- a/src/System/Half.cpp
+++ b/src/System/Half.cpp
@@ -29,7 +29,7 @@
else if(abs < 0x38800000) // Denormal
{
unsigned int mantissa = (abs & 0x007FFFFF) | 0x00800000;
- int e = 113 - (abs >> 23);
+ unsigned int e = 113 - (abs >> 23);
if(e < 24)
{