Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkHalf_DEFINED
      9 #define SkHalf_DEFINED
     10 
     11 #include "SkNx.h"
     12 #include "SkTypes.h"
     13 
     14 // 16-bit floating point value
     15 // format is 1 bit sign, 5 bits exponent, 10 bits mantissa
     16 // only used for storage
     17 typedef uint16_t SkHalf;
     18 
     19 #define SK_HalfMin      0x0400   // 2^-24  (minimum positive normal value)
     20 #define SK_HalfMax      0x7bff   // 65504
     21 #define SK_HalfEpsilon  0x1400   // 2^-10
     22 
     23 // convert between half and single precision floating point
     24 float SkHalfToFloat(SkHalf h);
     25 SkHalf SkFloatToHalf(float f);
     26 
     27 // Convert between half and single precision floating point, but pull any dirty
     28 // trick we can to make it faster as long as it's correct enough for values in [0,1].
     29 static inline     Sk4f SkHalfToFloat_01(uint64_t);
     30 static inline uint64_t SkFloatToHalf_01(const Sk4f&);
     31 
     32 // ~~~~~~~~~~~ impl ~~~~~~~~~~~~~~ //
     33 
     34 // Like the serial versions in SkHalf.cpp, these are based on
     35 // https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
     36 
     37 // GCC 4.9 lacks the intrinsics to use ARMv8 f16<->f32 instructions, so we use inline assembly.
     38 
     39 static inline Sk4f SkHalfToFloat_01(uint64_t hs) {
     40 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
     41     float32x4_t fs;
     42     asm ("fmov  %d[fs], %[hs]        \n"   // vcreate_f16(hs)
     43          "fcvtl %[fs].4s, %[fs].4h   \n"   // vcvt_f32_f16(...)
     44         : [fs] "=w" (fs)                   // =w: write-only NEON register
     45         : [hs] "r" (hs));                  //  r: read-only 64-bit general register
     46     return fs;
     47 
     48 #elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
     49     // NEON makes this pretty easy:
     50     //   - denormals are 10-bit * 2^-14 == 24-bit fixed point;
     51     //   - handle normals the same way as in SSE: align mantissa, then rebias exponent.
     52     uint32x4_t h = vmovl_u16(vcreate_u16(hs)),
     53                is_denorm = vcltq_u32(h, vdupq_n_u32(1<<10));
     54     float32x4_t denorm = vcvtq_n_f32_u32(h, 24),
     55                   norm = vreinterpretq_f32_u32(vaddq_u32(vshlq_n_u32(h, 13),
     56                                                          vdupq_n_u32((127-15) << 23)));
     57     return vbslq_f32(is_denorm, denorm, norm);
     58 
     59 #elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
     60     // If our input is a normal 16-bit float, things are pretty easy:
     61     //   - shift left by 13 to put the mantissa in the right place;
     62     //   - the exponent is wrong, but it just needs to be rebiased;
     63     //   - re-bias the exponent from 15-bias to 127-bias by adding (127-15).
     64 
     65     // If our input is denormalized, we're going to do the same steps, plus a few more fix ups:
     66     //   - the input is h = K*2^-14, for some 10-bit fixed point K in [0,1);
     67     //   - by shifting left 13 and adding (127-15) to the exponent, we constructed the float value
     68     //     2^-15*(1+K);
     69     //   - we'd need to subtract 2^-15 and multiply by 2 to get back to K*2^-14, or equivallently
     70     //     multiply by 2 then subtract 2^-14.
     71     //
     72     //   - We'll work that multiply by 2 into the rebias, by adding 1 more to the exponent.
     73     //   - Conveniently, this leaves that rebias constant 2^-14, exactly what we want to subtract.
     74 
     75     __m128i h = _mm_unpacklo_epi16(_mm_loadl_epi64((const __m128i*)&hs), _mm_setzero_si128());
     76     const __m128i is_denorm = _mm_cmplt_epi32(h, _mm_set1_epi32(1<<10));
     77 
     78     __m128i rebias = _mm_set1_epi32((127-15) << 23);
     79     rebias = _mm_add_epi32(rebias, _mm_and_si128(is_denorm, _mm_set1_epi32(1<<23)));
     80 
     81     __m128i f = _mm_add_epi32(_mm_slli_epi32(h, 13), rebias);
     82     return _mm_sub_ps(_mm_castsi128_ps(f),
     83                       _mm_castsi128_ps(_mm_and_si128(is_denorm, rebias)));
     84 #else
     85     float fs[4];
     86     for (int i = 0; i < 4; i++) {
     87         fs[i] = SkHalfToFloat(hs >> (i*16));
     88     }
     89     return Sk4f::Load(fs);
     90 #endif
     91 }
     92 
     93 static inline uint64_t SkFloatToHalf_01(const Sk4f& fs) {
     94     uint64_t r;
     95 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
     96     float32x4_t vec = fs.fVec;
     97     asm ("fcvtn %[vec].4h, %[vec].4s  \n"   // vcvt_f16_f32(vec)
     98          "fmov  %[r], %d[vec]         \n"   // vst1_f16(&r, ...)
     99         : [r] "=r" (r)                      // =r: write-only 64-bit general register
    100         , [vec] "+w" (vec));                // +w: read-write NEON register
    101 
    102 // TODO: ARMv7 NEON float->half?
    103 
    104 #elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
    105     // Scale down from 127-bias to 15-bias, then cut off bottom 13 mantissa bits.
    106     // This doesn't round, so it can be 1 bit too small.
    107     const __m128 rebias = _mm_castsi128_ps(_mm_set1_epi32((127 - (127-15)) << 23));
    108     __m128i h = _mm_srli_epi32(_mm_castps_si128(_mm_mul_ps(fs.fVec, rebias)), 13);
    109     _mm_storel_epi64((__m128i*)&r, _mm_packs_epi32(h,h));
    110 
    111 #else
    112     SkHalf hs[4];
    113     for (int i = 0; i < 4; i++) {
    114         hs[i] = SkFloatToHalf(fs[i]);
    115     }
    116     r = (uint64_t)hs[3] << 48
    117       | (uint64_t)hs[2] << 32
    118       | (uint64_t)hs[1] << 16
    119       | (uint64_t)hs[0] <<  0;
    120 #endif
    121     return r;
    122 }
    123 
    124 #endif
    125