Add float-to-half (binary16) conversion functions.
Based on code by Fabian Giesen at https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/. These will be needed for creating binary16 textures from floating point data. BUG=skia:3103 Review URL: https://codereview.chromium.org/760753003
This commit is contained in:
parent
9881d63c57
commit
936799204b
@ -101,6 +101,8 @@
|
||||
'<(skia_src_path)/core/SkGlyphCache.h',
|
||||
'<(skia_src_path)/core/SkGlyphCache_Globals.h',
|
||||
'<(skia_src_path)/core/SkGraphics.cpp',
|
||||
'<(skia_src_path)/core/SkHalf.cpp',
|
||||
'<(skia_src_path)/core/SkHalf.h',
|
||||
'<(skia_src_path)/core/SkInstCnt.cpp',
|
||||
'<(skia_src_path)/core/SkImageFilter.cpp',
|
||||
'<(skia_src_path)/core/SkImageInfo.cpp',
|
||||
|
97
src/core/SkHalf.cpp
Normal file
97
src/core/SkHalf.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkHalf.h"
|
||||
#include "SkFloatBits.h"
|
||||
|
||||
uint16_t halfMantissa(SkHalf h) {
|
||||
return h & 0x03ff;
|
||||
}
|
||||
|
||||
uint16_t halfExponent(SkHalf h) {
|
||||
return (h >> 10) & 0x001f;
|
||||
}
|
||||
|
||||
uint16_t halfSign(SkHalf h) {
|
||||
return h >> 15;
|
||||
}
|
||||
|
||||
union FloatUIntUnion {
|
||||
uint32_t fUInt; // this must come first for the initializations below to work
|
||||
float fFloat;
|
||||
};
|
||||
|
||||
// based on Fabien Giesen's float_to_half_fast3()
|
||||
// see https://gist.github.com/rygorous/2156668
|
||||
SkHalf SkFloatToHalf(float f) {
|
||||
static const uint32_t f32infty = { 255 << 23 };
|
||||
static const uint32_t f16infty = { 31 << 23 };
|
||||
static const FloatUIntUnion magic = { 15 << 23 };
|
||||
static const uint32_t sign_mask = 0x80000000u;
|
||||
static const uint32_t round_mask = ~0xfffu;
|
||||
SkHalf o = 0;
|
||||
|
||||
FloatUIntUnion floatUnion;
|
||||
floatUnion.fFloat = f;
|
||||
|
||||
uint32_t sign = floatUnion.fUInt & sign_mask;
|
||||
floatUnion.fUInt ^= sign;
|
||||
|
||||
// NOTE all the integer compares in this function can be safely
|
||||
// compiled into signed compares since all operands are below
|
||||
// 0x80000000. Important if you want fast straight SSE2 code
|
||||
// (since there's no unsigned PCMPGTD).
|
||||
|
||||
// Inf or NaN (all exponent bits set)
|
||||
if (floatUnion.fUInt >= f32infty)
|
||||
// NaN->qNaN and Inf->Inf
|
||||
o = (floatUnion.fUInt > f32infty) ? 0x7e00 : 0x7c00;
|
||||
// (De)normalized number or zero
|
||||
else {
|
||||
floatUnion.fUInt &= round_mask;
|
||||
floatUnion.fFloat *= magic.fFloat;
|
||||
floatUnion.fUInt -= round_mask;
|
||||
// Clamp to signed infinity if overflowed
|
||||
if (floatUnion.fUInt > f16infty) {
|
||||
floatUnion.fUInt = f16infty;
|
||||
}
|
||||
|
||||
o = floatUnion.fUInt >> 13; // Take the bits!
|
||||
}
|
||||
|
||||
o |= sign >> 16;
|
||||
return o;
|
||||
}
|
||||
|
||||
// based on Fabien Giesen's half_to_float_fast2()
|
||||
// see https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
||||
float SkHalfToFloat(SkHalf h) {
|
||||
static const FloatUIntUnion magic = { 126 << 23 };
|
||||
FloatUIntUnion o;
|
||||
|
||||
if (halfExponent(h) == 0)
|
||||
{
|
||||
// Zero / Denormal
|
||||
o.fUInt = magic.fUInt + halfMantissa(h);
|
||||
o.fFloat -= magic.fFloat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set mantissa
|
||||
o.fUInt = halfMantissa(h) << 13;
|
||||
// Set exponent
|
||||
if (halfExponent(h) == 0x1f)
|
||||
// Inf/NaN
|
||||
o.fUInt |= (255 << 23);
|
||||
else
|
||||
o.fUInt |= ((127 - 15 + halfExponent(h)) << 23);
|
||||
}
|
||||
|
||||
// Set sign
|
||||
o.fUInt |= (halfSign(h) << 31);
|
||||
return o.fFloat;
|
||||
}
|
22
src/core/SkHalf.h
Normal file
22
src/core/SkHalf.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkHalf_DEFINED
|
||||
#define SkHalf_DEFINED
|
||||
|
||||
#include "SkTypes.h"
|
||||
|
||||
// 16-bit floating point value
|
||||
// format is 1 bit sign, 5 bits exponent, 10 bits mantissa
|
||||
// only used for storage
|
||||
typedef uint16_t SkHalf;
|
||||
|
||||
// convert between half and single precision floating point
|
||||
float SkHalfToFloat(SkHalf h);
|
||||
SkHalf SkFloatToHalf(float f);
|
||||
|
||||
#endif
|
@ -9,6 +9,7 @@
|
||||
#include "SkEndian.h"
|
||||
#include "SkFloatBits.h"
|
||||
#include "SkFloatingPoint.h"
|
||||
#include "SkHalf.h"
|
||||
#include "SkMathPriv.h"
|
||||
#include "SkPoint.h"
|
||||
#include "SkRandom.h"
|
||||
@ -326,6 +327,61 @@ static void unittest_isfinite(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, SkScalarIsFinite(0));
|
||||
}
|
||||
|
||||
static void unittest_half(skiatest::Reporter* reporter) {
|
||||
static const float gFloats[] = {
|
||||
0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
|
||||
-0.f, -1.f, -0.5f, -0.499999f, -0.5000001f, -1.f/3
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(gFloats); ++i) {
|
||||
SkHalf h = SkFloatToHalf(gFloats[i]);
|
||||
float f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, gFloats[i]));
|
||||
}
|
||||
|
||||
// check some special values
|
||||
union FloatUnion {
|
||||
uint32_t fU;
|
||||
float fF;
|
||||
};
|
||||
|
||||
static const FloatUnion largestPositiveHalf = { ((142 << 23) | (1023 << 13)) };
|
||||
SkHalf h = SkFloatToHalf(largestPositiveHalf.fF);
|
||||
float f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestPositiveHalf.fF));
|
||||
|
||||
static const FloatUnion largestNegativeHalf = { (1u << 31) | (142u << 23) | (1023u << 13) };
|
||||
h = SkFloatToHalf(largestNegativeHalf.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestNegativeHalf.fF));
|
||||
|
||||
static const FloatUnion smallestPositiveHalf = { 102 << 23 };
|
||||
h = SkFloatToHalf(smallestPositiveHalf.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, smallestPositiveHalf.fF));
|
||||
|
||||
static const FloatUnion overflowHalf = { ((143 << 23) | (1023 << 13)) };
|
||||
h = SkFloatToHalf(overflowHalf.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
|
||||
|
||||
static const FloatUnion underflowHalf = { 101 << 23 };
|
||||
h = SkFloatToHalf(underflowHalf.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, f == 0.0f );
|
||||
|
||||
static const FloatUnion inf32 = { 255 << 23 };
|
||||
h = SkFloatToHalf(inf32.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
|
||||
|
||||
static const FloatUnion nan32 = { 255 << 23 | 1 };
|
||||
h = SkFloatToHalf(nan32.fF);
|
||||
f = SkHalfToFloat(h);
|
||||
REPORTER_ASSERT(reporter, SkScalarIsNaN(f) );
|
||||
|
||||
}
|
||||
|
||||
static void test_muldiv255(skiatest::Reporter* reporter) {
|
||||
for (int a = 0; a <= 255; a++) {
|
||||
for (int b = 0; b <= 255; b++) {
|
||||
@ -464,6 +520,7 @@ DEF_TEST(Math, reporter) {
|
||||
|
||||
unittest_fastfloat(reporter);
|
||||
unittest_isfinite(reporter);
|
||||
unittest_half(reporter);
|
||||
|
||||
for (i = 0; i < 10000; i++) {
|
||||
SkFixed numer = rand.nextS();
|
||||
|
Loading…
Reference in New Issue
Block a user