[Piglit] [PATCH 2/2] glsl-es-3.00: Generate tests for builtin packing functions

Chad Versace chad.versace at linux.intel.com
Wed Jan 9 23:58:04 PST 2013


for stage in const vs fs:
  for d in pack unpack:
    for type in Snorm Unorm Half:
      generate ${stage}-${d}${type}2x16.shader_test

The tests are generated by a new Python script,
gen_builtin_packing_tests.py, and placed into directory
spec/glsl-es-3.00/execution/built-in-functions.

Signed-off-by: Chad Versace <chad.versace at linux.intel.com>
---
 generated_tests/CMakeLists.txt               |   7 +-
 generated_tests/gen_builtin_packing_tests.py | 899 +++++++++++++++++++++++++++
 tests/util/glsl-packing.c                    |  18 +-
 3 files changed, 912 insertions(+), 12 deletions(-)
 create mode 100644 generated_tests/gen_builtin_packing_tests.py

diff --git a/generated_tests/CMakeLists.txt b/generated_tests/CMakeLists.txt
index e371ff8..3ee6d56 100644
--- a/generated_tests/CMakeLists.txt
+++ b/generated_tests/CMakeLists.txt
@@ -20,6 +20,10 @@ endfunction(piglit_make_generated_tests custom_target generator_script)
 
 # Create custom commands and targets to build generated tests.
 piglit_make_generated_tests(
+	builtin_packing_tests.list
+	gen_builtin_packing_tests.py
+        glsl-packing)
+piglit_make_generated_tests(
 	builtin_uniform_tests.list
 	gen_builtin_uniform_tests.py
 	builtin_function.py)
@@ -49,7 +53,8 @@ piglit_make_generated_tests(
 # Add a "gen-tests" target that can be used to generate all the
 # tests without doing any other compilation.
 add_custom_target(gen-tests ALL
-	DEPENDS builtin_uniform_tests.list
+	DEPENDS builtin_packing_tests.list
+		builtin_uniform_tests.list
 		constant_array_size_tests.list
 		interpolation_tests.list
 		non-lvalue_tests.list
diff --git a/generated_tests/gen_builtin_packing_tests.py b/generated_tests/gen_builtin_packing_tests.py
new file mode 100644
index 0000000..1711685
--- /dev/null
+++ b/generated_tests/gen_builtin_packing_tests.py
@@ -0,0 +1,899 @@
+#!/usr/bin/env python2
+
+import mako.template
+import mako.runtime
+import math
+import optparse
+import os
+import sys
+import textwrap as tw
+
+from collections import namedtuple
+from mako.template import Template
+from subprocess import check_output
+
+# ----------------------------------------------------------------------------
+# Overview
+# ----------------------------------------------------------------------------
+
+# This scripts generates tests for the GLSL packing functions, such as
+# packSnorm2x16.
+
+# Given an input and a packing/unpacking function, there exist multiple
+# possible outputs. The actual output is dependent on the GLSL compiler's and
+# hardware's choice of rounding mode (to even or to nearest) and handling of
+# subnormal (also called denormalized) floating point numbers. In each
+# generated test, the function's actual output is compared against
+# a reasonable subset set of possible outputs.
+
+# For details on how the expected outputs are calculated, see the source of the
+# glsl-packing tool.
+
+# ----------------------------------------------------------------------------
+# Templates
+# ----------------------------------------------------------------------------
+
+# Test evaluation of constant pack2x16 expressions.
+const_pack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    in vec4 vertex;
+    out vec4 vert_color;
+
+    void main()
+    {
+        ${func.return_precision} uint actual;
+
+        gl_Position = vertex;
+
+        % for io in func.inouts:
+        actual = ${func.name}(vec2(${io.input[0]},
+                                   ${io.input[1]}));
+
+        if (true
+            % for u in io.possible_outputs:
+            && actual != ${u}
+            % endfor
+           ) {
+            vert_color = red;
+            return;
+        }
+
+        % endfor
+
+        vert_color = green;
+    }
+
+    [fragment shader]
+    in vec4 vert_color;
+    out vec4 frag_color;
+
+    void main()
+    {
+        frag_color = vert_color;
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+"""))
+
+# Test evaluation of constant unpack2x16 expressions.
+const_unpack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    in vec4 vertex;
+    out vec4 vert_color;
+
+    void main()
+    {
+        ${func.return_precision} vec2 actual;
+
+        gl_Position = vertex;
+
+        % for io in func.inouts:
+        actual = ${func.name}(${io.input});
+
+        if (true
+            % for v in io.possible_outputs:
+            && actual != vec2(${v[0]}, ${v[1]})
+            % endfor
+           ) {
+            vert_color = red;
+            return;
+        }
+
+        % endfor
+
+        vert_color = green;
+    }
+
+    [fragment shader]
+    in vec4 vert_color;
+    out vec4 frag_color;
+
+    void main()
+    {
+        frag_color = vert_color;
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+"""))
+
+# Test execution of pack2x16 functions in the vertex shader.
+vs_pack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    uniform vec2 func_input;
+
+    % for j in range(func.num_possible_outputs):
+    uniform ${func.return_precision} uint expect${j};
+    % endfor
+
+    in vec4 vertex;
+    out vec4 vert_color;
+
+    void main()
+    {
+        gl_Position = vertex;
+        ${func.return_precision} uint actual = ${func.name}(func_input);
+
+        if (false
+            % for j in range(func.num_possible_outputs):
+            || actual == expect${j}
+            % endfor
+           ) {
+           vert_color = green;
+        } else {
+            vert_color = red;
+        }
+    }
+
+    [fragment shader]
+    in vec4 vert_color;
+    out vec4 frag_color;
+
+    void main()
+    {
+        frag_color = vert_color;
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    % for io in func.inouts:
+    uniform vec2 func_input ${io.input[0]} ${io.input[1]}
+    % for j in range(func.num_possible_outputs):
+    uniform uint expect${j} ${io.possible_outputs[j]}
+    % endfor
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+
+    % endfor
+"""))
+
+# Test execution of unpack2x16 functions in the vertex shader.
+vs_unpack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    uniform highp uint func_input;
+
+    % for j in range(func.num_possible_outputs):
+    uniform ${func.return_precision} vec2 expect${j};
+    % endfor
+
+    in vec4 vertex;
+    out vec4 vert_color;
+
+    void main()
+    {
+        gl_Position = vertex;
+
+        ${func.return_precision} vec2 actual = ${func.name}(func_input);
+
+        if (false
+            % for j in range(func.num_possible_outputs):
+            || actual == expect${j}
+            % endfor
+           ) {
+            vert_color = green;
+        } else {
+            vert_color = red;
+        }
+    }
+
+    [fragment shader]
+    in vec4 vert_color;
+    out vec4 frag_color;
+
+    void main()
+    {
+        frag_color = vert_color;
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    % for io in func.inouts:
+    uniform uint func_input ${io.input}
+    % for j in range(func.num_possible_outputs):
+    uniform vec2 expect${j} ${io.possible_outputs[j][0]} ${io.possible_outputs[j][1]}
+    % endfor
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+
+    % endfor
+"""))
+
+
+# Test execution of pack2x16 functions in the fragment shader.
+fs_pack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    in vec4 vertex;
+
+    void main()
+    {
+        gl_Position = vertex;
+    }
+
+    [fragment shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    uniform vec2 func_input;
+
+    % for i in range(func.num_possible_outputs):
+    uniform ${func.return_precision} uint expect${i};
+    % endfor
+
+    out vec4 frag_color;
+
+    void main()
+    {
+        ${func.return_precision} uint actual = ${func.name}(func_input);
+
+        if (false
+            % for i in range(func.num_possible_outputs):
+            || actual == expect${i}
+            % endfor
+           ) {
+            frag_color = green;
+        } else {
+            frag_color = red;
+        }
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    % for io in func.inouts:
+    uniform vec2 func_input ${io.input[0]} ${io.input[1]}
+    % for i in range(func.num_possible_outputs):
+    uniform uint expect${i} ${io.possible_outputs[i]}
+    % endfor
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+
+    % endfor
+"""))
+
+# Test execution of unpack2x16 functions in the fragment shader.
+fs_unpack_2x16_template = Template(tw.dedent("""\
+    [require]
+    GL >= 3.0 es
+    GLSL >= 3.00 es
+
+    [vertex shader]
+    in vec4 vertex;
+
+    void main()
+    {
+        gl_Position = vertex;
+    }
+
+    [fragment shader]
+    const vec4 red = vec4(1, 0, 0, 1);
+    const vec4 green = vec4(0, 1, 0, 1);
+
+    uniform highp uint func_input;
+
+    % for i in range(func.num_possible_outputs):
+    uniform ${func.return_precision} vec2 expect${i};
+    % endfor
+
+    out vec4 frag_color;
+
+    void main()
+    {
+        ${func.return_precision} vec2 actual = ${func.name}(func_input);
+
+        if (false
+            % for i in range(func.num_possible_outputs):
+            || actual == expect${i}
+            % endfor
+           ) {
+            frag_color = green;
+        } else {
+            frag_color = red;
+        }
+    }
+
+    [vertex data]
+    vertex/float/2
+    -1.0 -1.0
+     1.0 -1.0
+     1.0  1.0
+    -1.0  1.0
+
+    [test]
+    % for io in func.inouts:
+    uniform uint func_input ${io.input}
+    % for i in range(func.num_possible_outputs):
+    uniform vec2 expect${i} ${io.possible_outputs[i][0]} ${io.possible_outputs[i][1]}
+    % endfor
+    draw arrays GL_TRIANGLE_FAN 0 4
+    probe all rgba 0.0 1.0 0.0 1.0
+
+    % endfor
+"""))
+
+template_table = {
+    ("const", "p", "2x16") : const_pack_2x16_template,
+    ("const", "u", "2x16") : const_unpack_2x16_template,
+    ("vs",    "p", "2x16") : vs_pack_2x16_template,
+    ("vs",    "u", "2x16") : vs_unpack_2x16_template,
+    ("fs",    "p", "2x16") : fs_pack_2x16_template,
+    ("fs",    "u", "2x16") : fs_unpack_2x16_template,
+}
+
+# ----------------------------------------------------------------------------
+# Function inputs and expected outputs
+# ----------------------------------------------------------------------------
+
+inout_tuple = namedtuple("inout_tuple", ("input", "possible_outputs"))
+
+glsl_packing_exe_path = None
+
+def set_glsl_packing_exe_path():
+    global glsl_packing_exe_path
+
+    if glsl_packing_exe_path is not None:
+        return glsl_packing_exe_path
+
+    if "PIGLIT_BUILD_DIR" in os.environ:
+        piglit_build_dir = os.path.join(os.environ["PIGLIT_BUILD_DIR"])
+    else:
+        piglit_build_dir = os.path.join(os.path.dirname(__file__), os.pardir)
+
+    glsl_packing_exe_path = os.path.join(piglit_build_dir,
+                                           "bin",
+                                           "glsl-packing")
+
+    if not os.path.exists(glsl_packing_exe_path):
+        print(("error: file {0!r} does not exist. maybe you forgot to " + \
+               "build it or forgot to define environment var " + \
+              "PIGLIT_BUILD_DIR.").format(glsl_packing_exe_path))
+        sys.exit(1)
+
+    return glsl_packing_exe_path
+
+def glsl_literal(x):
+    """Represent x as a string suitable as a GLSL literal.
+
+    x must be an int or float.
+
+    If x is an integer, then it is assumed to be unsigned 32-bit integer. For
+    example, glsl_literal(1) returns "1u" and glsl_literal(-1) returns
+    "0xffffffffu".
+    """
+    if type(x) == int:
+        # The packing functions are concerned only with unsigned integers.
+        return "{0}u".format(x % 2**32)
+    elif type(x) == float:
+        if math.isnan(x):
+            # GLSL ES 3.00 and GLSL 4.10 do not require implementations to
+            # support NaN, so we do not test it.
+            assert(False)
+        elif math.isinf(x):
+            # GLSL ES 3.00 lacks a literal for infinity. However, a literal
+            # value which is too large to stored as a float will be converted to
+            # infintiy. From page 31 of the GLSL ES 3.00 spec:
+            #
+            #   If the value of the floating point number is too large (small)
+            #   to be stored as a single precision value, it is converted to
+            #   positive (negative) infinity.
+            #
+            return repr(math.copysign(1.0e256, x))
+        else:
+            return repr(x)
+    else:
+        assert(False)
+
+class PackUnpackFunc(object):
+
+    def __init__(self, name, dimension):
+        assert(dimension in ["2x16", "4x8"])
+
+        self.__name = name
+        self.__dimension = dimension
+
+    @property
+    def name(self):
+        return self.__name
+
+    @property
+    def dimension(self):
+        return self.__dimension
+
+    @property
+    def num_possible_outputs(self):
+        return len(self.inouts[0])
+
+class Pack2x16Func(PackUnpackFunc):
+
+    def __init__(self, name):
+        PackUnpackFunc.__init__(self, name=name, dimension="2x16")
+        self.__inouts = None
+
+    @property
+    def return_precision(self):
+        return "highp"
+
+    @property
+    def inouts(self):
+        if self.__inouts is not None:
+            return self.__inouts
+
+        def get_expected_output(x, y, func_opts):
+            args = [glsl_packing_exe_path, self.name, repr(x), repr(y)]
+            args.extend(func_opts)
+            return glsl_literal(int(check_output(args)))
+
+        self.__inouts = []
+
+        for y in self.float_inputs:
+            yl = glsl_literal(y)
+
+            for x in self.float_inputs:
+                xl = glsl_literal(x)
+                input = (xl, yl)
+
+                possible_outputs = []
+                for func_opts in self.func_opts_seq:
+                    possible_outputs.append(get_expected_output(x, y, func_opts))
+
+                self.__inouts.append(
+                    inout_tuple(
+                        input=(xl, yl),
+                        possible_outputs=possible_outputs))
+
+        return self.__inouts
+
+class Unpack2x16Func(PackUnpackFunc):
+
+    def __init__(self, name, return_precision):
+        PackUnpackFunc.__init__(self, name=name, dimension="2x16")
+        self.__return_precision = return_precision
+        self.__inouts = None
+
+    @property
+    def return_precision(self):
+        return self.__return_precision
+
+    @property
+    def func_opts_seq(self):
+        return ((),
+                ("flush_float16",),
+                ("flush_float32",))
+
+    @property
+    def inouts(self):
+        if self.__inouts is not None:
+            return self.__inouts
+
+        def get_expected_output(uint32, func_opts):
+            args = [glsl_packing_exe_path, self.name, repr(uint32)]
+            args.extend(func_opts)
+            out = check_output(args)
+            vec2 = out.strip().split()
+            return (glsl_literal(float(vec2[0])),
+                    glsl_literal(float(vec2[1])))
+
+        self.__inouts = []
+
+        for y in self.uint16_inputs:
+            for x in self.uint16_inputs:
+                    uint32 = (y << 16) | x
+
+                    possible_outputs = []
+                    for func_opts in self.func_opts_seq:
+                        possible_outputs.append(get_expected_output(uint32, func_opts))
+
+                    self.__inouts.append(
+                        inout_tuple(
+                            input=glsl_literal(uint32),
+                            possible_outputs=possible_outputs))
+
+        return self.__inouts
+
+class PackSnorm2x16Func(Pack2x16Func):
+
+    def __init__(self):
+        Pack2x16Func.__init__(self, name="packSnorm2x16")
+        self.__float_inputs = None
+
+    @property
+    def func_opts_seq(self):
+        return (("round_to_even",),
+                ("round_to_even", "flush_float16"),
+                ("round_to_even", "flush_float32"),
+                ("round_to_nearest",),
+                ("round_to_nearest", "flush_float16"),
+                ("round_to_nearest", "flush_float32"))
+    @property
+    def float_inputs(self):
+        if self.__float_inputs is not None:
+            return self.__float_inputs
+
+        # The domain of packSnorm2x16 is [-inf, +inf]^2, yet the function clamps
+        # its input to range [-1, +1]^2.
+        #
+        # Below are listed important classes in the function's domain, and test
+        # inputs chosen from each class.
+        #   - zero: -0.0, 0.0
+        #   - near zero: -0.1, 0.1
+        #   - just inside the clamp range: -0.9, 0.9
+        #   - on the clamp boundary: -1.0, 1.0
+        #   - just outside the clamp range: -1.1, 1.1
+        #   - infinity: -inf, +inf
+        #
+        # We test -0.0 in order to stress the implementation's handling of zero.
+        # The implementation should return a uint16_t that encodes -0.0; that is,
+        # with the sign bit set.
+
+        pos_floats = (0.0, 0.1, 0.9, 1.0, 1.1, float("+inf"))
+        neg_floats = tuple(reversed(tuple((-x for x in pos_floats))))
+        all_floats = pos_floats + neg_floats
+
+        self.__float_inputs = all_floats
+        return self.__float_inputs
+
+class UnpackSnorm2x16Func(Unpack2x16Func):
+
+    def __init__(self):
+        Unpack2x16Func.__init__(
+            self,
+            name="unpackSnorm2x16",
+            return_precision="highp")
+
+    @property
+    def uint16_inputs(self):
+        return (0, 1, 2, 3,
+                2**15 - 1,
+                2**15,
+                2**15 + 1,
+                2**16 - 1, # max uint16
+                )
+
+class PackUnorm2x16Func(Pack2x16Func):
+
+    def __init__(self):
+        Pack2x16Func.__init__(self, name="packUnorm2x16")
+
+    @property
+    def func_opts_seq(self):
+        return (("round_to_even",),
+                ("round_to_even", "flush_float16"),
+                ("round_to_even", "flush_float32"),
+                ("round_to_nearest",),
+                ("round_to_nearest", "flush_float16"),
+                ("round_to_nearest", "flush_float32"))
+
+    @property
+    def float_inputs(self):
+
+        # The domain of packUnorm2x16 is [-inf, +inf]^2, yet the function clamps
+        # its input to range [0, 1]^2.
+        #
+        # Below are listed important classes in the function's domain, and test
+        # inputs chosen from each class.
+        #   - zero: -0.0, 0.0
+        #   - just inside the clamp range: 0.1, 0.9
+        #   - on the clamp boundary: 0.0, 1.0
+        #   - just outside the clamp range: -0.1, 1.1
+        #   - infinity: -inf, +inf
+        #
+        # We test -0.0 in order to stress the implementation's handling of zero.
+        # The implementation should return a uint16_t that encodes +0.0; that is,
+        # without the sign bit set.
+
+        return (float("-inf"),
+                -0.1, -0.0,
+                +0.0, 0.1, 0.9, 1.0, 1.1,
+                float("+inf"))
+
+class UnpackUnorm2x16Func(Unpack2x16Func):
+
+    def __init__(self):
+        Unpack2x16Func.__init__(
+            self,
+            name="unpackUnorm2x16",
+            return_precision="highp")
+
+    @property
+    def uint16_inputs(self):
+        return (0, 1, 2, 3,
+                2**15 - 1,
+                2**15,
+                2**15 + 1,
+                2**16 - 1, # max uint16
+                )
+
+class PackHalf2x16Func(Pack2x16Func):
+
+    def __init__(self):
+        Pack2x16Func.__init__(self, name="packHalf2x16")
+        self.__float_inputs = None
+
+    @property
+    def func_opts_seq(self):
+        return ((),
+                ("flush_float16",),
+                ("flush_float32",))
+
+    @property
+    def float_inputs(self):
+        if self.__float_inputs is not None:
+            return self.__float_inputs
+
+        # The domain of packHalf2x16 is ([-inf, +inf] + {NaN})^2 and the function
+        # does not clamp its input.
+        #
+        # For the min and max value of the two classes of float16 values,
+        # subnormal and normalized, choose the following test inputs:
+        #   - slightly below the minmax
+        #   - the exact minmax value
+        #   - slightly above the minmax
+        #
+        # We also test -0.0 and +0.0 in order to stress the implementation's
+        # handling of zero.
+
+        # Get info from `glsl-packing print-float16-info`.
+        info = dict()
+        output = check_output([glsl_packing_exe_path, "print-float16-info"])
+        output = output.rstrip() # Remove trailing newline
+        lines = output.split("\n")
+        for line in lines:
+            s = line.split(":")
+            name = s[0]
+            value = s[1]
+            info[name] = float(value)
+
+        subnormal_min = info["subnormal_min"]
+        subnormal_max = info["subnormal_max"]
+        normal_min = info["normal_min"]
+        normal_max = info["normal_max"]
+        min_step = info["min_step"]
+        max_step = info["max_step"]
+
+        pos_floats = (
+            # for x = 0;
+            #     x <= normal_min + min_step
+            #     x +=  min_step
+            0.0,
+            subnormal_min,
+            subnormal_min + 1.0 * min_step,
+
+            # for x = subnormal_max - min_step
+            #     x <= normal_min + min_step
+            #     x += min_step
+            subnormal_max - 1.0 * min_step,
+            subnormal_max,
+            normal_min,
+            normal_min + 1.0 * min_step,
+
+            # for x = normal_max - max_step
+            #     x <= normal_max + max_step
+            #     x += max_step
+            normal_max - 1.0 * max_step,
+            normal_max,
+            normal_max + 1.0 * max_step,
+
+            float("inf"),
+            )
+        neg_floats = tuple(reversed(tuple((-x for x in pos_floats))))
+        all_floats = neg_floats + pos_floats
+
+        self.__float_inputs = all_floats
+        return self.__float_inputs
+
+class UnpackHalf2x16Func(Unpack2x16Func):
+
+    def __init__(self):
+        Unpack2x16Func.__init__(
+            self,
+            name="unpackHalf2x16",
+            return_precision="mediump")
+
+        self.__uint16_inputs = None
+
+    @property
+    def uint16_inputs(self):
+        if self.__uint16_inputs is not None:
+            return self.__uint16_inputs
+
+        # Encode a float16 with the given sign, exponent, and mantissa bits into
+        # a uint16.
+        def float16(s, e, m):
+            return (s << 15) | (e << 10) | m
+
+        # For each of the two classes of float16 values, subnormal and normalized,
+        # below are listed the exponent and mantissa of the class's maximum and
+        # minimum values and of some values slightly inside the bounds.
+        bounds = (
+            ( 0,    0), # zero
+
+            ( 0,    1), # subnormal_min
+            ( 0,    2), # subnormal_min + subnormal_step
+
+            ( 0, 1022), # subnormal_max - subnormal_step
+            ( 0, 1023), # subnormal_max
+
+            ( 1,    0), # normal_min
+            ( 1,    1), # normal_min + normal_min_step
+
+            (30, 1022), # normal_max - normal_max_step
+            (30, 1023), # normal_max
+
+            (31,    0), # inf
+        )
+
+        pos_float16 = tuple(float16(0, e, m) for (e, m) in bounds)
+        neg_float16 = tuple(float16(1, e, m) for (e, m) in reversed(bounds))
+        all_float16 = neg_float16 + pos_float16
+
+        self.__uint16_inputs = all_float16
+        return self.__uint16_inputs
+
+class Test(object):
+
+    def __init__(self, func, execution_stage):
+        assert(isinstance(func, PackUnpackFunc))
+        assert(execution_stage in ("const", "vs", "fs"))
+
+        self.__template = template_table[(execution_stage,
+                                          func.name[0],
+                                          func.dimension)]
+        self.__func = func
+        self.__filename = os.path.join(
+                            "spec",
+                            "glsl-es-3.00",
+                            "execution",
+                            "built-in-functions",
+                            "{0}-{1}.shader_test"\
+                            .format(execution_stage, func.name))
+
+    @property
+    def filename(self):
+        return self.__filename
+
+    def write_file(self):
+        dirname = os.path.dirname(self.filename)
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
+
+        with open(self.filename, "w") as buffer:
+            ctx = mako.runtime.Context(buffer, func=self.__func)
+            self.__template.render_context(ctx)
+
+def all_tests_iter():
+    # Format these master lists as one item per line. This allows one to
+    # easily debug by commenting out individual items.
+
+    func_classes = (
+        PackSnorm2x16Func,
+        PackUnorm2x16Func,
+        PackHalf2x16Func,
+        UnpackSnorm2x16Func,
+        UnpackUnorm2x16Func,
+        UnpackHalf2x16Func,
+        )
+
+    execution_stages = (
+        "const",
+        "vs",
+        "fs",
+        )
+
+    for stage in execution_stages:
+        for func in func_classes:
+            yield Test(func(), stage)
+
+def main():
+    set_glsl_packing_exe_path()
+
+    parser = optparse.OptionParser(
+                description="Generate shader tests that test the built-in " + \
+                            "packing functions",
+                usage="usage: %prog [-h] [--names-only]")
+    parser.add_option(
+        '--names-only',
+        dest='names_only',
+        action='store_true',
+        help="Don't output files, just generate a list of filenames to stdout")
+
+    (options, args) = parser.parse_args()
+
+    if len(args) != 0:
+        # User gave extra args.
+        parser.print_help()
+        sys.exit(1)
+
+    for test in all_tests_iter():
+        print(test.filename)
+        sys.stdout.flush()
+
+        if not options.names_only:
+            test.write_file()
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/util/glsl-packing.c b/tests/util/glsl-packing.c
index 317f0d1..d02610a 100644
--- a/tests/util/glsl-packing.c
+++ b/tests/util/glsl-packing.c
@@ -30,10 +30,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-/* TODO: Reject round options for pack/unpackHalf.
- * TODO: Discuss choices in packHalf.
- */
-
 #define PRIf32_PRECISION "24"
 #define PRIf32 "%." PRIf32_PRECISION "f"
 
@@ -82,7 +78,7 @@ static const char *help_text =
 	"FUNC_OPTS\n"
 	"    flush_float16\n"
 	"    flush_float32\n"
-	"        All PACK_FUNC and UNPACK_FUNC commands accept the flush options.\n"
+	"        All PACK_FUNC and UNPACK_FUNC commands accept a flush mode.\n"
 	"\n"
 	"        The GLSL ES 3.00 and GLSL 4.10 specs allows implementations to truncate\n"
 	"        subnormal floats to zero. From section 4.5.1 \"Range and Precision\"\n"
@@ -99,8 +95,8 @@ static const char *help_text =
 	"\n"
 	"    round_to_nearest\n"
 	"    round_to_even\n"
-	"        All PACK_FUNC and UNPACK_FUNC commands except pack/unpackHalf2x16 accept\n"
-	"        the rounding option. At most one rounding option may be specified.\n"
+	"        Only packSnorm and packUnorm commands accept a rounding mode. At most one\n"
+	"        most one rounding mode may be specified.\n"
 	"\n"
 	"        For some packing functions, the GLSL ES 3.00 specification's\n"
 	"        definition of the function's behavior involves the `round()`\n"
@@ -591,10 +587,10 @@ parse_func_opts(struct func_options *func_opts,
 		usage_error("unrecognized option: %s", arg);
 	}
 
-	if (func_opts->round != NULL
-	    && (strncmp(command_name, "packHalf", 8) == 0 ||
-	        strncmp(command_name, "unpackHalf", 10) == 0)) {
-	   usage_error("Half functions do not accept any rounding options");
+	if (func_opts->round != NULL &&
+	    strncmp(command_name, "packSnorm", 9) != 0 &&
+	    strncmp(command_name, "packUnorm", 9) != 0) {
+	   usage_error("only packSnorm and packUnom accept a rounding mode");
 	}
 
 	if (!func_opts->round ) {
-- 
1.8.1



More information about the Piglit mailing list