[Mesa-dev] [PATCH 3/6] glsl: move half<->float convertion to util
Rob Clark
robdclark at gmail.com
Sat Oct 10 12:29:40 PDT 2015
On Sat, Oct 10, 2015 at 3:09 PM, Matt Turner <mattst88 at gmail.com> wrote:
> On Sat, Oct 10, 2015 at 11:47 AM, Rob Clark <robdclark at gmail.com> wrote:
>> From: Rob Clark <robclark at freedesktop.org>
>>
>> Needed in NIR too, so move out of mesa/main/imports.c
>>
>> Signed-off-by: Rob Clark <robclark at freedesktop.org>
>> ---
>> src/glsl/Makefile.am | 1 +
>> src/mesa/main/imports.c | 148 --------------------------------------
>> src/mesa/main/imports.h | 38 ++++++++--
>> src/util/Makefile.sources | 2 +
>> src/util/convert.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++
>> src/util/convert.h | 43 +++++++++++
>> 6 files changed, 259 insertions(+), 152 deletions(-)
>> create mode 100644 src/util/convert.c
>> create mode 100644 src/util/convert.h
>>
>> diff --git a/src/glsl/Makefile.am b/src/glsl/Makefile.am
>> index 3265391..347919b 100644
>> --- a/src/glsl/Makefile.am
>> +++ b/src/glsl/Makefile.am
>> @@ -160,6 +160,7 @@ glsl_compiler_SOURCES = \
>> glsl_compiler_LDADD = \
>> libglsl.la \
>> $(top_builddir)/src/libglsl_util.la \
>> + $(top_builddir)/src/util/libmesautil.la \
>> $(PTHREAD_LIBS)
>>
>> glsl_test_SOURCES = \
>> diff --git a/src/mesa/main/imports.c b/src/mesa/main/imports.c
>> index 350e675..230ebbc 100644
>> --- a/src/mesa/main/imports.c
>> +++ b/src/mesa/main/imports.c
>> @@ -307,154 +307,6 @@ _mesa_bitcount_64(uint64_t n)
>> }
>> #endif
>>
>> -
>> -/**
>> - * Convert a 4-byte float to a 2-byte half float.
>> - *
>> - * Not all float32 values can be represented exactly as a float16 value. We
>> - * round such intermediate float32 values to the nearest float16. When the
>> - * float32 lies exactly between to float16 values, we round to the one with
>> - * an even mantissa.
>> - *
>> - * This rounding behavior has several benefits:
>> - * - It has no sign bias.
>> - *
>> - * - It reproduces the behavior of real hardware: opcode F32TO16 in Intel's
>> - * GPU ISA.
>> - *
>> - * - By reproducing the behavior of the GPU (at least on Intel hardware),
>> - * compile-time evaluation of constant packHalf2x16 GLSL expressions will
>> - * result in the same value as if the expression were executed on the GPU.
>> - */
>> -GLhalfARB
>> -_mesa_float_to_half(float val)
>> -{
>> - const fi_type fi = {val};
>> - const int flt_m = fi.i & 0x7fffff;
>> - const int flt_e = (fi.i >> 23) & 0xff;
>> - const int flt_s = (fi.i >> 31) & 0x1;
>> - int s, e, m = 0;
>> - GLhalfARB result;
>> -
>> - /* sign bit */
>> - s = flt_s;
>> -
>> - /* handle special cases */
>> - if ((flt_e == 0) && (flt_m == 0)) {
>> - /* zero */
>> - /* m = 0; - already set */
>> - e = 0;
>> - }
>> - else if ((flt_e == 0) && (flt_m != 0)) {
>> - /* denorm -- denorm float maps to 0 half */
>> - /* m = 0; - already set */
>> - e = 0;
>> - }
>> - else if ((flt_e == 0xff) && (flt_m == 0)) {
>> - /* infinity */
>> - /* m = 0; - already set */
>> - e = 31;
>> - }
>> - else if ((flt_e == 0xff) && (flt_m != 0)) {
>> - /* NaN */
>> - m = 1;
>> - e = 31;
>> - }
>> - else {
>> - /* regular number */
>> - const int new_exp = flt_e - 127;
>> - if (new_exp < -14) {
>> - /* The float32 lies in the range (0.0, min_normal16) and is rounded
>> - * to a nearby float16 value. The result will be either zero, subnormal,
>> - * or normal.
>> - */
>> - e = 0;
>> - m = _mesa_lroundevenf((1 << 24) * fabsf(fi.f));
>> - }
>> - else if (new_exp > 15) {
>> - /* map this value to infinity */
>> - /* m = 0; - already set */
>> - e = 31;
>> - }
>> - else {
>> - /* The float32 lies in the range
>> - * [min_normal16, max_normal16 + max_step16)
>> - * and is rounded to a nearby float16 value. The result will be
>> - * either normal or infinite.
>> - */
>> - e = new_exp + 15;
>> - m = _mesa_lroundevenf(flt_m / (float) (1 << 13));
>> - }
>> - }
>> -
>> - assert(0 <= m && m <= 1024);
>> - if (m == 1024) {
>> - /* The float32 was rounded upwards into the range of the next exponent,
>> - * so bump the exponent. This correctly handles the case where f32
>> - * should be rounded up to float16 infinity.
>> - */
>> - ++e;
>> - m = 0;
>> - }
>> -
>> - result = (s << 15) | (e << 10) | m;
>> - return result;
>> -}
>> -
>> -
>> -/**
>> - * Convert a 2-byte half float to a 4-byte float.
>> - * Based on code from:
>> - * http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html
>> - */
>> -float
>> -_mesa_half_to_float(GLhalfARB val)
>> -{
>> - /* XXX could also use a 64K-entry lookup table */
>> - const int m = val & 0x3ff;
>> - const int e = (val >> 10) & 0x1f;
>> - const int s = (val >> 15) & 0x1;
>> - int flt_m, flt_e, flt_s;
>> - fi_type fi;
>> - float result;
>> -
>> - /* sign bit */
>> - flt_s = s;
>> -
>> - /* handle special cases */
>> - if ((e == 0) && (m == 0)) {
>> - /* zero */
>> - flt_m = 0;
>> - flt_e = 0;
>> - }
>> - else if ((e == 0) && (m != 0)) {
>> - /* denorm -- denorm half will fit in non-denorm single */
>> - const float half_denorm = 1.0f / 16384.0f; /* 2^-14 */
>> - float mantissa = ((float) (m)) / 1024.0f;
>> - float sign = s ? -1.0f : 1.0f;
>> - return sign * mantissa * half_denorm;
>> - }
>> - else if ((e == 31) && (m == 0)) {
>> - /* infinity */
>> - flt_e = 0xff;
>> - flt_m = 0;
>> - }
>> - else if ((e == 31) && (m != 0)) {
>> - /* NaN */
>> - flt_e = 0xff;
>> - flt_m = 1;
>> - }
>> - else {
>> - /* regular */
>> - flt_e = e + 112;
>> - flt_m = m << 13;
>> - }
>> -
>> - fi.i = (flt_s << 31) | (flt_e << 23) | flt_m;
>> - result = fi.f;
>> - return result;
>> -}
>> -
>> /*@}*/
>>
>>
>> diff --git a/src/mesa/main/imports.h b/src/mesa/main/imports.h
>> index 9024758..3a09304 100644
>> --- a/src/mesa/main/imports.h
>> +++ b/src/mesa/main/imports.h
>> @@ -42,6 +42,7 @@
>> #include "compiler.h"
>> #include "glheader.h"
>> #include "errors.h"
>> +#include "util/convert.h"
>>
>> #ifdef __cplusplus
>> extern "C" {
>> @@ -396,12 +397,41 @@ _mesa_flsll(uint64_t n)
>> #endif
>> }
>>
>> +/**
>> + * Convert a 4-byte float to a 2-byte half float.
>> + *
>> + * Not all float32 values can be represented exactly as a float16 value. We
>> + * round such intermediate float32 values to the nearest float16. When the
>> + * float32 lies exactly between to float16 values, we round to the one with
>> + * an even mantissa.
>> + *
>> + * This rounding behavior has several benefits:
>> + * - It has no sign bias.
>> + *
>> + * - It reproduces the behavior of real hardware: opcode F32TO16 in Intel's
>> + * GPU ISA.
>> + *
>> + * - By reproducing the behavior of the GPU (at least on Intel hardware),
>> + * compile-time evaluation of constant packHalf2x16 GLSL expressions will
>> + * result in the same value as if the expression were executed on the GPU.
>> + */
>> +static inline GLhalfARB
>> +_mesa_float_to_half(float val)
>> +{
>> + return float_to_half(val);
>> +}
>>
>> -extern GLhalfARB
>> -_mesa_float_to_half(float f);
>>
>> -extern float
>> -_mesa_half_to_float(GLhalfARB h);
>> +/**
>> + * Convert a 2-byte half float to a 4-byte float.
>> + * Based on code from:
>> + * http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html
>> + */
>> +static inline float
>> +_mesa_half_to_float(GLhalfARB val)
>> +{
>> + return half_to_float(val);
>> +}
>>
>> static inline bool
>> _mesa_half_is_negative(GLhalfARB h)
>> diff --git a/src/util/Makefile.sources b/src/util/Makefile.sources
>> index e45431d..71b2a25 100644
>> --- a/src/util/Makefile.sources
>> +++ b/src/util/Makefile.sources
>> @@ -1,5 +1,7 @@
>> MESA_UTIL_FILES := \
>> bitset.h \
>> + convert.c \
>> + convert.h \
>> debug.c \
>> debug.h \
>> format_srgb.h \
>> diff --git a/src/util/convert.c b/src/util/convert.c
>> new file mode 100644
>> index 0000000..e86e322
>> --- /dev/null
>> +++ b/src/util/convert.c
>
> half-float.c seems like a better name.
I think I started w/ math.c/h until I realized that conflicted.. just
picked convert.c/h since that didn't seem to conflict with anything,
but not stuck on the name so I can change it
>> @@ -0,0 +1,179 @@
>> +/*
>> + * Copyright © 2015 Red Hat
>
> What the?
>
> No. This code was written by Brian (commit 7eb3e9b) and Chad (commit
> 529b6d1). Its copyright belongs to VMware and Intel (and probably
> "gking" from [1])
>
> [1] https://web.archive.org/web/20030303115125/http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html
was just generic boilerplate I have for new files.. didn't mean to
step on any toes, I can change it to something else if you let me know
what is preferred..
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice (including the next
>> + * paragraph) shall be included in all copies or substantial portions of the
>> + * Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
>> + * IN THE SOFTWARE.
>> + *
>> + * Authors:
>> + * Rob Clark <robclark at freedesktop.org>
>
> We're trying to stop with author lists generally, but listing yourself
> as an author on some code you copied from somewhere else is not okay.
sorry, was just boilerplate.. no objection to changing it to something else
>> + */
>> +
>> +#include <math.h>
>> +#include <assert.h>
>> +#include "convert.h"
>> +#include "rounding.h"
>> +
>> +typedef union { float f; int32_t i; uint32_t u; } fi_type;
>> +
>> +/**
>> + * Convert a 4-byte float to a 2-byte half float.
>> + *
>> + * Not all float32 values can be represented exactly as a float16 value. We
>> + * round such intermediate float32 values to the nearest float16. When the
>> + * float32 lies exactly between to float16 values, we round to the one with
>> + * an even mantissa.
>> + *
>> + * This rounding behavior has several benefits:
>> + * - It has no sign bias.
>> + *
>> + * - It reproduces the behavior of real hardware: opcode F32TO16 in Intel's
>> + * GPU ISA.
>> + *
>> + * - By reproducing the behavior of the GPU (at least on Intel hardware),
>> + * compile-time evaluation of constant packHalf2x16 GLSL expressions will
>> + * result in the same value as if the expression were executed on the GPU.
>> + */
>> +uint16_t
>> +float_to_half(float val)
>> +{
>> + const fi_type fi = {val};
>> + const int flt_m = fi.i & 0x7fffff;
>> + const int flt_e = (fi.i >> 23) & 0xff;
>> + const int flt_s = (fi.i >> 31) & 0x1;
>> + int s, e, m = 0;
>> + uint16_t result;
>> +
>> + /* sign bit */
>> + s = flt_s;
>> +
>> + /* handle special cases */
>> + if ((flt_e == 0) && (flt_m == 0)) {
>> + /* zero */
>> + /* m = 0; - already set */
>> + e = 0;
>> + }
>> + else if ((flt_e == 0) && (flt_m != 0)) {
>> + /* denorm -- denorm float maps to 0 half */
>> + /* m = 0; - already set */
>> + e = 0;
>> + }
>> + else if ((flt_e == 0xff) && (flt_m == 0)) {
>> + /* infinity */
>> + /* m = 0; - already set */
>> + e = 31;
>> + }
>> + else if ((flt_e == 0xff) && (flt_m != 0)) {
>> + /* NaN */
>> + m = 1;
>> + e = 31;
>> + }
>> + else {
>> + /* regular number */
>> + const int new_exp = flt_e - 127;
>> + if (new_exp < -14) {
>> + /* The float32 lies in the range (0.0, min_normal16) and is rounded
>> + * to a nearby float16 value. The result will be either zero, subnormal,
>> + * or normal.
>> + */
>> + e = 0;
>> + m = _mesa_lroundevenf((1 << 24) * fabsf(fi.f));
>> + }
>> + else if (new_exp > 15) {
>> + /* map this value to infinity */
>> + /* m = 0; - already set */
>> + e = 31;
>> + }
>> + else {
>> + /* The float32 lies in the range
>> + * [min_normal16, max_normal16 + max_step16)
>> + * and is rounded to a nearby float16 value. The result will be
>> + * either normal or infinite.
>> + */
>> + e = new_exp + 15;
>> + m = _mesa_lroundevenf(flt_m / (float) (1 << 13));
>> + }
>> + }
>> +
>> + assert(0 <= m && m <= 1024);
>> + if (m == 1024) {
>> + /* The float32 was rounded upwards into the range of the next exponent,
>> + * so bump the exponent. This correctly handles the case where f32
>> + * should be rounded up to float16 infinity.
>> + */
>> + ++e;
>> + m = 0;
>> + }
>> +
>> + result = (s << 15) | (e << 10) | m;
>> + return result;
>> +}
>> +
>> +
>> +/**
>> + * Convert a 2-byte half float to a 4-byte float.
>> + * Based on code from:
>> + * http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html
>> + */
>> +float
>> +half_to_float(uint16_t val)
>> +{
>> + /* XXX could also use a 64K-entry lookup table */
>> + const int m = val & 0x3ff;
>> + const int e = (val >> 10) & 0x1f;
>> + const int s = (val >> 15) & 0x1;
>> + int flt_m, flt_e, flt_s;
>> + fi_type fi;
>> + float result;
>> +
>> + /* sign bit */
>> + flt_s = s;
>> +
>> + /* handle special cases */
>> + if ((e == 0) && (m == 0)) {
>> + /* zero */
>> + flt_m = 0;
>> + flt_e = 0;
>> + }
>> + else if ((e == 0) && (m != 0)) {
>> + /* denorm -- denorm half will fit in non-denorm single */
>> + const float half_denorm = 1.0f / 16384.0f; /* 2^-14 */
>> + float mantissa = ((float) (m)) / 1024.0f;
>> + float sign = s ? -1.0f : 1.0f;
>> + return sign * mantissa * half_denorm;
>> + }
>> + else if ((e == 31) && (m == 0)) {
>> + /* infinity */
>> + flt_e = 0xff;
>> + flt_m = 0;
>> + }
>> + else if ((e == 31) && (m != 0)) {
>> + /* NaN */
>> + flt_e = 0xff;
>> + flt_m = 1;
>> + }
>> + else {
>> + /* regular */
>> + flt_e = e + 112;
>> + flt_m = m << 13;
>> + }
>> +
>> + fi.i = (flt_s << 31) | (flt_e << 23) | flt_m;
>> + result = fi.f;
>> + return result;
>> +}
>> diff --git a/src/util/convert.h b/src/util/convert.h
>> new file mode 100644
>> index 0000000..30d36a1
>> --- /dev/null
>> +++ b/src/util/convert.h
>> @@ -0,0 +1,43 @@
>> +/*
>> + * Copyright © 2015 Red Hat
>
> Seems fishy.
>
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice (including the next
>> + * paragraph) shall be included in all copies or substantial portions of the
>> + * Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
>> + * IN THE SOFTWARE.
>> + *
>> + * Authors:
>> + * Rob Clark <robclark at freedesktop.org>
>
> Again, no author list.
>
>> + */
>> +
>> +#ifndef _MATH_H_
>> +#define _MATH_H_
>
> Wrong name.
sorry, just left over from my first attempt before I realized
util/math.h conflicted w/ <math.h>..
>> +
>> +#include <stdint.h>
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +
>> +uint16_t float_to_half(float val);
>> +float half_to_float(uint16_t val);
>
> I think these functions need to be prefixed with something -- util_*
> or something or just leave them as _mesa_*.
util_xyz conflicted with gallium u_half stuff..
current choices where just something that didn't seem to conflict with
anything, so after bikeshedding is done let me know what is preferred
and I will go with that ;-)
the point was more just how to move things around to untangle the NIR
dependency on GLSL without completely pulling the yarn out of the
sweater..
BR,
-R
>> +
>> +#ifdef __cplusplus
>> +} /* extern C */
>> +#endif
>> +
>> +#endif /* _MATH_H_ */
More information about the mesa-dev
mailing list