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

Paul Berry stereotype441 at gmail.com
Fri Jan 11 10:28:57 PST 2013


On 9 January 2013 23:58, Chad Versace <chad.versace at linux.intel.com> wrote:

> 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)
>

Since this script runs at build time, it means that $PIGLIT_BUILD_DIR must
be set both when running piglit and when building piglit.  (Previously, it
was sufficient just to set it when running piglit).

My preference would be to eliminate this requirement if at all possible
(say by having the path to the glsl-packing executable passed into the
script as a command-line argument).  Failing that, we should at least
clearly document both in the commit message and in piglit's toplevel README
that $PIGLIT_BUILD_DIR is now needed both for building and running piglit.


> +
> +    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)
>

This function is trying to do the same job as the glsl_constant() function
in builtin_function.py, but with slightly different semantics (better
handling of infinities, automatic casting of negative integers to uint).
I'm dubious about whether automatic casting of negative integers to uint is
desirable, and as far as I can tell this functionality isn't used.  Could
we instead just add the inf-handling code to builtin_function.py's
glsl_constant() and use that function instead?


> +
> +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])
>

I'm stumbling a lot over self.inouts.  At this point in the code it's
confusing (because it's not obvious that it's a property getter that is
going to be defined in derived classes).  Later it seems unweildy, since
you are going to a lot of effort to compute it lazily.

How about making inouts an argument to PackUnpackFunc.__init__(), like name
and dimension, and then just compute it eagerly in the derived class
constructor before calling PackUnpackFunc.__init__()?  Although self.inouts
is expensive to compute (since it involves shelling out to the GLSL packing
exe), I don't think we save anything by computing it lazily.


> +
> +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
>

Sorry if this is redundant from our conversation yesterday, but I think we
need to test cases that require rounding, especially cases where rounding
up would increase the exponent of the float16, since those are sharp
corners that an implementor might get wrong.


> +
> +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
>

It looks like the hunks below were probably intended to get folded into
patch 1/2.


> @@ -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
>
> _______________________________________________
> Piglit mailing list
> Piglit at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/piglit
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/piglit/attachments/20130111/f0f24a3a/attachment-0001.html>


More information about the Piglit mailing list