Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: src/core/SkHalf.h

Issue 2145663003: Expand _01 half<->float limitation to _finite. Simplify. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: drop _mm_packus_epi32 for now. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkBitmap.cpp ('k') | src/core/SkLinearBitmapPipeline_sample.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2014 Google Inc. 2 * Copyright 2014 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #ifndef SkHalf_DEFINED 8 #ifndef SkHalf_DEFINED
9 #define SkHalf_DEFINED 9 #define SkHalf_DEFINED
10 10
11 #include "SkNx.h" 11 #include "SkNx.h"
12 #include "SkTypes.h" 12 #include "SkTypes.h"
13 13
14 // 16-bit floating point value 14 // 16-bit floating point value
15 // format is 1 bit sign, 5 bits exponent, 10 bits mantissa 15 // format is 1 bit sign, 5 bits exponent, 10 bits mantissa
16 // only used for storage 16 // only used for storage
17 typedef uint16_t SkHalf; 17 typedef uint16_t SkHalf;
18 18
19 #define SK_HalfMin 0x0400 // 2^-24 (minimum positive normal value) 19 #define SK_HalfMin 0x0400 // 2^-24 (minimum positive normal value)
20 #define SK_HalfMax 0x7bff // 65504 20 #define SK_HalfMax 0x7bff // 65504
21 #define SK_HalfEpsilon 0x1400 // 2^-10 21 #define SK_HalfEpsilon 0x1400 // 2^-10
22 22
23 // convert between half and single precision floating point 23 // convert between half and single precision floating point
24 float SkHalfToFloat(SkHalf h); 24 float SkHalfToFloat(SkHalf h);
25 SkHalf SkFloatToHalf(float f); 25 SkHalf SkFloatToHalf(float f);
26 26
27 // Convert between half and single precision floating point, but pull any dirty 27 // Convert between half and single precision floating point,
28 // trick we can to make it faster as long as it's correct enough for values in [ 0,1]. 28 // assuming inputs and outputs are both finite.
29 static inline Sk4f SkHalfToFloat_01(uint64_t); 29 static inline Sk4f SkHalfToFloat_finite(uint64_t);
30 static inline uint64_t SkFloatToHalf_01(const Sk4f&); 30 static inline uint64_t SkFloatToHalf_finite(const Sk4f&);
31 31
32 // ~~~~~~~~~~~ impl ~~~~~~~~~~~~~~ // 32 // ~~~~~~~~~~~ impl ~~~~~~~~~~~~~~ //
33 33
34 // Like the serial versions in SkHalf.cpp, these are based on 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/ 35 // https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
36 36
37 // GCC 4.9 lacks the intrinsics to use ARMv8 f16<->f32 instructions, so we use i nline assembly. 37 // GCC 4.9 lacks the intrinsics to use ARMv8 f16<->f32 instructions, so we use i nline assembly.
38 38
39 static inline Sk4f SkHalfToFloat_01(uint64_t hs) { 39 static inline Sk4f SkHalfToFloat_finite(uint64_t hs) {
40 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64) 40 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
41 float32x4_t fs; 41 float32x4_t fs;
42 asm ("fmov %d[fs], %[hs] \n" // vcreate_f16(hs) 42 asm ("fmov %d[fs], %[hs] \n" // vcreate_f16(hs)
43 "fcvtl %[fs].4s, %[fs].4h \n" // vcvt_f32_f16(...) 43 "fcvtl %[fs].4s, %[fs].4h \n" // vcvt_f32_f16(...)
44 : [fs] "=w" (fs) // =w: write-only NEON register 44 : [fs] "=w" (fs) // =w: write-only NEON register
45 : [hs] "r" (hs)); // r: read-only 64-bit general regis ter 45 : [hs] "r" (hs)); // r: read-only 64-bit general regis ter
46 return fs; 46 return fs;
47 #else
48 Sk4i bits = SkNx_cast<int>(Sk4h::Load(&hs)), // Expand to 32 bit.
49 sign = bits & 0x00008000, // Save the sign bit for later...
50 positive = bits ^ sign, // ...but strip it off f or now.
51 is_denorm = positive < (1<<10); // Exponent == 0?
47 52
48 #elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON) 53 // For normal half floats, extend the mantissa by 13 zero bits,
49 // NEON makes this pretty easy: 54 // then adjust the exponent from 15 bias to 127 bias.
50 // - denormals are 10-bit * 2^-14 == 24-bit fixed point; 55 Sk4i norm = (positive << 13) + ((127 - 15) << 23);
51 // - handle normals the same way as in SSE: align mantissa, then rebias ex ponent.
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 56
59 #elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 57 // For denorm half floats, mask in the exponent-only float K that turns our
60 // If our input is a normal 16-bit float, things are pretty easy: 58 // denorm value V*2^-14 into a normalized float K + V*2^-14. Then subtract off K.
61 // - shift left by 13 to put the mantissa in the right place; 59 const Sk4i K = ((127-15) + (23-10) + 1) << 23;
62 // - the exponent is wrong, but it just needs to be rebiased; 60 Sk4i mask_K = positive | K;
63 // - re-bias the exponent from 15-bias to 127-bias by adding (127-15). 61 Sk4f denorm = Sk4f::Load(&mask_K) - Sk4f::Load(&K);
64 62
65 // If our input is denormalized, we're going to do the same steps, plus a fe w more fix ups: 63 Sk4i merged = (sign << 16) | is_denorm.thenElse(Sk4i::Load(&denorm), norm);
66 // - the input is h = K*2^-14, for some 10-bit fixed point K in [0,1); 64 return Sk4f::Load(&merged);
67 // - by shifting left 13 and adding (127-15) to the exponent, we construct ed 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 th e 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_set zero_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 65 #endif
91 } 66 }
92 67
93 static inline uint64_t SkFloatToHalf_01(const Sk4f& fs) { 68 static inline uint64_t SkFloatToHalf_finite(const Sk4f& fs) {
94 uint64_t r; 69 uint64_t r;
95 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64) 70 #if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
96 float32x4_t vec = fs.fVec; 71 float32x4_t vec = fs.fVec;
97 asm ("fcvtn %[vec].4h, %[vec].4s \n" // vcvt_f16_f32(vec) 72 asm ("fcvtn %[vec].4h, %[vec].4s \n" // vcvt_f16_f32(vec)
98 "fmov %[r], %d[vec] \n" // vst1_f16(&r, ...) 73 "fmov %[r], %d[vec] \n" // vst1_f16(&r, ...)
99 : [r] "=r" (r) // =r: write-only 64-bit general reg ister 74 : [r] "=r" (r) // =r: write-only 64-bit general reg ister
100 , [vec] "+w" (vec)); // +w: read-write NEON register 75 , [vec] "+w" (vec)); // +w: read-write NEON register
76 #else
77 Sk4i bits = Sk4i::Load(&fs),
78 sign = bits & 0x80000000, // Save the sign bit f or later...
79 positive = bits ^ sign, // ...but strip it off for now.
80 will_be_denorm = positive < ((127-15+1) << 23); // positve < smallest normal half?
101 81
102 // TODO: ARMv7 NEON float->half? 82 // For normal half floats, adjust the exponent from 127 bias to 15 bias,
83 // then drop the bottom 13 mantissa bits.
84 Sk4i norm = (positive - ((127 - 15) << 23)) >> 13;
103 85
104 #elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 86 // This mechanically inverts the denorm half -> normal float conversion abov e.
105 // Scale down from 127-bias to 15-bias, then cut off bottom 13 mantissa bits . 87 // Knowning that and reading its explanation will leave you feeling more con fident
106 // This doesn't round, so it can be 1 bit too small. 88 // than reading my best attempt at explaining this directly.
107 const __m128 rebias = _mm_castsi128_ps(_mm_set1_epi32((127 - (127-15)) << 23 )); 89 const Sk4i K = ((127-15) + (23-10) + 1) << 23;
108 __m128i h = _mm_srli_epi32(_mm_castps_si128(_mm_mul_ps(fs.fVec, rebias)), 13 ); 90 Sk4f plus_K = Sk4f::Load(&positive) + Sk4f::Load(&K);
109 _mm_storel_epi64((__m128i*)&r, _mm_packs_epi32(h,h)); 91 Sk4i denorm = Sk4i::Load(&plus_K) ^ K;
110 92
111 #else 93 Sk4i merged = (sign >> 16) | will_be_denorm.thenElse(denorm, norm);
112 SkHalf hs[4]; 94 SkNx_cast<uint16_t>(merged).store(&r);
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 95 #endif
121 return r; 96 return r;
122 } 97 }
123 98
124 #endif 99 #endif
OLDNEW
« no previous file with comments | « src/core/SkBitmap.cpp ('k') | src/core/SkLinearBitmapPipeline_sample.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698