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

Unified Diff: src/core/SkHalf.h

Issue 2151023003: Revert of Expand _01 half<->float limitation to _finite. Simplify. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkBitmap.cpp ('k') | src/core/SkLinearBitmapPipeline_sample.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/core/SkHalf.h
diff --git a/src/core/SkHalf.h b/src/core/SkHalf.h
index 2f2ed66c6a64016ee0ae512f92c2876298f2988e..5f5575ae1aeaede7f1fd6bde72c8df549f8557bf 100644
--- a/src/core/SkHalf.h
+++ b/src/core/SkHalf.h
@@ -24,10 +24,10 @@
float SkHalfToFloat(SkHalf h);
SkHalf SkFloatToHalf(float f);
-// Convert between half and single precision floating point,
-// assuming inputs and outputs are both finite.
-static inline Sk4f SkHalfToFloat_finite(uint64_t);
-static inline uint64_t SkFloatToHalf_finite(const Sk4f&);
+// Convert between half and single precision floating point, but pull any dirty
+// trick we can to make it faster as long as it's correct enough for values in [0,1].
+static inline Sk4f SkHalfToFloat_01(uint64_t);
+static inline uint64_t SkFloatToHalf_01(const Sk4f&);
// ~~~~~~~~~~~ impl ~~~~~~~~~~~~~~ //
@@ -36,7 +36,7 @@
// GCC 4.9 lacks the intrinsics to use ARMv8 f16<->f32 instructions, so we use inline assembly.
-static inline Sk4f SkHalfToFloat_finite(uint64_t hs) {
+static inline Sk4f SkHalfToFloat_01(uint64_t hs) {
#if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
float32x4_t fs;
asm ("fmov %d[fs], %[hs] \n" // vcreate_f16(hs)
@@ -44,28 +44,53 @@
: [fs] "=w" (fs) // =w: write-only NEON register
: [hs] "r" (hs)); // r: read-only 64-bit general register
return fs;
+
+#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
+ // NEON makes this pretty easy:
+ // - denormals are 10-bit * 2^-14 == 24-bit fixed point;
+ // - handle normals the same way as in SSE: align mantissa, then rebias exponent.
+ uint32x4_t h = vmovl_u16(vcreate_u16(hs)),
+ is_denorm = vcltq_u32(h, vdupq_n_u32(1<<10));
+ float32x4_t denorm = vcvtq_n_f32_u32(h, 24),
+ norm = vreinterpretq_f32_u32(vaddq_u32(vshlq_n_u32(h, 13),
+ vdupq_n_u32((127-15) << 23)));
+ return vbslq_f32(is_denorm, denorm, norm);
+
+#elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+ // If our input is a normal 16-bit float, things are pretty easy:
+ // - shift left by 13 to put the mantissa in the right place;
+ // - the exponent is wrong, but it just needs to be rebiased;
+ // - re-bias the exponent from 15-bias to 127-bias by adding (127-15).
+
+ // If our input is denormalized, we're going to do the same steps, plus a few more fix ups:
+ // - the input is h = K*2^-14, for some 10-bit fixed point K in [0,1);
+ // - by shifting left 13 and adding (127-15) to the exponent, we constructed the float value
+ // 2^-15*(1+K);
+ // - we'd need to subtract 2^-15 and multiply by 2 to get back to K*2^-14, or equivallently
+ // multiply by 2 then subtract 2^-14.
+ //
+ // - We'll work that multiply by 2 into the rebias, by adding 1 more to the exponent.
+ // - Conveniently, this leaves that rebias constant 2^-14, exactly what we want to subtract.
+
+ __m128i h = _mm_unpacklo_epi16(_mm_loadl_epi64((const __m128i*)&hs), _mm_setzero_si128());
+ const __m128i is_denorm = _mm_cmplt_epi32(h, _mm_set1_epi32(1<<10));
+
+ __m128i rebias = _mm_set1_epi32((127-15) << 23);
+ rebias = _mm_add_epi32(rebias, _mm_and_si128(is_denorm, _mm_set1_epi32(1<<23)));
+
+ __m128i f = _mm_add_epi32(_mm_slli_epi32(h, 13), rebias);
+ return _mm_sub_ps(_mm_castsi128_ps(f),
+ _mm_castsi128_ps(_mm_and_si128(is_denorm, rebias)));
#else
- Sk4i bits = SkNx_cast<int>(Sk4h::Load(&hs)), // Expand to 32 bit.
- sign = bits & 0x00008000, // Save the sign bit for later...
- positive = bits ^ sign, // ...but strip it off for now.
- is_denorm = positive < (1<<10); // Exponent == 0?
-
- // For normal half floats, extend the mantissa by 13 zero bits,
- // then adjust the exponent from 15 bias to 127 bias.
- Sk4i norm = (positive << 13) + ((127 - 15) << 23);
-
- // For denorm half floats, mask in the exponent-only float K that turns our
- // denorm value V*2^-14 into a normalized float K + V*2^-14. Then subtract off K.
- const Sk4i K = ((127-15) + (23-10) + 1) << 23;
- Sk4i mask_K = positive | K;
- Sk4f denorm = Sk4f::Load(&mask_K) - Sk4f::Load(&K);
-
- Sk4i merged = (sign << 16) | is_denorm.thenElse(Sk4i::Load(&denorm), norm);
- return Sk4f::Load(&merged);
+ float fs[4];
+ for (int i = 0; i < 4; i++) {
+ fs[i] = SkHalfToFloat(hs >> (i*16));
+ }
+ return Sk4f::Load(fs);
#endif
}
-static inline uint64_t SkFloatToHalf_finite(const Sk4f& fs) {
+static inline uint64_t SkFloatToHalf_01(const Sk4f& fs) {
uint64_t r;
#if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64)
float32x4_t vec = fs.fVec;
@@ -73,25 +98,25 @@
"fmov %[r], %d[vec] \n" // vst1_f16(&r, ...)
: [r] "=r" (r) // =r: write-only 64-bit general register
, [vec] "+w" (vec)); // +w: read-write NEON register
+
+// TODO: ARMv7 NEON float->half?
+
+#elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+ // Scale down from 127-bias to 15-bias, then cut off bottom 13 mantissa bits.
+ // This doesn't round, so it can be 1 bit too small.
+ const __m128 rebias = _mm_castsi128_ps(_mm_set1_epi32((127 - (127-15)) << 23));
+ __m128i h = _mm_srli_epi32(_mm_castps_si128(_mm_mul_ps(fs.fVec, rebias)), 13);
+ _mm_storel_epi64((__m128i*)&r, _mm_packs_epi32(h,h));
+
#else
- Sk4i bits = Sk4i::Load(&fs),
- sign = bits & 0x80000000, // Save the sign bit for later...
- positive = bits ^ sign, // ...but strip it off for now.
- will_be_denorm = positive < ((127-15+1) << 23); // positve < smallest normal half?
-
- // For normal half floats, adjust the exponent from 127 bias to 15 bias,
- // then drop the bottom 13 mantissa bits.
- Sk4i norm = (positive - ((127 - 15) << 23)) >> 13;
-
- // This mechanically inverts the denorm half -> normal float conversion above.
- // Knowning that and reading its explanation will leave you feeling more confident
- // than reading my best attempt at explaining this directly.
- const Sk4i K = ((127-15) + (23-10) + 1) << 23;
- Sk4f plus_K = Sk4f::Load(&positive) + Sk4f::Load(&K);
- Sk4i denorm = Sk4i::Load(&plus_K) ^ K;
-
- Sk4i merged = (sign >> 16) | will_be_denorm.thenElse(denorm, norm);
- SkNx_cast<uint16_t>(merged).store(&r);
+ SkHalf hs[4];
+ for (int i = 0; i < 4; i++) {
+ hs[i] = SkFloatToHalf(fs[i]);
+ }
+ r = (uint64_t)hs[3] << 48
+ | (uint64_t)hs[2] << 32
+ | (uint64_t)hs[1] << 16
+ | (uint64_t)hs[0] << 0;
#endif
return r;
}
« 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