<div dir="ltr">On 9 January 2013 23:58, Chad Versace <span dir="ltr"><<a href="mailto:chad.versace@linux.intel.com" target="_blank">chad.versace@linux.intel.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">




<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">In order to generate tests for the GLSL packing functions (packSnorm2x16<br>
and friends), we must be able to simulate them. This patch adds<br>
a C executable that does exactly that.<br>
<br>
I originally tried to write this tool in Python, but manipulating 16-bit<br>
unsigned integers is not one of Python's strengths.<br>
<br>
Signed-off-by: Chad Versace <<a href="mailto:chad.versace@linux.intel.com" target="_blank">chad.versace@linux.intel.com</a>><br>
---<br>
 tests/util/CMakeLists.no_api.txt |   3 +<br>
 tests/util/glsl-packing.c        | 799 +++++++++++++++++++++++++++++++++++++++<br>
 2 files changed, 802 insertions(+)<br>
 create mode 100644 tests/util/glsl-packing.c<br>
<br>
diff --git a/tests/util/CMakeLists.no_api.txt b/tests/util/CMakeLists.no_api.txt<br>
index c331368..f1ac407 100644<br>
--- a/tests/util/CMakeLists.no_api.txt<br>
+++ b/tests/util/CMakeLists.no_api.txt<br>
@@ -6,8 +6,11 @@ piglit_add_library (piglitutil<br>
        ${UTIL_SOURCES}<br>
 )<br>
<br>
+piglit_add_executable(glsl-packing glsl-packing.c)<br>
+<br>
 if(UNIX)<br>
        target_link_libraries(piglitutil m)<br>
+       target_link_libraries(glsl-packing m)<br>
 endif(UNIX)<br>
<br>
 # vim: ft=cmake:<br>
diff --git a/tests/util/glsl-packing.c b/tests/util/glsl-packing.c<br>
new file mode 100644<br>
index 0000000..317f0d1<br>
--- /dev/null<br>
+++ b/tests/util/glsl-packing.c<br>
@@ -0,0 +1,799 @@<br>
+/*<br>
+ * Copyright © 2012 Intel Corporation<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>
+ * DEALINGS IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#include <assert.h><br>
+#include <inttypes.h><br>
+#include <math.h><br>
+#include <stdarg.h><br>
+#include <stdbool.h><br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <string.h><br>
+<br>
+/* TODO: Reject round options for pack/unpackHalf.<br>
+ * TODO: Discuss choices in packHalf.<br>
+ */<br></blockquote><div><br></div><div>Are these TODO items still open?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+#define PRIf32_PRECISION "24"<br>
+#define PRIf32 "%." PRIf32_PRECISION "f"<br>
+<br>
+static const char *help_text =<br>
+       "NAME\n"<br>
+       "    glsl-packing - Print the result of a GLSL packing function\n"<br>
+       "\n"<br>
+       "SYNOPSIS\n"<br>
+       "    glsl-packing PACK_FUNC X Y [FUNC_OPTS]\n"<br>
+       "    glsl-packing UNPACK_FUNC U [FUNC_OPTS]\n"<br>
+       "    glsl-packing print-float16-info\n"<br>
+       "\n"<br>
+       "COMMANDS\n"<br>
+       "    All floats are printed with the printf specifier " PRIf32 ".\n"<br>
+       "\n"<br>
+       "    glsl-packing PACK_FUNC X Y [FUNC_OPTS]\n"<br>
+       "        Print the result of calling PACK_FUNC on vec2(X, Y).\n"<br>
+       "\n"<br>
+       "        PACK_FUNC must be one of:\n"<br>
+       "            packSnorm2x16\n"<br>
+       "            packUnorm2x16\n"<br>
+       "            packHalf2x16\n"<br>
+       "\n"<br>
+       "        X and Y must be floating point numbers in a format consumable\n"<br>
+       "        by strof(3).\n"<br>
+       "\n"<br>
+       "    glsl-packing UNPACK_FUNC U [FUNC_OPTS]\n"<br>
+       "        Print the result of calling UNPACK_FUNC on uint(U).\n"<br>
+       "\n"<br>
+       "        UNPACK_FUNC must be one of:\n"<br>
+       "            unpackSnorm2x16\n"<br>
+       "            unpackUnorm2x16\n"<br>
+       "            unpackHalf2x16\n"<br>
+       "\n"<br>
+       "        U must be an unsigned integer in a format consumable by scanf(3).\n"<br>
+       "\n"<br>
+       "    glsl-packing print-float16-info\n"<br>
+       "        Print the following special values of IEEE 754 16-bit floats:\n"<br>
+       "            subnormal_min\n"<br>
+       "            subnormal_max\n"<br>
+       "            normal_min\n"<br>
+       "            normal_max\n"<br>
+       "            min_step\n"<br>
+       "            max_step\n"<br>
+       "\n"<br>
+       "FUNC_OPTS\n"<br>
+       "    flush_float16\n"<br>
+       "    flush_float32\n"<br>
+       "        All PACK_FUNC and UNPACK_FUNC commands accept the flush options.\n"<br>
+       "\n"<br>
+       "        The GLSL ES 3.00 and GLSL 4.10 specs allows implementations to truncate\n"<br>
+       "        subnormal floats to zero. From section 4.5.1 \"Range and Precision\"\n"<br>
+       "        of the two specs:\n"<br>
+       "            Any subnormal (denormalized) value input into a shader or\n"<br>
+       "            potentially generated by any operation in a shader can be\n"<br>
+       "            flushed to 0.\n"<br>
+       "\n"<br>
+       "        If flush_float32 is specified, then glsl-packing will simulate the behavior\n"<br>
+       "        of a GLSL implementation that flushes subnormal 32-bit floating-point values\n"<br>
+       "        to 0. Likewise if flush_float16 is enabled.\n"<br>
+       "\n"<br>
+       "        Enabling flush_float16 implicitly enables flush_float32.\n"<br>
+       "\n"<br>
+       "    round_to_nearest\n"<br>
+       "    round_to_even\n"<br>
+       "        All PACK_FUNC and UNPACK_FUNC commands except pack/unpackHalf2x16 accept\n"<br>
+       "        the rounding option. At most one rounding option may be specified.\n"<br>
+       "\n"<br>
+       "        For some packing functions, the GLSL ES 3.00 specification's\n"<br>
+       "        definition of the function's behavior involves the `round()`\n"<br>
+       "        function, whose behavior at 0.5 is not specified. From section \n"<br>
+       "        8.3 of the spec:\n"<br>
+       "            The fraction 0.5 will round in a direction chosen by the\n"<br>
+       "            implementation, presumably the direction that is fastest.\n"<br>
+       "\n"<br>
+       "        If a rounding option is given, it determines the rounding behavior at 0.5.\n"<br>
+       ;<br>
+<br>
+struct func_options;<br>
+<br>
+typedef uint16_t<br>
+(*pack_1x16_func_t)(float, struct func_options*);<br>
+<br>
+typedef void<br>
+(*unpack_1x16_func_t)(uint16_t, float*, struct func_options*);<br>
+<br>
+typedef float<br>
+(*round_func_t)(float);<br></blockquote><div><br></div>I'm not convinced that the full generality of function pointers is really necessary here--all we really need is to select signed/unsigned normalization and nearest/even rounding mode.  I would prefer to see enums for this purpose--they are more self-documenting and harder to make mistakes with.  An added advantage of using enums is that a single enum can serve to select signed/unsigned normalization for both packing and unpacking operations.<br>




<br></div><div class="gmail_quote">If you feel that it's really important to use function pointers here, I'd like to at least see the purpose and expected values of each documented here, e.g. "This function pointer represents the possible normalization modes for a packing function.  It will always point to either pack_snorm_1x16 or pack_unorm_1x16."<br>




</div><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+struct func_options {<br>
+       round_func_t round;<br>
+       bool flush_float16;<br>
+       bool flush_float32;<br>
+};<br>
+<br>
+/**<br>
+ * Flush subnormal 32-bit floating point numbers to ±0.0, preserving the<br>
+ * sign bit.<br>
+ */<br>
+static float<br>
+flush_float32(float f)<br>
+{<br>
+       if (fpclassify(f) == FP_SUBNORMAL) {<br>
+               return copysign(0.0, f);<br>
+       } else {<br>
+               return f;<br>
+       }<br>
+}<br>
+<br>
+/**<br>
+ * Flush subnormal 16-bit floating point numbers to to ±0.0, preserving the<br>
+ * sign bit.<br>
+ */<br>
+static uint16_t<br>
+flush_float16(uint16_t u)<br>
+{<br>
+       if (!(u & 0x7c00)) {<br>
+               return u & 0x8000;<br>
+       } else {<br>
+               return u;<br>
+       }<br>
+}<br>
+<br>
+static float<br>
+clampf(float x, float min, float max)<br>
+{<br>
+       if (x < min)<br>
+               return min;<br>
+       else if (x > max)<br>
+               return max;<br>
+       else<br>
+               return x;<br>
+}<br>
+<br>
+static float<br>
+round_to_nearest(float x)<br>
+{<br>
+       float i;<br>
+       float f;<br>
+<br>
+       f = modff(x, &i);<br>
+<br>
+       if (fabs(f) < 0.5f)<br>
+               return i;<br>
+       else<br>
+               return i + copysignf(1.0f, x);<br>
+}<br>
+<br>
+static float<br>
+round_to_even(float x)<br>
+{<br>
+       float i;<br>
+       float f;<br>
+<br>
+       f = modff(x, &i);<br>
+<br>
+       if (fabs(f) < 0.5f)<br>
+               return i;<br>
+       else if (fabs(f) == 0.5f)<br>
+               return i + fmodf(i, copysignf(2.0f, x));<br>
+       else<br>
+               return i + copysignf(1.0f, x);<br>
+}<br>
+<br>
+static void<br>
+func_options_init(struct func_options *func_opts)<br>
+{<br>
+       memset(func_opts, 0, sizeof(*func_opts));<br>
+}<br></blockquote><div><br></div><div>Not a big deal, but I'd feel a little more comfortable if this function set func_opts->round to a valid default, rather than leaving it NULL.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">




+<br>
+static uint32_t<br>
+pack_2x16(pack_1x16_func_t pack_1x16,<br>
+         float x, float y,<br>
+         struct func_options *func_opts)<br>
+{<br>
+       uint16_t ux;<br>
+       uint16_t uy;<br>
+<br>
+       if (func_opts->flush_float32) {<br>
+               x = flush_float32(x);<br>
+               y = flush_float32(y);<br>
+       }<br>
+<br>
+       ux = pack_1x16(x, func_opts);<br>
+       uy = pack_1x16(y, func_opts);<br>
+<br>
+       if (func_opts->flush_float16) {<br>
+               ux = flush_float16(ux);<br>
+               uy = flush_float16(uy);<br>
+       }<br>
+<br>
+       return ((uint32_t) uy << 16) | ux;<br>
+}<br>
+<br>
+static void<br>
+unpack_2x16(unpack_1x16_func_t unpack_1x16,<br>
+            uint32_t u,<br>
+            float *x, float *y,<br>
+            struct func_options *func_opts)<br>
+<br>
+{<br>
+        uint16_t ux = u & 0xffff;<br>
+        uint16_t uy = u >> 16;<br>
+<br>
+        if (func_opts->flush_float16) {<br>
+                ux = flush_float16(ux);<br>
+                uy = flush_float16(uy);<br>
+        }<br>
+<br>
+        unpack_1x16(ux, x, func_opts);<br>
+        unpack_1x16(uy, y, func_opts);<br>
+<br>
+        if (func_opts->flush_float32) {<br>
+                *x = flush_float32(*x);<br>
+                *y = flush_float32(*y);<br>
+        }<br>
+}<br>
+<br>
+static uint16_t<br>
+pack_snorm_1x16(float x, struct func_options *func_opts)<br>
+{<br>
+       return (uint16_t) func_opts->round(clampf(x, -1.0f, +1.0f) * 32767.0f);<br></blockquote><div><br></div><div>I believe the conversion from a negative float directly to a uint16_t is not well-defined.  For portability's sake we should do:<br>


<br></div><div>return (uint16_t) (int32_t) func_opts->round(...);<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+static void<br>
+unpack_snorm_1x16(uint16_t u, float *f, struct func_options *func_opts)<br>
+{<br>
+        *f = clampf((int16_t) u / 32767.0f, -1.0f, +1.0f);<br>
+}<br>
+<br>
+static uint16_t<br>
+pack_unorm_1x16(float x, struct func_options *func_opts)<br>
+{<br>
+       return (uint16_t) func_opts->round(clampf(x, 0.0f, 1.0f) * 65535.0f);<br>
+}<br>
+<br>
+static void<br>
+unpack_unorm_1x16(uint16_t u, float *f, struct func_options *func_opts)<br>
+{<br>
+        *f = (float) u / 65535.0f;<br>
+}<br>
+<br>
+static uint16_t<br>
+pack_half_1x16(float x, struct func_options *func_opts)<br>
+{<br>
+       /* The bit layout of a float16 is:<br>
+        *   sign: 15<br>
+        *   exponent: 10:14<br>
+        *   mantissa: 0:9<br>
+        *<br>
+        * The sign, exponent, and mantissa of a float16 determine its value<br>
+        * thus:<br>
+        *<br>
+        *  if e = 0 and m = 0, then zero:       (-1)^s * 0<br>
+        *  if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10<br>
+        *  if 0 < e < 31, then normal:          (-1)^s * 2^(e - 15) * (1 + m / 2^10)<br>
+        *  if e = 31 and m = 0, then inf:       (-1)^s * inf<br>
+        *  if e = 31 and m != 0, then nan<br>
+        *<br>
+        *  where 0 <= m < 2^10 .<br>
+        */<br>
+<br>
+       /* Calculate the resultant float16's sign, exponent, and mantissa<br>
+        * bits.<br>
+        */<br>
+       const int s = (copysignf(1.0f, x) < 0) ? 1 : 0;<br>
+       int e;<br>
+       int m;<br>
+<br>
+       switch (fpclassify(x)) {<br>
+       case FP_NAN:<br>
+               return 0xffffu;<br>
+       case FP_INFINITE:<br>
+               e = 31;<br>
+               m = 0;<br>
+               break;<br>
+       case FP_SUBNORMAL:<br>
+       case FP_ZERO:<br>
+               e = 0;<br>
+               m = 0;<br>
+               break;<br>
+       case FP_NORMAL: {<br>
+               /* Recall that the form of subnormal and normal float16 values<br>
+                * are<br>
+                *<br>
+                *   subnormal: 2^(e - 14) * m / 2^10 where e = 0<br>
+                *   normal: 2^(e - 15) * (1 + m / 2^10) where 1 <= e <= 30<br>
+                *<br>
+                * where 0 <= m < 2^10. Therefore some key boundary values of<br>
+                * float16 are:<br>
+                *<br>
+                *   min_subnormal = 2^(-14) * 1 / 2^10<br>
+                *   max_subnormal = 2^(-14) * 1023 / 2^10<br>
+                *   min_normal    = 2^(1-15) * (1 + 0 / 2^10)<br>
+                *   max_normal    = 2^(30 - 15) * (1 + 1023 / 2^10)<br>
+                *<br>
+                * Representing the same boundary values in the form returned<br>
+                * by frexpf(), 2^e * f where 0.5 <= f < 1, gives<br>
+                *<br>
+                *   min_subnormal = 2^(-14) * 1 / 2^10<br>
+                *                 = 2^(-23) * 1 / 2<br>
+                *                 = 2^(-23) * 0.5<br>
+                *<br>
+                *   max_subnormal = 2^(-14) * 1023 / 2^10<br>
+                *                 = 2^(-14) * 0.9990234375<br>
+                *<br>
+                *   min_normal    = 2^(1 - 15) * (1 + 0 / 2^10)<br>
+                *                 = 2^(-14)<br>
+                *                 = 2^(-13) * 0.5<br>
+                *<br>
+                *   max_normal    = 2^(30 - 15) * (1 + 1023 / 2^10)<br>
+                *                 = 2^15 * (2^10 + 1023) / 2^10<br>
+                *                 = 2^16 * (2^10 + 1023) / 2^11<br>
+                *                 = 2^16 * 0.99951171875<br>
+                */<br>
+<br>
+               /* Represent the absolute value of the input in form 2^E * F<br>
+                * where 0.5 <= F < 1.<br>
+                */<br>
+               int E;<br>
+               float F;<br>
+<br>
+               F = frexpf(fabs(x), &E);<br>
+<br>
+               /* Now calculate the results's exponent and mantissa by<br>
+                * comparing the input against the boundary values above.<br>
+                */<br>
+               if (E == -23 && F < 0.5f) {<br></blockquote><div><br></div><div>The documentation for frexpf() claims that when x is nonzero, F will always be in the range [0.5, 1), so it seems to me that this branch will never be taken.  Perhaps maybe you meant "if (E < -23)"?<br>
<br></div><div>A further problem is that if 0.5 * min_subnormal < abs(x) < 1.5 * min_subnormal, then by all rights x should be converted to min_subnormal.  I think we should drop this branch entirely and let the subnormal branch below handle it.  Zero isn't special--it falls neatly out of the formula for subnormal numbers--so we don't need a separate case to handle it.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                       /* The float32 input is too small to be represented as<br>
+                        * a float16. The result is zero.<br>
+                        */<br>
+                       e = 0;<br>
+                       m = 0;<br>
+               } else if (E < -13 || (E == -13 && F < 0.5f)) {<br></blockquote><div><br></div><div>As above, F will never be < 0.5.  Assuming we combine this branch with the one above, this if test should turn into simply "if (E < -13)".<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                       /* The resultant float16 value is subnormal. Let's<br>
+                        * calculate m.<br>
+                        *<br>
+                        *   2^E * F = 2^(-14) * m / 2^10<br>
+                        *         m = 2^(E + 24) * F<br>
+                        */<br>
+                       e = 0;<br>
+                       m = powf(2, E + 24) * F;<br></blockquote><div><br></div><div>This discards bits that should be rounded.  For example, if x == 5.75*2^-24, this should be represented as a subnormal value with m=6, not 5.  But E=-21 and F=23/32, so powf(2, E + 24) * F == 5.75, which turns into a 5 when stored in m (which is an int).  I think what we want here is:<br>


<br></div><div>m = round_to_even(powf(2, E + 24) * F);<br><br></div><div>Note that if 1023.5 <= powf(2, E + 24) * F < 1024.0, this will push m out of range; we need to detect that case and set e = 1, m = 0.  (This is an important edge case that we need to make sure the implementation gets right too!)  I think we can handle this (and other cases of m going out of range) with a single if test just before the "break" statement--see below.<br>

</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+               } else if (E < 16 || (E == 16 && F <= 0.99951171875f)) {<br></blockquote><div><br></div><div>Thanks to the m = 1024 trick below, we should be able to change this to "else if (E <= 16)".<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                       /* The resultant float16 is normal. Let's calculate<br>
+                        * e and m.<br>
+                        *<br>
+                        *   2^E * F = 2^(e - 15) * (1 + m / 2^10)          (1)<br>
+                        *           = 2^(e - 15) * (2^10 + m) / 2^10       (2)<br>
+                        *           = 2^(e - 14) * (2^10 + m) / 2^11       (3)<br>
+                        *<br>
+                        * Substituting<br>
+                        *<br>
+                        *   e1 := E                                        (4)<br>
+                        *   f1 := F                                        (5) <br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                        *   e2 := e - 14                                   (6)<br>
+                        *   f2 := (2^10 + m) / 2^11                        (7)<br>
+                        *<br>
+                        * transforms the equation to<br>
+                        *<br>
+                        *   2^e1 * f1 = 2^e2 * f2                          (8)<br>
+                        *<br>
+                        * By definition, f1 lies in the range [0.5, 1). By<br>
+                        * equation 7, f2 lies there also. This observation<br>
+                        * combined with equation 8 implies f1 = f2, which in<br>
+                        * turn implies e1 = e2. Therefore<br>
+                        *<br>
+                        *   e = E + 14<br>
+                        *   m = 2^11 * F - 2^10<br></blockquote><div><br></div><div>I found this math hard to follow.  Consider something like this instead:<br><br></div><div>The resultant float16 is normal.  Let's calculate e and m.<br>
<br></div><div>2 ^ E * F = 2^(e - 15) * (1 + m / 2^10)<br></div><div>  = 2^(e - 15) * (2^10 + m) / 2^10<br></div><div>  = 2^(e - 14) * (2^10 + m) / 2^11<br><br>This equation is trivially satisfied if<br><br></div><div>E = e - 14<br>
</div><div>F = (2^10 + m) / 2^11<br><br></div><div>Solving for e and m,<br><br></div><div>e = E + 14<br></div><div>m = 2^11 * F - 2^10<br><br></div><div>Since F is guaranteed to be in the range [0.5, 1), m is guaranteed to be in the range [0, 2^10), so this constitutes a valid float16.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+                        */<br>
+                       e = E + 14;<br>
+                       m = powf(2, 11) * F - powf(2, 10);<br></blockquote><div><br></div><div>We need to deal with the same sort of rounding issue mentioned above.  I think we can do so with<br><br>m = round_to_even(powf(2, 11) * F - powf(2, 10));<br>
<br></div><div>Again, we need to worry about what happens if this pushes m to 1024.  See below.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+               } else {<br>
+                       /* The float32 input is too large to represent as a<br>
+                        * float16. The result is infinite.<br>
+                        */<br>
+                       e = 31;<br>
+                       m = 0;<br>
+               }<br>
+               break;<br></blockquote><div><br></div><div>At this point it should be trivial to handle the m = 1024 case with:<br><br></div><div>if (m == 1024) {<br></div><div>   m = 0;<br></div><div>   ++e;<br>}<br><br>
</div><div>(Note that this rounds up max_subnormal + epsilon to min_normal, and max_normal + epsilon to infinity, both of which are correct behaviours).<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+       }<br>
+       default:<br>
+               assert(0);<br>
+               break;<br>
+       }<br>
+<br>
+       assert(s == 0 || s == 1);<br>
+       assert(0 <= e && e <= 31);<br>
+       assert(0 <= m && m <= 1023);<br>
+<br>
+       return (s << 15) | (e << 10) | m;<br>
+}<br>
+<br>
+static void<br>
+unpack_half_1x16(uint16_t u, float *f, struct func_options *func_opts)<br>
+{<br>
+       /* The bit layout of a float16 is:<br>
+        *   sign: 15<br>
+        *   exponent: 10:14<br>
+        *   mantissa: 0:9<br>
+        *<br>
+        * The sign, exponent, and mantissa of a float16 determine its value<br>
+        * thus:<br>
+        *<br>
+        *  if e = 0 and m = 0, then zero:       (-1)^s * 0<br>
+        *  if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10<br>
+        *  if 0 < e < 31, then normal:          (-1)^s * 2^(e - 15) * (1 + m / 2^10)<br>
+        *  if e = 31 and m = 0, then inf:       (-1)^s * inf<br>
+        *  if e = 31 and m != 0, then nan<br>
+        *<br>
+        *  where 0 <= m < 2^10 .<br>
+        */<br>
+<br>
+       int s = (u >> 15) & 0x1;<br>
+       int e = (u >> 10) & 0x1f;<br>
+       int m = u & 0x3ff;<br>
+<br>
+       float sign = s ? -1 : 1;<br>
+<br>
+       if (e == 0) {<br>
+               *f = sign * pow(2, -24) * m;<br>
+       } else if (1 <= e && e <= 30) {<br>
+               *f = sign * pow(2, e - 15) * (1.0 + m / 1024.0);<br>
+       } else if (e == 31 && m == 0) {<br>
+               *f = sign * INFINITY;<br>
+       } else if (e == 31 && m != 0) {<br>
+               *f = NAN;<br>
+       } else {<br>
+               assert(0);<br>
+       }<br>
+}<br></blockquote><div><br></div><div>I'm not finished reviewing this patch yet--I'll try to send more comments as soon as I can.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+<br>
+struct round_func_key {<br>
+       const char *name;<br>
+       round_func_t func;<br>
+};<br>
+<br>
+static struct round_func_key round_func_list[] = {<br>
+       {"round_to_even",           round_to_even},<br>
+       {"round_to_nearest",        round_to_nearest},<br>
+       {0,                         0},<br>
+};<br>
+<br>
+struct pack_func_2x16_key {<br>
+       const char *name;<br>
+       pack_1x16_func_t func;<br>
+};<br>
+<br>
+static struct pack_func_2x16_key pack_func_2x16_list[] = {<br>
+       {"packSnorm2x16",           pack_snorm_1x16},<br>
+       {"packUnorm2x16",           pack_unorm_1x16},<br>
+       {"packHalf2x16",            pack_half_1x16},<br>
+       {0,                         0},<br>
+};<br>
+<br>
+struct unpack_2x16_func_key {<br>
+       const char *name;<br>
+       unpack_1x16_func_t func;<br>
+};<br>
+<br>
+static struct unpack_2x16_func_key unpack_2x16_func_list[] = {<br>
+       {"unpackSnorm2x16",         unpack_snorm_1x16},<br>
+       {"unpackUnorm2x16",         unpack_unorm_1x16},<br>
+       {"unpackHalf2x16",          unpack_half_1x16},<br>
+       {0,                         0},<br>
+};<br>
+<br>
+struct pack_2x16_args {<br>
+       pack_1x16_func_t pack_func;<br>
+       float x;<br>
+       float y;<br>
+       struct func_options func_opts;<br>
+};<br>
+<br>
+struct unpack_2x16_args {<br>
+       unpack_1x16_func_t unpack_func;<br>
+       uint32_t u;<br>
+       struct func_options func_opts;<br>
+};<br>
+<br>
+struct args {<br>
+       enum {<br>
+               ARG_HELP,<br>
+               ARG_PACK_2x16,<br>
+               ARG_UNPACK_2x16,<br>
+               ARG_PRINT_FLOAT16_INFO,<br>
+       } tag;<br>
+<br>
+       union {<br>
+               struct pack_2x16_args pack_2x16;<br>
+               struct unpack_2x16_args unpack_2x16;<br>
+       };<br>
+};<br>
+<br>
+static void<br>
+usage_error(const char *fmt, ...)<br>
+{<br>
+       printf("usage error");<br>
+       if (fmt && strcmp(fmt, "") != 0) {<br>
+               va_list va;<br>
+               va_start(va, fmt);<br>
+               printf(": ");<br>
+               vprintf(fmt, va);<br>
+               va_end(va);<br>
+       }<br>
+       printf("\n");<br>
+       printf("for help, call `glsl-packing -h`\n");<br>
+       exit(1);<br>
+}<br>
+<br>
+static void<br>
+parse_func_opts(struct func_options *func_opts,<br>
+                const char *command_name,<br>
+                int argc, char **argv)<br>
+{<br>
+       int i;<br>
+<br>
+       assert(strcmp(command_name, "print-float16-info") != 0);<br>
+<br>
+       func_options_init(func_opts);<br>
+<br>
+       for (i = 0; i < argc; ++i) {<br>
+               const char *arg = argv[i];<br>
+<br>
+               if (strcmp(arg, "flush_float16") == 0) {<br>
+                       /* flush_float16 implies flush_float32. */<br>
+                       func_opts->flush_float16 = true;<br>
+                       func_opts->flush_float32 = true;<br>
+                       continue;<br>
+               } else if (strcmp(arg, "flush_float32") == 0) {<br>
+                       func_opts->flush_float32 = true;<br>
+                       continue;<br>
+               } else {<br>
+                       /* Assume the arg is a rounding options. */<br>
+                       round_func_t round_func = NULL;<br>
+                       int i;<br>
+<br>
+                       for (i = 0; round_func_list[i].name != NULL; ++i) {<br>
+                               if (strcmp(arg, round_func_list[i].name) == 0) {<br>
+                                       round_func = round_func_list[i].func;<br>
+                                       break;<br>
+                               }<br>
+                       }<br>
+<br>
+                       if (round_func) {<br>
+                               if (func_opts->round) {<br>
+                                       usage_error("multiple rounding "<br>
+                                                   "options were given");<br>
+                               }<br>
+<br>
+                               func_opts->round = round_func;<br>
+                               continue;<br>
+                       }<br>
+               }<br>
+<br>
+               usage_error("unrecognized option: %s", arg);<br>
+       }<br>
+<br>
+       if (func_opts->round != NULL<br>
+           && (strncmp(command_name, "packHalf", 8) == 0 ||<br>
+               strncmp(command_name, "unpackHalf", 10) == 0)) {<br>
+          usage_error("Half functions do not accept any rounding options");<br>
+       }<br>
+<br>
+       if (!func_opts->round ) {<br>
+               /* default */<br>
+               func_opts->round = round_to_even;<br>
+       }<br>
+}<br>
+<br>
+static bool<br>
+parse_pack_2x16_args(struct pack_2x16_args *args, int argc, char **argv)<br>
+{<br>
+       int i;<br>
+       char *endptr;<br>
+       const char *func_name = NULL;<br>
+<br>
+       memset(args, 0, sizeof(*args));<br>
+<br>
+       /* Parse function name. */<br>
+       if (argc < 2)<br>
+               return false;<br>
+<br>
+       for (i = 0; (func_name = pack_func_2x16_list[i].name) != NULL; ++i) {<br>
+               if (strcmp(argv[1], func_name) == 0) {<br>
+                       args->pack_func = pack_func_2x16_list[i].func;<br>
+                       break;<br>
+               }<br>
+       }<br>
+<br>
+       if (args->pack_func == NULL) {<br>
+               /* Failed to parse function name. */<br>
+               return false;<br>
+       }<br>
+<br>
+       /* Parse inputs to packing function. */<br>
+       if (argc < 4)<br>
+               usage_error("not enough inputs for pack function");<br>
+<br>
+       args->x = strtof(argv[2], &endptr);<br>
+       if (endptr == argv[2])<br>
+               usage_error("unable parse input to pack function: %s", argv[2]);<br>
+<br>
+       args->y = strtof(argv[3], &endptr);<br>
+       if (endptr == argv[3])<br>
+               usage_error("unable parse input to pack function: %s", argv[3]);<br>
+<br>
+       parse_func_opts(&args->func_opts, func_name, argc - 4, argv + 4);<br>
+       return true;<br>
+}<br>
+<br>
+static bool<br>
+parse_unpack_2x16_args(struct unpack_2x16_args *args, int argc, char **argv)<br>
+{<br>
+       int i;<br>
+        const char *func_name = NULL;<br>
+<br>
+       memset(args, 0, sizeof(*args));<br>
+<br>
+       /* Parse function name. */<br>
+       if (argc < 2)<br>
+               return false;<br>
+<br>
+       for (i = 0; (func_name = unpack_2x16_func_list[i].name) != NULL; ++i) {<br>
+               if (strcmp(argv[1], func_name) == 0) {<br>
+                       args->unpack_func = unpack_2x16_func_list[i].func;<br>
+                       break;<br>
+               }<br>
+       }<br>
+<br>
+       if (args->unpack_func == NULL) {<br>
+               /* Failed to parse function name. */<br>
+               return false;<br>
+       }<br>
+<br>
+       /* Parse inputs to unpacking function. */<br>
+       if (argc < 3)<br>
+               usage_error("not enough inputs for unpack function");<br>
+       if (sscanf(argv[2],"%u", &args->u) != 1)<br>
+               usage_error("unable to parse input to unpack function: %s", argv[2]);<br>
+<br>
+       parse_func_opts(&args->func_opts, func_name, argc - 3, argv + 3);<br>
+       return true;<br>
+}<br>
+<br>
+static void<br>
+parse_args(struct args *args, int argc, char **argv)<br>
+{<br>
+       memset(args, 0, sizeof(*args));<br>
+<br>
+       if (argc < 2) {<br>
+               usage_error("no command was given");<br>
+       }<br>
+<br>
+       if (strcmp(argv[1], "-h") == 0 ||<br>
+           strcmp(argv[1], "--help") == 0) {<br>
+          args->tag = ARG_HELP;<br>
+          return;<br>
+       }<br>
+<br>
+       if (parse_pack_2x16_args(&args->pack_2x16, argc, argv)) {<br>
+               args->tag = ARG_PACK_2x16;<br>
+               return;<br>
+       }<br>
+<br>
+       if (parse_unpack_2x16_args(&args->unpack_2x16, argc, argv)) {<br>
+               args->tag = ARG_UNPACK_2x16;<br>
+               return;<br>
+       }<br>
+<br>
+       if (strcmp(argv[1], "print-float16-info") == 0) {<br>
+               if (argc > 2) {<br>
+                       usage_error("print-float16-info takes no args");<br>
+               }<br>
+               args->tag = ARG_PRINT_FLOAT16_INFO;<br>
+               return;<br>
+       };<br>
+<br>
+       usage_error("unrecognized command: %s", argv[1]);<br>
+}<br>
+<br>
+static void<br>
+cmd_pack_2x16(struct pack_2x16_args *args)<br>
+{<br>
+       uint32_t u = pack_2x16(args->pack_func,<br>
+                              args->x, args->y,<br>
+                              &args->func_opts);<br>
+       printf("%u\n", u);<br>
+}<br>
+<br>
+static void<br>
+cmd_unpack_2x16(struct unpack_2x16_args *args)<br>
+{<br>
+       float x;<br>
+       float y;<br>
+<br>
+       unpack_2x16(args->unpack_func,<br>
+                   args->u, &x, &y,<br>
+                   &args->func_opts);<br>
+       printf(PRIf32 " " PRIf32 "\n", x, y);<br>
+}<br>
+<br>
+static void<br>
+print_float16_value(const char *name, int e, int m)<br>
+{<br>
+       struct func_options func_opts;<br>
+       int s = 0;<br>
+       float f;<br>
+<br>
+       func_options_init(&func_opts);<br>
+       unpack_half_1x16((s << 15) | (e << 10) | m, &f, &func_opts);<br>
+       printf("%s: " PRIf32 "\n", name, f);<br>
+<br>
+}<br>
+<br>
+static void<br>
+print_float16_step(const char *name, int exp)<br>
+{<br>
+       printf("%s: " PRIf32 "\n", name, powf(2, exp));<br>
+}<br>
+<br>
+static void<br>
+cmd_print_float16_info(void)<br>
+{<br>
+       print_float16_value("subnormal_min", 0, 1);<br>
+       print_float16_value("subnormal_max", 0, 1023);<br>
+       print_float16_value("normal_min", 1, 0);<br>
+       print_float16_value("normal_max", 30, 1023);<br>
+       print_float16_step("min_step", -14 - 10);<br>
+       print_float16_step("max_step", 15 - 10);<br>
+}<br>
+<br>
+static void<br>
+exec_args(struct args *args)<br>
+{<br>
+       switch (args->tag) {<br>
+       case ARG_HELP:<br>
+               printf("%s", help_text);<br>
+               break;<br>
+       case ARG_PACK_2x16:<br>
+               cmd_pack_2x16(&args->pack_2x16);<br>
+               break;<br>
+       case ARG_UNPACK_2x16:<br>
+               cmd_unpack_2x16(&args->unpack_2x16);<br>
+               break;<br>
+       case ARG_PRINT_FLOAT16_INFO:<br>
+               cmd_print_float16_info();<br>
+               break;<br>
+       default:<br>
+               assert(0);<br>
+               break;<br>
+       }<br>
+}<br>
+<br>
+int<br>
+main(int argc, char **argv)<br>
+{<br>
+       struct args args;<br>
+<br>
+       parse_args(&args, argc, argv);<br>
+       exec_args(&args);<br>
+<br>
+       return 0;<br>
+}<br>
<span><font color="#888888">--<br>
1.8.1<br>
<br>
_______________________________________________<br>
Piglit mailing list<br>
<a href="mailto:Piglit@lists.freedesktop.org" target="_blank">Piglit@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/piglit" target="_blank">http://lists.freedesktop.org/mailman/listinfo/piglit</a><br>
</font></span></blockquote></div><br></div></div>