[Piglit] [PATCH 2/2] arb_gpu_shader_int64: add builtin test generator.

Dave Airlie airlied at gmail.com
Thu Jun 9 02:50:14 UTC 2016


From: Dave Airlie <airlied at redhat.com>

Like the normal and fp64 generators, this generates a bunch
of the built-in tests for use with shader_runner
---
 generated_tests/CMakeLists.txt                     |    5 +
 generated_tests/builtin_function_int64.py          | 1001 ++++++++++++++++++++
 generated_tests/gen_builtin_uniform_tests_int64.py |  642 +++++++++++++
 3 files changed, 1648 insertions(+)
 create mode 100644 generated_tests/builtin_function_int64.py
 create mode 100644 generated_tests/gen_builtin_uniform_tests_int64.py

diff --git a/generated_tests/CMakeLists.txt b/generated_tests/CMakeLists.txt
index 0b9048f..8422329 100644
--- a/generated_tests/CMakeLists.txt
+++ b/generated_tests/CMakeLists.txt
@@ -196,6 +196,10 @@ piglit_make_generated_tests(
 	templates/gen_vs_in_fp64/execution_base.mako
 	templates/gen_vs_in_fp64/shader.vert.mako
 	templates/gen_vs_in_fp64/shader_base.mako)
+piglit_make_generated_tests(
+	builtin_uniform_tests_int64.list
+	gen_builtin_uniform_tests_int64.py
+	builtin_function_int64.py)
 
 # OpenCL Test generators
 piglit_make_generated_tests(
@@ -247,6 +251,7 @@ add_custom_target(gen-gl-tests
 			vp-tex.list
 			variable_index_write_tests.list
 			vs_in_fp64.list
+			builtin_uniform_tests_int64.list
 )
 
 # Create a custom target for generating OpenCL tests
diff --git a/generated_tests/builtin_function_int64.py b/generated_tests/builtin_function_int64.py
new file mode 100644
index 0000000..56af117
--- /dev/null
+++ b/generated_tests/builtin_function_int64.py
@@ -0,0 +1,1001 @@
+# coding=utf-8
+#
+# Copyright © 2011 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+# This source file defines a set of test vectors that can be used to
+# test GLSL's built-in functions and operators.  It is intended to be
+# used by Python code that generates Piglit tests.
+#
+# The key export is the dictionary test_suite.  It contains an entry
+# for each possible overload of every pure built-in function and
+# operator.  By iterating through this dictionary you can find a set
+# of test vectors for testing nearly every built-in GLSL function.
+#
+# The following functions are not included, since they are not pure,
+# so they can't be tested using simple vectors:
+# - dFdx()
+# - dFdy()
+# - fwidth()
+# - ftransform()
+# - Increment and decrement operators
+#
+# The following functions are not included, since they need to be
+# tested in specialized ways:
+# - modf(): not tested because it has an out parameter
+# - isnan() and isinf(): not tested because special effort is required
+#   to create values that cause these functions to return true.
+#
+# Also not tested are array subscripting, field/method selection,
+# swizzling, the function call operator, assignment, and the sequence
+# operator.
+
+from __future__ import print_function, division, absolute_import
+import collections
+import itertools
+import functools
+
+from six.moves import range
+import numpy as np
+
+
+# Floating point types used by Python and numpy
+INT32_TYPES = (int, np.int32)
+UINT32_TYPES = (int, np.uint32)
+INT64_TYPES = (long, np.int64)
+UINT64_TYPES = (long, np.uint64)
+
+class GlslBuiltinType(object):
+    """Class representing a GLSL built-in type."""
+    def __init__(self, name, base_type, num_cols, num_rows,
+                 version_introduced):
+        self.__name = name
+        if base_type is not None:
+            self.__base_type = base_type
+        else:
+            self.__base_type = self
+        self.__num_cols = num_cols
+        self.__num_rows = num_rows
+        self.__version_introduced = version_introduced
+
+    @property
+    def name(self):
+        """The name of the type, as a string."""
+        return self.__name
+
+    @property
+    def base_type(self):
+        """For vectors and matrices, the type of data stored in each
+        element.  For scalars, equal to self.
+        """
+        return self.__base_type
+
+    @property
+    def num_cols(self):
+        """For matrices, the number of columns.  For vectors and
+        scalars, 1.
+        """
+        return self.__num_cols
+
+    @property
+    def num_rows(self):
+        """For vectors and matrices, the number of rows.  For scalars,
+        1.
+        """
+        return self.__num_rows
+
+    @property
+    def is_scalar(self):
+        return self.__num_cols == 1 and self.__num_rows == 1
+
+    @property
+    def is_vector(self):
+        return self.__num_cols == 1 and self.__num_rows != 1
+
+    @property
+    def is_matrix(self):
+        return self.__num_cols != 1
+
+    @property
+    def version_introduced(self):
+        """The earliest version of GLSL that this type appears in (as
+        a string, e.g. 110).
+        """
+        return self.__version_introduced
+
+    def __eq__(self, other):
+        if isinstance(other, GlslBuiltinType):
+            return self.name == other.name
+
+        return NotImplemented
+
+    def __lt__(self, other):
+        if isinstance(other, GlslBuiltinType):
+            return self.name < other.name
+
+        return NotImplemented
+
+    def __hash__(self):
+        """Hash the object.
+
+        This hash isn't super awesome, but it isn't prone to change since you
+        have to muck with private (__prefixed values) and some unlikely text in
+        addition.
+
+        """
+        return hash('__GLslBuiltinType_{}__'.format(self.name))
+
+    def __str__(self):
+        return self.__name
+
+    def __repr__(self):
+        return 'glsl_{0}'.format(self.__name)
+
+
+# Concrete declarations of GlslBuiltinType
+glsl_bool   = GlslBuiltinType('bool',   None,       1, 1, 110)
+glsl_bvec2  = GlslBuiltinType('bvec2',  glsl_bool,  1, 2, 110)
+glsl_bvec3  = GlslBuiltinType('bvec3',  glsl_bool,  1, 3, 110)
+glsl_bvec4  = GlslBuiltinType('bvec4',  glsl_bool,  1, 4, 110)
+glsl_int    = GlslBuiltinType('int',    None,       1, 1, 110)
+glsl_uint    = GlslBuiltinType('uint',    None,       1, 1, 110)
+glsl_int64_t  = GlslBuiltinType('int64_t', None,       1, 1, 460)
+glsl_i64vec2  = GlslBuiltinType('i64vec2', glsl_int64_t,  1, 2, 460)
+glsl_i64vec3  = GlslBuiltinType('i64vec3', glsl_int64_t,  1, 3, 460)
+glsl_i64vec4  = GlslBuiltinType('i64vec4', glsl_int64_t,  1, 4, 460)
+glsl_uint64_t = GlslBuiltinType('uint64_t', None,       1, 1, 460)
+glsl_u64vec2  = GlslBuiltinType('u64vec2', glsl_uint64_t,  1, 2, 460)
+glsl_u64vec3  = GlslBuiltinType('u64vec3', glsl_uint64_t,  1, 3, 460)
+glsl_u64vec4  = GlslBuiltinType('u64vec4', glsl_uint64_t,  1, 4, 460)
+
+
+# Named tuple representing the signature of a single overload of a
+# built-in GLSL function or operator:
+# - name is a name suitable for use in test filenames.  For functions,
+#   this is the name of the function.  For operators, it is a short
+#   description of the operator, beginning with "op", e.g. "op-plus".
+# - template is a Python format string that can be used to construct
+#   GLSL code that invokes the function or operator.
+# - version_introduced earliest version of GLSL the test applies to
+#   (as a string, e.g. 110).
+# - rettype is the return type of the function or operator (as a
+#   GlslBuiltinType).
+# - argtypes is a tuple containing the types of each parameter (as
+#   GlslBuiltinTypes).
+#
+# For example, the function
+#
+#   vec3 step(float edge, vec3 x)
+#
+# has a signature of
+#
+# Signature(name='step', template='step({0}, {1})',
+#           version_introduced=110, rettype='vec3',
+#           argtypes=('float', 'vec3'))
+Signature = collections.namedtuple(
+    'Signature',
+    ('name', 'template', 'version_introduced', 'extension', 'rettype', 'argtypes'))
+
+
+# Named tuple representing a single piece of test data for testing a
+# built-in GLSL function:
+# - arguments is a tuple containing the arguments to apply to the
+#   function.  Each argument is of a type native to numpy (e.g.
+#   numpy.float32 or numpy.ndarray)
+# - result is the value the function is expected to return.  It is
+#   also of a type native to numpy.
+# - tolerance is a float64 representing how much deviation from the
+#   result we expect, considering the floating point precision
+#   requirements of GLSL and OpenGL.  The value may be zero for test
+#   vectors involving booleans and integers.  If result is a vector or
+#   matrix, tolerance should be interpreted as the maximum permissible
+#   RMS error (as would be computed by the distance() function).
+TestVector = collections.namedtuple(
+    'TestVector', ('arguments', 'result', 'tolerance'))
+
+
+def glsl_type_of(value):
+    """Return the GLSL type corresponding to the given native numpy
+    value, as a GlslBuiltinType.
+    """
+
+    if isinstance(value, INT64_TYPES):
+        return glsl_int64_t
+    elif isinstance(value, UINT64_TYPES):
+        return glsl_uint64_t
+    elif isinstance(value, (bool, np.bool_)):
+        return glsl_bool
+    elif isinstance(value, INT32_TYPES):
+        return glsl_int
+    elif isinstance(value, UINT32_TYPES):
+        return glsl_uint
+    else:
+        if len(value.shape) == 1:
+            # Vector
+            vector_length = value.shape[0]
+            assert 2 <= vector_length <= 4
+            if value.dtype in INT64_TYPES:
+                return (glsl_i64vec2, glsl_i64vec3, glsl_i64vec4)[vector_length - 2]
+            elif value.dtype in UINT64_TYPES:
+                return (glsl_u64vec2, glsl_u64vec3, glsl_u64vec4)[vector_length - 2]
+            elif value.dtype == bool:
+                return (glsl_bvec2, glsl_bvec3, glsl_bvec4)[vector_length - 2]
+            else:
+                raise Exception(
+                    'Unexpected vector base type {0}'.format(value.dtype))
+
+def column_major_values(value):
+    """Given a native numpy value, return a list of the scalar values
+    comprising it, in column-major order."""
+    if isinstance(value, np.ndarray):
+        return list(np.reshape(value, -1, 'F'))
+    else:
+        return [value]
+
+
+def glsl_constant(value):
+    """Given a native numpy value, return GLSL code that constructs
+    it."""
+    column_major = np.reshape(np.array(value), -1, 'F')
+    if column_major.dtype == bool:
+        values = ['true' if x else 'false' for x in column_major]
+    else:
+        values = ['{0}lf'.format(repr(x)) for x in column_major]
+    if len(column_major) == 1:
+        return values[0]
+    else:
+        return '{0}({1})'.format(glsl_type_of(value), ', '.join(values))
+
+
+# Dictionary containing the test vectors.  Each entry in the
+# dictionary represents a single overload of a single built-in
+# function.  Its key is a Signature tuple, and its value is a list of
+# TestVector tuples.
+#
+# Note: the dictionary is initialized to {} here, but it is filled
+# with test vectors by code later in this file.
+test_suite = {}
+
+
+# Implementation
+# ==============
+#
+# The functions below shouldn't be necessary to call from outside this
+# file.  They exist solely to populate test_suite with test vectors.
+
+# Functions that simulate GLSL built-in functions (in the cases where
+# the GLSL built-in functions have no python or numpy equivalent, or
+# in cases where there is a behavioral difference).  These functions
+# return None if the behavior of the GLSL built-in is undefined for
+# the given set of inputs.
+def _multiply(x, y):
+    x_type = glsl_type_of(x)
+    y_type = glsl_type_of(y)
+
+    if x_type.is_vector and y_type.is_vector:
+        # vector * vector is done componentwise.
+        return x * y
+    else:
+        # All other cases are standard linear algebraic
+        # multiplication, which numpy calls "dot".
+        return np.dot(x, y)
+
+
+def _divide(x, y):
+    if any(y_element == 0 for y_element in column_major_values(y)):
+        # Division by zero is undefined.
+        return None
+    if glsl_type_of(x).base_type == glsl_int64_t:
+        # The GLSL spec does not make it clear what the rounding rules
+        # are when performing integer division.  C99 requires
+        # round-toward-zero, so in the absence of any other
+        # information, assume that's the correct behavior for GLSL.
+        #
+        # Python and numpy's rounding rules are inconsistent, so to
+        # make sure we get round-toward-zero behavior, divide the
+        # absolute values of x and y, and then fix the sign.
+        return (np.abs(x) // np.abs(y)) * (np.sign(x) * np.sign(y))
+    elif glsl_type_of(x).base_type == glsl_uint64_t:
+        return x // y
+    return x / y
+
+
+def _modulus(x, y):
+    if any(x_element < 0 for x_element in column_major_values(x)):
+        # Modulus operation with a negative first operand is
+        # undefined.
+        return None
+    if any(y_element <= 0 for y_element in column_major_values(y)):
+        # Modulus operation with a negative or zero second operand is
+        # undefined.
+        return None
+    return x % y
+
+
+def _lshift(x, y):
+    if not all(0 <= y_element < 32 for y_element in column_major_values(y)):
+        # Shifts by less than 0 or more than the number of bits in the
+        # type being shifted are undefined.
+        return None
+    # When the arguments to << don't have the same signedness, numpy
+    # likes to promote them to int64.  To avoid this, convert y to be
+    # the same type as x.
+    y_orig = y
+    result = x << y
+
+    # Shifting should always produce a result with the same base type
+    # as the left argument.
+    assert glsl_type_of(result).base_type == glsl_type_of(x).base_type
+
+    return result
+
+
+def _rshift(x, y):
+    if not all(0 <= y_element < 32 for y_element in column_major_values(y)):
+        # Shifts by less than 0 or more than the number of bits in the
+        # type being shifted are undefined.
+        return None
+    # When the arguments to >> don't have the same signedness, numpy
+    # likes to promote them to int64.  To avoid this, convert y to be
+    # the same type as x.
+    y_orig = y
+    result = x >> y
+
+    # Shifting should always produce a result with the same base type
+    # as the left argument.
+    assert glsl_type_of(result).base_type == glsl_type_of(x).base_type
+
+    return result
+
+
+def _equal(x, y):
+    return all(column_major_values(x == y))
+
+
+def _not_equal(x, y):
+    return not _equal(x, y)
+
+
+def _arctan2(y, x):
+    if x == y == 0.0:
+        return None
+    return np.arctan2(y, x)
+
+
+def _pow(x, y):
+    if x < 0.0:
+        return None
+    if x == 0.0 and y <= 0.0:
+        return None
+    return np.power(x, y)
+
+
+def _exp2(x):
+    # exp2() is not available in versions of numpy < 1.3.0 so we
+    # emulate it with power().
+    return np.power(2, x)
+
+
+def _trunc(x):
+    # trunc() rounds toward zero.  It is not available in version
+    # 1.2.1 of numpy so we emulate it with floor(), sign(), and abs().
+    return np.sign(x) * np.floor(np.abs(x))
+
+
+def _clamp(x, minVal, maxVal):
+    if minVal > maxVal:
+        return None
+    return min(max(x, minVal), maxVal)
+
+
+# Inefficient, but obvious
+def _mid3(x, y, z):
+    return np.sort([x, y, z])[1]
+
+def _smoothstep(edge0, edge1, x):
+    if edge0 >= edge1:
+        return None
+    t = _clamp((x-edge0)/(edge1-edge0), 0.0, 1.0)
+    return t*t*(3.0-2.0*t)
+
+
+def _argument_types_match(arguments, argument_indices_to_match):
+    """Return True if all of the arguments indexed by
+    argument_indices_to_match have the same GLSL type.
+    """
+    types = [glsl_type_of(arguments[i]) for i in argument_indices_to_match]
+    return all(x == types[0] for x in types)
+
+
+def _strict_tolerance(arguments, result):
+    """Compute tolerance using a strict interpretation of the GLSL and
+    OpenGL standards.
+
+    From the GLSL 1.20 spec (4.1.4 "Floats"):
+
+      "As an input value to one of the processing units, a
+      floating-point variable is expected to match the IEEE single
+      precision floating-point definition for precision and dynamic
+      range.  It is not required that the precision of internal
+      processing match the IEEE floating-point specification for
+      floating-point operations, but the guidelines for precision
+      established by the OpenGL 1.4 specification must be met."
+
+    From the OpenGL 1.4 spec (2.1.1 "Floating-Point Computation"):
+
+      "We require simply that numbers' floating-point parts contain
+      enough bits ... so that individual results of floating-point
+      operations are accurate to about 1 part in 10^5."
+
+    A harsh interpretation of the above is that (a) no precision is
+    lost in moving numbers into or out of the GPU, and (b) any
+    built-in function constitutes a single operation, so therefore the
+    error in applying any built-in function should be off by no more
+    than 1e-5 times its theoretically correct value.
+
+    This is not the only possible interpretation, however.  Certain
+    built-in functions, such as the cross product, are computed by a
+    formula consisting of many elementary multiplications and
+    additions, in which a large amount of cancellation sometimes
+    occurs.  It's possible that these rules are meant to apply to
+    those elementary multiplications and additions, and not the full
+    built-in function. Other built-in functions, such as the trig
+    functions, are typically implemented by a series approximation, in
+    which 1 part in 10^5 accuracy seems like overkill.  See below for
+    the tolerance computation we use on these other functions.
+    """
+    return 1e-5 * np.linalg.norm(result)
+
+
+def _trig_tolerance(arguments, result):
+    """Compute a more lenient tolerance bound for trig functions.
+
+    The GLSL and OpenGL specs don't provide any guidance as to the
+    required accuracy of trig functions (other than the "1 part in
+    10^5" general accuracy requirement, which seems like overkill for
+    trig functions.
+
+    So the tolerance here is rather arbitrarily chosen to be either 1
+    part in 10^3 or 10^-4, whichever is larger.
+    """
+    return max(1e-4, 1e-3 * np.linalg.norm(result))
+
+
+def _cross_product_tolerance(arguments, result):
+    """Compute a more lenient tolerance bound for cross product.
+
+    Since the computation of a cross product may involve a large
+    amount of cancellation, an error tolerance of 1 part in 10^5
+    (referred to the magnitude of the result vector) is overly tight.
+
+    So instead we allow the error to be 1 part in 10^5 referred to the
+    product of the magnitudes of the arguments.
+    """
+    assert len(arguments) == 2
+    return 1e-5 * np.linalg.norm(arguments[0]) * np.linalg.norm(arguments[1])
+
+
+def _simulate_function(test_inputs, python_equivalent, tolerance_function):
+    """Construct test vectors by simulating a GLSL function on a list
+    of possible inputs, and return a list of test vectors.
+
+    test_inputs is a list of possible input sequences, each of which
+    represents a set of arguments that should be applied to the
+    function.
+
+    python_equivalent is the function to simulate--it should return
+    None if the GLSL function returns undefined results for the given
+    set of inputs, otherwise it should return the expected result.
+    Input sequences for which python_equivalent returns None are
+    ignored.
+
+    tolerance_function is the function to call to compute the
+    tolerance.  It should take the set of arguments and the expected
+    result as its parameters.  It is only used for functions that
+    return floating point values.
+    """
+    test_vectors = []
+    for inputs in test_inputs:
+        expected_output = python_equivalent(*inputs)
+        if expected_output is not None:
+            tolerance = tolerance_function(inputs, expected_output)
+            test_vectors.append(TestVector(inputs, expected_output, tolerance))
+    return test_vectors
+
+
+def _vectorize_test_vectors(test_vectors, scalar_arg_indices, vector_length):
+    """Build a new set of test vectors by combining elements of
+    test_vectors into vectors of length vector_length. For example,
+    vectorizing the test vectors
+
+    [TestVector((10, 20), 30, tolerance), TestVector((11, 20), 31, tolerance)]
+
+    into vectors of length 2 would produce the result:
+
+    [TestVector((vec2(10, 11), vec2(20, 20)), vec2(30, 31), new_tolerance)].
+
+    Tolerances are combined in root-sum-square fashion.
+
+    scalar_arg_indices is a sequence of argument indices which should
+    not be vectorized.  So, if scalar_arg_indices is [1] in the above
+    example, the result would be:
+
+    [TestVector((vec2(10, 11), 20), vec2(30, 31), new_tolerance)].
+    """
+    def make_groups(test_vectors):
+        """Group test vectors according to the values passed to the
+        arguments that should not be vectorized.
+        """
+        groups = {}
+        for tv in test_vectors:
+            key = tuple(tv.arguments[i] for i in scalar_arg_indices)
+            if key not in groups:
+                groups[key] = []
+            groups[key].append(tv)
+        return groups
+
+    def partition_vectors(test_vectors, partition_size):
+        """Partition test_vectors into lists of length partition_size.
+        If partition_size does not evenly divide the number of test
+        vectors, wrap around as necessary to ensure that every input
+        test vector is included.
+        """
+        for i in range(0, len(test_vectors), partition_size):
+            partition = []
+            for j in range(partition_size):
+                partition.append(test_vectors[(i + j) % len(test_vectors)])
+            yield partition
+
+    def merge_vectors(test_vectors):
+        """Merge the given set of test vectors (whose arguments and
+        result are scalars) into a single test vector whose arguments
+        and result are vectors.  For argument indices in
+        scalar_arg_indices, leave the argument as a scalar.
+        """
+        arity = len(test_vectors[0].arguments)
+        arguments = []
+        for j in range(arity):
+            if j in scalar_arg_indices:
+                arguments.append(test_vectors[0].arguments[j])
+            else:
+                arguments.append(
+                    np.array([tv.arguments[j] for tv in test_vectors]))
+        result = np.array([tv.result for tv in test_vectors])
+        tolerance = np.linalg.norm([tv.tolerance for tv in test_vectors])
+        return TestVector(arguments, result, tolerance)
+
+    vectorized_test_vectors = []
+    groups = make_groups(test_vectors)
+    for key in sorted(groups.keys()):
+        test_vectors = groups[key]
+        vectorized_test_vectors.extend(
+            merge_vectors(partition)
+            for partition in partition_vectors(test_vectors, vector_length))
+    return vectorized_test_vectors
+
+
+def _store_test_vector(test_suite_dict, name, glsl_version, extension, test_vector,
+                       template=None):
+    """Store a test vector in the appropriate place in
+    test_suite_dict.  The dictionary key (which is a Signature tuple)
+    is generated by consulting the argument and return types of the
+    test vector, and combining them with name and glsl_version.
+
+    glsl_version is adjusted if necessary to reflect when the argument
+    and return types were introduced into GLSL.
+
+    If template is supplied, it is used insted as the template for the
+    Signature objects generated.
+    """
+    if template is None:
+        arg_indices = range(len(test_vector.arguments))
+        template = '{0}({1})'.format(
+            name, ', '.join('{{{0}}}'.format(i) for i in arg_indices))
+    rettype = glsl_type_of(test_vector.result)
+    argtypes = tuple(glsl_type_of(arg) for arg in test_vector.arguments)
+    adjusted_glsl_version = glsl_version
+
+    signature = Signature(
+        name, template, adjusted_glsl_version, extension, rettype, argtypes)
+    if signature not in test_suite_dict:
+        test_suite_dict[signature] = []
+    test_suite_dict[signature].append(test_vector)
+
+
+def _store_test_vectors(test_suite_dict, name, glsl_version, extension,
+                        test_vectors, template=None):
+    """Store multiple test vectors in the appropriate places in
+    test_suite_dict.
+
+    If template is supplied, it is used insted as the template for the
+    Signature objects generated.
+    """
+    for test_vector in test_vectors:
+        _store_test_vector(test_suite_dict, name, glsl_version, extension,
+                           test_vector, template=template)
+
+
+def make_arguments(input_generators):
+    """Construct a list of tuples of input arguments to test.
+
+    input_generators is a list, the ith element of which is a sequence
+    of values that are suitable for use as the ith argument of the
+    function under test.
+
+    Output is a list, each element of which is a tuple of arguments to
+    be passed to the function under test.  These values are produced
+    by taking the cartesian product of the input sequences.
+
+    """
+    return list(itertools.product(*input_generators))
+
+
+def _make_componentwise_test_vectors(test_suite_dict):
+    """Add test vectors to test_suite_dict for GLSL built-in
+    functions that operate on vectors in componentwise fashion.
+    Examples include sin(), cos(), min(), max(), and clamp().
+    """
+    ints = [np.int32(x) for x in [-5, -2, -1, 0, 1, 2, 5]]
+    uints = [np.uint32(x) for x in [0, 1, 2, 5, 34]]
+    bools = [True, False]
+
+    def f(name, arity, python_equivalent,
+          alternate_scalar_arg_indices, test_inputs,
+          tolerance_function=_strict_tolerance):
+
+        """Create test vectors for the function with the given name
+        and arity, which was introduced in the given glsl_version.
+
+        python_equivalent is a Python function which operates on scalars,
+        and simulates the GLSL function.  This function should return None
+        in any case where the output of the GLSL function is undefined.
+
+        If alternate_scalar_arg_indices is not None, also create test
+        vectors for an alternate vectorized version of the function,
+        in which some arguments are scalars.
+        alternate_scalar_arg_indices is a sequence of the indices of
+        the arguments which are scalars.
+
+        test_inputs is a list, the ith element of which is a list of
+        values that are suitable for use as the ith argument of the
+        function.
+
+        If tolerance_function is supplied, it is a function which
+        should be used to compute the tolerance for the test vectors.
+        Otherwise, _strict_tolerance is used.
+        """
+        scalar_test_vectors = _simulate_function(
+            make_arguments(test_inputs), python_equivalent, tolerance_function)
+        _store_test_vectors(
+            test_suite_dict, name, 150, "ARB_gpu_shader_int64", scalar_test_vectors)
+        if alternate_scalar_arg_indices is None:
+            scalar_arg_indices_list = [()]
+        else:
+            scalar_arg_indices_list = [(), alternate_scalar_arg_indices]
+        for scalar_arg_indices in scalar_arg_indices_list:
+            for vector_length in (2, 3, 4):
+                _store_test_vectors(
+                    test_suite_dict, name, 150, "ARB_gpu_shader_int64",
+                    _vectorize_test_vectors(
+                        scalar_test_vectors, scalar_arg_indices,
+                        vector_length))
+
+    f('abs', 1, np.abs, None, [np.linspace(-10, 15, 5, dtype=np.dtype(np.int64))])
+    f('sign', 1, np.sign, None, [np.linspace(-15, 15, 5, dtype=np.dtype(np.int64))])
+    f('min', 2, min, [1],
+      [np.linspace(-20, 20, 4, dtype=np.dtype(np.int64)), np.linspace(-20, 20, 4, dtype=np.dtype(np.int64))])
+    f('min', 2, min, [1],
+      [np.linspace(20, 90, 4, dtype=np.dtype(np.uint64)), np.linspace(20, 90, 4, dtype=np.dtype(np.uint64))])
+    f('max', 2, max, [1],
+      [np.linspace(-20, 20, 4, dtype=np.dtype(np.int64)), np.linspace(-20, 20, 4, dtype=np.dtype(np.int64))])
+    f('max', 2, max, [1],
+      [np.linspace(20, 90, 4, dtype=np.dtype(np.uint64)), np.linspace(20, 90, 4, dtype=np.dtype(np.uint64))])
+    f('clamp', 3, _clamp, [1, 2], [np.linspace(-20, 20, 4, dtype=np.dtype(np.int64)),
+                                   np.linspace(-15, 15, 3, dtype=np.dtype(np.int64)),
+                                   np.linspace(-15, 15, 3, dtype=np.dtype(np.int64))])
+    f('mix', 3, lambda x, y, a: y if a else x, None,
+      [np.linspace(-20, 20, 2, dtype=np.dtype(np.int64)), np.linspace(-30, 30, 2, dtype=np.dtype(np.int64)), bools])
+
+_make_componentwise_test_vectors(test_suite)
+
+
+def _make_vector_relational_test_vectors(test_suite_dict):
+    """Add test vectors to test_suite_dict for GLSL built-in functions
+    that operate on vectors of floats, ints, or bools, but not on
+    single floats, ints, or bools.  Examples include lessThan(),
+    equal(), and not().
+    """
+    _default_inputs = {
+        'v': np.linspace(-15, 15, 5, dtype=np.dtype(np.int64)),
+        'b': np.array([False, True])
+        }
+
+    def f(name, arity, python_equivalent, arg_types,
+          tolerance_function=_strict_tolerance,
+          extension=None):
+        """Make test vectors for the function with the given name and
+        arity, which was introduced in the given glsl_version.
+
+        python_equivalent is a Python function which operates on scalars,
+        and simulates the GLSL function.
+
+        arg_types is a string containing 'v' if the function supports
+        standard "vec" inputs, 'i' if it supports "ivec" inputs, and 'b'
+        if it supports "bvec" inputs.  The output type of the function is
+        assumed to be the same as its input type.
+
+        If tolerance_function is supplied, it is a function which
+        should be used to compute the tolerance for the test vectors.
+        Otherwise, _strict_tolerance is used.
+        """
+        for arg_type in arg_types:
+            test_inputs = [_default_inputs[arg_type]]*arity
+            scalar_test_vectors = _simulate_function(
+                make_arguments(test_inputs), python_equivalent,
+                tolerance_function)
+            for vector_length in (2, 3, 4):
+                _store_test_vectors(
+                    test_suite_dict, name, 150, "ARB_gpu_shader_int64",
+                    _vectorize_test_vectors(
+                        scalar_test_vectors, (), vector_length))
+
+    f('lessThan', 2, lambda x, y: x < y, 'v')
+    f('lessThanEqual', 2, lambda x, y: x <= y, 'v')
+    f('greaterThan', 2, lambda x, y: x > y, 'v')
+    f('greaterThanEqual', 2, lambda x, y: x >= y, 'v')
+    f('equal', 2, lambda x, y: x == y, 'v')
+    f('notEqual', 2, lambda x, y: x != y, 'v')
+
+_make_vector_relational_test_vectors(test_suite)
+
+
+def _make_vector_or_matrix_test_vectors(test_suite_dict):
+    """Add test vectors to test_suite_dict for GLSL built-in functions
+    that operate on vectors/matrices as a whole.  Examples include
+    length(), dot(), cross(), normalize(), and refract().
+    """
+    def match_args(*indices):
+        """Return a function that determines whether the type of the
+        arguments at the given indices match.
+
+        For example:
+
+            match(1, 3)
+
+        is equivalent to:
+
+            lambda a, b, c, d: glsl_type_of(b) == glsl_type_of(d)
+        """
+        return lambda *args: _argument_types_match(args, indices)
+
+    def match_simple_binop(x, y):
+        """Detemine whether the type of the arguments is compatible
+        for a simple binary operator (such as '+').
+
+        Arguments are compatible if one is a scalar and the other is a
+        vector/matrix with the same base type, or if they are the same
+        type.
+        """
+        x_type = glsl_type_of(x)
+        y_type = glsl_type_of(y)
+        if x_type.base_type != y_type.base_type:
+            return False
+        if x_type.is_scalar or y_type.is_scalar:
+            return True
+        return x_type == y_type
+
+    def match_multiply(x, y):
+        """Determine whether the type of the arguments is compatible
+        for multiply.
+
+        Arguments are compatible if they are scalars, vectors, or
+        matrices with the same base type, and the vector/matrix sizes
+        are properly matched.
+        """
+        x_type = glsl_type_of(x)
+        y_type = glsl_type_of(y)
+        if x_type.base_type != y_type.base_type:
+            return False
+        if x_type.is_scalar or y_type.is_scalar:
+            return True
+        if x_type.is_vector and y_type.is_matrix:
+            # When multiplying vector * matrix, the vector is
+            # transposed to a row vector.  So its row count must match
+            # the row count of the matrix.
+            return x_type.num_rows == y_type.num_rows
+        elif x_type.is_vector:
+            assert y_type.is_vector
+            # When multiplying vector * vector, the multiplication is
+            # done componentwise, so the types must match exactly.
+            return x_type == y_type
+        else:
+            assert x_type.is_matrix
+            # When multiplying matrix * matrix or matrix * vector, a
+            # standard linear algebraic multiply is used, so x's
+            # column count must match y's row count.
+            return x_type.num_cols == y_type.num_rows
+
+    def match_shift(x, y):
+        """Determine whether the type of the arguments is compatible
+        for shift operations.
+
+        Arguments are compatible if they are the same length or the
+        first one is a vector and the second is a scalar.  Their base
+        types need not be the same, but they both must be integral.
+        """
+        x_type = glsl_type_of(x)
+        y_type = glsl_type_of(y)
+        if x_type.base_type not in (glsl_int64_t, glsl_uint64_t):
+            return False
+        if y_type.base_type not in (glsl_int, glsl_uint):
+            return False
+        if y_type.is_scalar:
+            return True
+        assert not x_type.is_matrix
+        assert not y_type.is_matrix
+        return x_type.num_rows == y_type.num_rows
+
+    int64s = [np.int64(x) for x in [0, -100000000000, 100000000000]]
+    uint64s = [np.uint64(x) for x in [0,  10, 100000000000]]
+
+    int64vecs = [
+        np.array([-10, -12], dtype=np.int64),
+        np.array([-42, 48], dtype=np.int64),
+        np.array([-1333333333333333259, 85, 94], dtype=np.int64),
+        np.array([167, 66, 187], dtype=np.int64),
+        np.array([165, 133, 193, 76], dtype=np.int64),
+        np.array([80, -15, -51, 0], dtype=np.int64)
+        ]
+    int64_i64vecs = int64s + int64vecs
+    i64vec3s = [
+        np.array([-3, -85, -94], dtype=np.int64),
+        np.array([ 1333333333333333259, 66, 87], dtype=np.int64),
+        ]
+
+    uint64vecs = [
+        np.array([10, 12], dtype=np.uint64),
+        np.array([42, 48], dtype=np.uint64),
+        np.array([1333333333333333259, 85, 94], dtype=np.uint64),
+        np.array([167, 66, 187], dtype=np.uint64),
+        np.array([165, 133, 193, 76], dtype=np.uint64),
+        np.array([80, 15, 51, 0], dtype=np.uint64)
+        ]
+    uint64_u64vecs = uint64s + uint64vecs
+    u64vec3s = [
+        np.array([3, 85, 94], dtype=np.uint64),
+        np.array([ 1333333333333333259, 66, 87], dtype=np.uint64),
+        ]
+    small_uints = [np.uint32(x) for x in [0, 1, 2, 5, 25, 31]]
+
+    def f(name, arity, python_equivalent,
+          filter, test_inputs, tolerance_function=_strict_tolerance,
+          template=None):
+        """Make test vectors for the function with the given name and
+        arity, which was introduced in the given glsl_version.
+
+        python_equivalent is a Python function which simulates the GLSL
+        function.  This function should return None in any case where the
+        output of the GLSL function is undefined.  However, it need not
+        check that the lengths of the input vectors are all the same.
+
+        If filter is not None, it will be called with each set of
+        arguments, and test cases will only be generated if the filter
+        returns True.
+
+        test_inputs is a list, the ith element of which is a list of
+        vectors and/or scalars that are suitable for use as the ith
+        argument of the function.
+
+        If tolerance_function is supplied, it is a function which
+        should be used to compute the tolerance for the test vectors.
+        Otherwise, _strict_tolerance is used.
+
+        If template is supplied, it is used insted as the template for
+        the Signature objects generated.
+        """
+        test_inputs = make_arguments(test_inputs)
+
+        if filter is not None:
+            test_inputs = \
+                [arguments for arguments in test_inputs if filter(*arguments)]
+        _store_test_vectors(
+            test_suite_dict, name, 150, "ARB_gpu_shader_int64",
+            _simulate_function(
+                test_inputs, python_equivalent, tolerance_function),
+            template=template)
+
+    f('op-add', 2, lambda x, y: x + y, match_simple_binop,
+      [int64s+int64vecs+uint64s+uint64vecs,
+       int64s+int64vecs+uint64s+uint64vecs],
+      template='({0} + {1})')
+    f('op-sub', 2, lambda x, y: x - y, match_simple_binop,
+      [int64s+int64vecs+uint64s+uint64vecs,
+       int64s+int64vecs+uint64s+uint64vecs],
+      template='({0} - {1})')
+    f('op-mult', 2, _multiply, match_multiply,
+      [int64s+int64vecs+uint64s+uint64vecs,
+       int64s+int64vecs+uint64s+uint64vecs],
+      template='({0} * {1})')
+    f('op-div', 2, _divide, match_simple_binop,
+      [int64s+int64vecs,
+       int64s+int64vecs],
+      template='({0} / {1})')
+    f('op-mod', 2, _modulus, match_simple_binop,
+      [int64s+int64vecs,
+       int64s+int64vecs],
+      template='({0} % {1})')
+    f('op-gt', 2, lambda x, y: x > y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} > {1})')
+    f('op-lt', 2, lambda x, y: x < y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} < {1})')
+    f('op-ge', 2, lambda x, y: x >= y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} >= {1})')
+    f('op-le', 2, lambda x, y: x <= y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} <= {1})')
+    f('op-eq', 2, lambda x, y: x == y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} == {1})')
+    f('op-ne', 2, lambda x, y: x != y, match_args(0, 1),
+      [int64s+uint64s,
+       int64s+uint64s],
+      template = '({0} != {1})')
+    f('op-bitand', 2, lambda x, y: x & y, match_simple_binop,
+      [int64s+uint64s, int64s+uint64s],
+      template='({0} & {1})')
+    f('op-bitor', 2, lambda x, y: x | y, match_simple_binop,
+      [int64s+uint64s, int64s+uint64s],
+      template='({0} | {1})')
+    f('op-bitxor', 2, lambda x, y: x ^ y, match_simple_binop,
+      [int64s+uint64s, int64s+uint64s],
+      template='({0} ^ {1})')
+    f('op-lshift', 2, _lshift, match_shift,
+      [int64s+uint64s,
+       small_uints],
+      template='({0} << {1})')
+    f('op-rshift', 2, _rshift, match_shift,
+      [int64s+uint64s,
+       small_uints],
+      template='({0} >> {1})')
+_make_vector_or_matrix_test_vectors(test_suite)
+
+
+def _check_signature_safety(test_suite_dict):
+    """As a final safety check, verify that for each possible
+    combination of name and argtypes, there is exactly one
+    signature.
+    """
+    name_argtype_combos = set()
+    for signature in test_suite_dict:
+        name_argtype_combo = (signature.name, signature.argtypes, signature.extension)
+        if name_argtype_combo in name_argtype_combos:
+            raise Exception(
+                'Duplicate signature found for {0}'.format(name_argtype_combo))
+        name_argtype_combos.add(name_argtype_combo)
+_check_signature_safety(test_suite)
diff --git a/generated_tests/gen_builtin_uniform_tests_int64.py b/generated_tests/gen_builtin_uniform_tests_int64.py
new file mode 100644
index 0000000..0bb2752
--- /dev/null
+++ b/generated_tests/gen_builtin_uniform_tests_int64.py
@@ -0,0 +1,642 @@
+# coding=utf-8
+#
+# Copyright © 2011 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+# Generate a set of shader_runner tests for every overloaded version
+# of every built-in function, based on the test vectors computed by
+# builtin_function.py.
+#
+# In each set of generated tests, one test exercises the built-in
+# function in each type of shader (vertex, geometry, and fragment).
+# In all cases, the inputs to the built-in function come from
+# uniforms, so that the effectiveness of the test won't be
+# circumvented by constant folding in the GLSL compiler.
+#
+# The tests operate by invoking the built-in function in the
+# appropriate shader, applying a scale and offset so that the expected
+# values are in the range [0.25, 0.75], and then outputting the result
+# as a solid rgba color, which is then checked using shader_runner's
+# "probe rgba" command.
+#
+# For built-in functions whose result type is a matrix, the test
+# checks one column at a time.
+#
+# This program outputs, to stdout, the name of each file it generates.
+# With the optional argument --names-only, it only outputs the names
+# of the files; it doesn't generate them.
+
+from __future__ import print_function, division, absolute_import
+from builtin_function_int64 import *
+import abc
+import numpy
+import optparse
+import os
+import os.path
+
+from six.moves import range
+
+from modules import utils
+
+
+def compute_offset_and_scale(test_vectors):
+    """Compute scale and offset values such that for each result in
+    test_vectors, (result - offset) * scale is in the range [0.25,
+    0.75], and scale is less than or equal to 1.0.  These values are
+    used to transform the test vectors so that their outputs can be
+    stored in gl_FragColor without overflow.
+    """
+    low = min(numpy.min(tv.result) for tv in test_vectors)
+    hi = max(numpy.max(tv.result) for tv in test_vectors)
+    span = hi - low
+    center = (hi + low)/2.0
+    span *= 2.0
+    if span < 1.0:
+        span = 1.0
+    offset = center - span/2.0
+    scale = 1.0/span
+    return offset, scale
+
+
+def shader_runner_format(values):
+    """Format the given values for use in a shader_runner "uniform" or
+    "probe rgba" command.  Bools are converted to 0's and 1's, and
+    values are separated by spaces.
+    """
+    transformed_values = []
+    for value in values:
+        if isinstance(value, (bool, np.bool_)):
+            transformed_values.append(int(value))
+        else:
+            transformed_values.append(value)
+    return ' '.join(repr(x) for x in transformed_values)
+
+
+def shader_runner_type(glsl_type):
+    """Return the appropriate type name necessary for binding a
+    uniform of the given type using shader_runner's "uniform" command.
+    Boolean values and vectors are converted to ints, and square
+    matrices are written in "matNxN" form.
+    """
+    if glsl_type.base_type == glsl_bool:
+        if glsl_type.is_scalar:
+            return 'int'
+        else:
+            return 'ivec{0}'.format(glsl_type.num_rows)
+    else:
+        return str(glsl_type)
+
+
+class Comparator(object):
+    """Base class which abstracts how we compare expected and actual
+    values.
+    """
+    __metaclass__ = abc.ABCMeta
+
+    def make_additional_declarations(self):
+        """Return additional declarations, if any, that are needed in
+        the shader program.
+        """
+        return ''
+
+    @abc.abstractmethod
+    def make_result_handler(self, invocation, output_var):
+        """Return the shader code that is needed to produce the result
+        and store it in output_var.
+
+        invocation is the GLSL code to compute the output of the
+        built-in function.
+        """
+
+    @abc.abstractmethod
+    def make_result_test(self, test_num, test_vector):
+        """Return the shader_runner test code that is needed to test a
+        single test vector.
+        """
+
+    def testname_suffix(self):
+        """Return a string to be used as a suffix on the test name to
+        distinguish it from tests using other comparators."""
+        return ''
+
+
+class BoolComparator(Comparator):
+    """Comparator that tests functions returning bools and bvecs by
+    converting them to floats.
+
+    This comparator causes code to be generated in the following form:
+
+        rettype result = func(args);
+        output_var = vec4(result, 0.0, ...);
+    """
+    def __init__(self, signature):
+        assert not signature.rettype.is_matrix
+        self.__signature = signature
+        self.__padding = 4 - signature.rettype.num_rows
+
+    def make_result_handler(self, invocation, output_var):
+        statements = '  {0} result = {1};\n'.format(
+            self.__signature.rettype, invocation)
+        statements += '  {0} = vec4(result{1});\n'.format(
+            output_var, ', 0.0' * self.__padding)
+        return statements
+
+    def convert_to_float(self, value):
+        """Convert the given vector or scalar value to a list of
+        floats representing the expected color produced by the test.
+        """
+        value = value*1.0  # convert bools to floats
+        value = column_major_values(value)
+        value += [0.0] * self.__padding
+        return value
+
+    def make_result_test(self, test_num, test_vector, draw):
+        test = draw
+        test += 'probe rgba {0} 0 {1}\n'.format(
+            test_num,
+            shader_runner_format(self.convert_to_float(test_vector.result)))
+        return test
+
+
+class BoolIfComparator(Comparator):
+    """Comparator that tests functions returning bools by evaluating
+    them inside an if statement.
+
+    This comparator causes code to be generated in the following form:
+
+        if (func(args))
+          output_var = vec4(1.0, 1.0, 0.0, 1.0);
+        else
+          output_var = vecp(0.0, 0.0, 1.0, 1.0);
+    """
+    def __init__(self, signature):
+        assert signature.rettype == glsl_bool
+        self.__padding = 4 - signature.rettype.num_rows
+
+    def make_result_handler(self, invocation, output_var):
+        statements = '  if({0})\n'.format(invocation)
+        statements += '    {0} = vec4(1.0, 1.0, 0.0, 1.0);\n'.format(
+            output_var)
+        statements += '  else\n'
+        statements += '    {0} = vec4(0.0, 0.0, 1.0, 1.0);\n'.format(
+            output_var)
+        return statements
+
+    def convert_to_float(self, value):
+        """Convert the given vector or scalar value to a list of
+        floats representing the expected color produced by the test.
+        """
+        if value:
+            return [1.0, 1.0, 0.0, 1.0]
+        else:
+            return [0.0, 0.0, 1.0, 1.0]
+
+    def make_result_test(self, test_num, test_vector, draw):
+        test = draw
+        test += 'probe rgba {0} 0 {1}\n'.format(
+            test_num,
+            shader_runner_format(self.convert_to_float(test_vector.result)))
+        return test
+
+    def testname_suffix(self):
+        return '-using-if'
+
+
+class IntComparator(Comparator):
+    """Comparator that tests functions returning ints or ivecs using a
+    strict equality test.
+
+    This comparator causes code to be generated in the following form:
+
+        rettype result = func(args);
+        output_var = result == expected ? vec4(0.0, 1.0, 0.0, 1.0)
+                                        : vec4(1.0, 0.0, 0.0, 1.0);
+    """
+    def __init__(self, signature):
+        self.__signature = signature
+
+    def make_additional_declarations(self):
+        return 'uniform {0} expected;\n'.format(self.__signature.rettype)
+
+    def make_result_handler(self, invocation, output_var):
+        statements = '  {0} result = {1};\n'.format(
+            self.__signature.rettype, invocation)
+        statements += '  {v} = {cond} ? {green} : {red};\n'.format(
+            v=output_var, cond='result == expected',
+            green='vec4(0.0, 1.0, 0.0, 1.0)',
+            red='vec4(1.0, 0.0, 0.0, 1.0)')
+        return statements
+
+    def make_result_test(self, test_num, test_vector, draw):
+        test = 'uniform {0} expected {1}\n'.format(
+            shader_runner_type(self.__signature.rettype),
+            shader_runner_format(column_major_values(test_vector.result)))
+        test += draw
+        test += 'probe rgba {0} 0 0.0 1.0 0.0 1.0\n'.format(test_num)
+        return test
+
+
+class FloatComparator(Comparator):
+    """Comparator that tests functions returning floats or vecs using a
+    strict equality test.
+
+    This comparator causes code to be generated in the following form:
+
+        rettype result = func(args);
+        output_var = distance(result, expected) <= tolerance
+                     ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);
+    """
+    def __init__(self, signature):
+        self.__signature = signature
+
+    def make_additional_declarations(self):
+        decls += 'uniform {0} expected;\n'.format(self.__signature.rettype)
+        return decls
+
+    def make_indexers(self):
+        """Build a list of strings which index into every possible
+        value of the result.  For example, if the result is a vec2,
+        then build the indexers ['[0]', '[1]'].
+        """
+        if self.__signature.rettype.num_cols == 1:
+            col_indexers = ['']
+        else:
+            col_indexers = ['[{0}]'.format(i)
+                            for i in range(self.__signature.rettype.num_cols)]
+        if self.__signature.rettype.num_rows == 1:
+            row_indexers = ['']
+        else:
+            row_indexers = ['[{0}]'.format(i)
+                            for i in range(self.__signature.rettype.num_rows)]
+        return [col_indexer + row_indexer
+                for col_indexer in col_indexers
+                for row_indexer in row_indexers]
+
+    def make_result_handler(self, invocation, output_var):
+        statements = '  {0} result = {1};\n'.format(
+            self.__signature.rettype, invocation)
+        # Can't use distance when testing itself, or when the rettype
+        # is a matrix.
+        condition = 'result == expected'
+        statements += '  {v} = {cond} ? {green} : {red};\n'.format(
+            v=output_var, cond=condition, green='vec4(0.0, 1.0, 0.0, 1.0)',
+            red='vec4(1.0, 0.0, 0.0, 1.0)')
+        return statements
+
+    def make_result_test(self, test_num, test_vector, draw):
+        test = 'uniform {0} expected {1}\n'.format(
+            shader_runner_type(self.__signature.rettype),
+            shader_runner_format(column_major_values(test_vector.result)))
+        test += draw
+        test += 'probe rgba {0} 0 0.0 1.0 0.0 1.0\n'.format(test_num)
+        return test
+
+
+class ShaderTest(object):
+    """Class used to build a test of a single built-in.  This is an
+    abstract base class--derived types should override test_prefix(),
+    make_vertex_shader(), make_fragment_shader(), and other functions
+    if necessary.
+    """
+    __metaclass__ = abc.ABCMeta
+
+    def __init__(self, signature, test_vectors, use_if):
+        """Prepare to build a test for a single built-in.  signature
+        is the signature of the built-in (a key from the
+        builtin_function.test_suite dict), and test_vectors is the
+        list of test vectors for testing the given builtin (the
+        corresponding value from the builtin_function.test_suite
+        dict).
+
+        If use_if is True, then the generated test checks the result
+        by using it in an if statement--this only works for builtins
+        returning bool.
+        """
+        self._signature = signature
+        self._test_vectors = test_vectors
+        if use_if:
+            self._comparator = BoolIfComparator(signature)
+        elif signature.rettype.base_type == glsl_bool:
+            self._comparator = BoolComparator(signature)
+        elif signature.rettype.base_type == glsl_int64_t:
+            self._comparator = IntComparator(signature)
+        elif signature.rettype.base_type == glsl_uint64_t:
+            self._comparator = IntComparator(signature)
+        else:
+            raise Exception('Unexpected rettype {0}'.format(signature.rettype))
+
+    def glsl_version(self):
+        return self._signature.version_introduced
+
+    def draw_command(self):
+        if self.glsl_version() >= 140:
+            return 'draw arrays GL_TRIANGLE_FAN 0 4\n'
+        else:
+            return 'draw rect -1 -1 2 2\n'
+
+    def make_additional_requirements(self):
+        """Return a string that should be included in the test's
+        [require] section.
+        """
+        if self._signature.extension:
+            return 'GL_{0}\n'.format(self._signature.extension)
+        return ""
+
+    @abc.abstractmethod
+    def test_prefix(self):
+        """Return the prefix that should be used in the test file name
+        to identify the type of test, e.g. "vs" for a vertex shader
+        test.
+        """
+
+    @abc.abstractmethod
+    def make_vertex_shader(self):
+        """Return the vertex shader for this test."""
+
+    def make_geometry_shader(self):
+        """Return the geometry shader for this test (or None if this
+        test doesn't require a geometry shader).  No need to
+        reimplement this function in classes that don't use geometry
+        shaders.
+        """
+        return None
+
+    def make_geometry_layout(self):
+        """Return the geometry layout for this test (or None if this
+        test doesn't require a geometry layout section).  No need to
+        reimplement this function in classes that don't use geometry
+        shaders.
+        """
+        return None
+
+    @abc.abstractmethod
+    def make_fragment_shader(self):
+        """Return the fragment shader for this test."""
+
+    def make_test_shader(self, additional_declarations, prefix_statements,
+                         output_var, suffix_statements):
+        """Generate the shader code necessary to test the built-in.
+        additional_declarations is a string containing any
+        declarations that need to be before the main() function of the
+        shader.  prefix_statements is a string containing any
+        additional statements than need to be inside the main()
+        function of the shader, before the built-in function is
+        called.  output_var is the variable that the result of the
+        built-in function should be assigned to, after conversion to a
+        vec4.  suffix_statements is a string containing any additional
+        statements that need to be inside the main() function of the
+        shader, after the built-in function is called.
+        """
+        shader = ''
+        if self._signature.extension:
+            shader += '#extension GL_{0} : require\n'.format(self._signature.extension)
+        shader += additional_declarations
+        for i in range(len(self._signature.argtypes)):
+            shader += 'uniform {0} arg{1};\n'.format(
+                self._signature.argtypes[i], i)
+        shader += self._comparator.make_additional_declarations()
+        shader += '\n'
+        shader += 'void main()\n'
+        shader += '{\n'
+        shader += prefix_statements
+        invocation = self._signature.template.format(
+            *['arg{0}'.format(i)
+              for i in range(len(self._signature.argtypes))])
+        shader += self._comparator.make_result_handler(invocation, output_var)
+        shader += suffix_statements
+        shader += '}\n'
+        return shader
+
+    def make_test(self):
+        """Make the complete shader_runner test file, and return it as
+        a string.
+        """
+        test = ''
+        for test_num, test_vector in enumerate(self._test_vectors):
+            for i in range(len(test_vector.arguments)):
+                test += 'uniform {0} arg{1} {2}\n'.format(
+                    shader_runner_type(self._signature.argtypes[i]),
+                    i, shader_runner_format(
+                        column_major_values(test_vector.arguments[i])))
+            # Note: shader_runner uses a 250x250 window so we must
+            # ensure that test_num <= 250.
+            test += self._comparator.make_result_test(
+                test_num % 250, test_vector, self.draw_command())
+        return test
+
+    def make_vbo_data(self):
+        # Starting with GLSL 1.40/GL 3.1, we need to use VBOs and
+        # vertex shader input bindings for our vertex data instead of
+        # the piglit drawing utilities and gl_Vertex.
+        if self.glsl_version() < 140:
+            return ""
+        vbo = '[vertex data]\n'
+        vbo += 'vertex/float/2\n'
+        vbo += '-1.0 -1.0\n'
+        vbo += ' 1.0 -1.0\n'
+        vbo += ' 1.0  1.0\n'
+        vbo += '-1.0  1.0\n'
+        vbo += '\n'
+        return vbo
+
+    def filename(self):
+        argtype_names = '-'.join(
+            str(argtype) for argtype in self._signature.argtypes)
+        if self._signature.extension:
+            subdir = self._signature.extension.lower()
+        else:
+            subdir = 'glsl-{0:1.2f}'.format(float(self.glsl_version()) / 100)
+        return os.path.join(
+            'spec', subdir, 'execution', 'built-in-functions',
+            '{0}-{1}-{2}{3}.shader_test'.format(
+                self.test_prefix(), self._signature.name, argtype_names,
+                self._comparator.testname_suffix()))
+
+    def generate_shader_test(self):
+        """Generate the test and write it to the output file."""
+        shader_test = '[require]\n'
+        shader_test += 'GLSL >= {0:1.2f}\n'.format(
+            float(self.glsl_version()) / 100)
+        shader_test += self.make_additional_requirements()
+        shader_test += '\n'
+        shader_test += '[vertex shader]\n'
+        shader_test += self.make_vertex_shader()
+        shader_test += '\n'
+        gs = self.make_geometry_shader()
+        if gs:
+            shader_test += '[geometry shader]\n'
+            shader_test += gs
+            shader_test += '\n'
+        gl = self.make_geometry_layout()
+        if gl:
+            shader_test += '[geometry layout]\n'
+            shader_test += gl
+            shader_test += '\n'
+        shader_test += '[fragment shader]\n'
+        shader_test += self.make_fragment_shader()
+        shader_test += '\n'
+        shader_test += self.make_vbo_data()
+        shader_test += '[test]\n'
+        shader_test += 'clear color 0.0 0.0 1.0 0.0\n'
+        shader_test += 'clear\n'
+        shader_test += self.make_test()
+        filename = self.filename()
+        dirname = os.path.dirname(filename)
+        utils.safe_makedirs(dirname)
+        with open(filename, 'w') as f:
+            f.write(shader_test)
+
+
+class VertexShaderTest(ShaderTest):
+    """Derived class for tests that exercise the built-in in a vertex
+    shader.
+    """
+    def test_prefix(self):
+        return 'vs'
+
+    def make_vertex_shader(self):
+        if self.glsl_version() >= 140:
+            return self.make_test_shader(
+                'in vec4 vertex;\n' +
+                'out vec4 color;\n',
+                '  gl_Position = vertex;\n',
+                'color', '')
+        else:
+            return self.make_test_shader(
+                'varying vec4 color;\n',
+                '  gl_Position = gl_Vertex;\n',
+                'color', '')
+
+    def make_fragment_shader(self):
+        shader = '''varying vec4 color;
+
+void main()
+{
+  gl_FragColor = color;
+}
+'''
+        return shader
+
+
+class GeometryShaderTest(ShaderTest):
+    """Derived class for tests that exercise the built-in in a
+    geometry shader.
+    """
+    def test_prefix(self):
+        return 'gs'
+
+    def glsl_version(self):
+        return max(150, ShaderTest.glsl_version(self))
+
+    def make_vertex_shader(self):
+        shader = ''
+        shader += "in vec4 vertex;\n"
+        shader += "out vec4 vertex_to_gs;\n"
+
+        shader += "void main()\n"
+        shader += "{\n"
+        shader += "     vertex_to_gs = vertex;\n"
+        shader += "}\n"
+
+        return shader
+
+    def make_geometry_shader(self):
+        additional_declarations = ''
+        additional_declarations += 'layout(triangles) in;\n'
+        additional_declarations \
+            += 'layout(triangle_strip, max_vertices = 3) out;\n'
+        additional_declarations += 'in vec4 vertex_to_gs[3];\n'
+        additional_declarations += 'out vec4 color;\n'
+        return self.make_test_shader(
+            additional_declarations,
+            '  vec4 tmp_color;\n',
+            'tmp_color',
+            '  for (int i = 0; i < 3; i++) {\n'
+            '    gl_Position = vertex_to_gs[i];\n'
+            '    color = tmp_color;\n'
+            '    EmitVertex();\n'
+            '  }\n')
+
+    def make_fragment_shader(self):
+        shader = '''varying vec4 color;
+
+void main()
+{
+  gl_FragColor = color;
+}
+'''
+        return shader
+
+
+class FragmentShaderTest(ShaderTest):
+    """Derived class for tests that exercise the built-in in a
+    fragment shader.
+    """
+    def test_prefix(self):
+        return 'fs'
+
+    def make_vertex_shader(self):
+        shader = ""
+        if self.glsl_version() >= 140:
+            shader += "in vec4 vertex;\n"
+
+        shader += "void main()\n"
+        shader += "{\n"
+        if self.glsl_version() >= 140:
+            shader += "        gl_Position = vertex;\n"
+        else:
+            shader += "        gl_Position = gl_Vertex;\n"
+        shader += "}\n"
+
+        return shader
+
+    def make_fragment_shader(self):
+        return self.make_test_shader('', '', 'gl_FragColor', '')
+
+
+def all_tests():
+    for use_if in [False, True]:
+        for signature, test_vectors in sorted(test_suite.items()):
+            if use_if and signature.rettype != glsl_bool:
+                continue
+            yield VertexShaderTest(signature, test_vectors, use_if)
+            yield GeometryShaderTest(signature, test_vectors, use_if)
+            yield FragmentShaderTest(signature, test_vectors, use_if)
+
+
+def main():
+    desc = 'Generate shader tests that test built-in functions using uniforms'
+    usage = 'usage: %prog [-h] [--names-only]'
+    parser = optparse.OptionParser(description=desc, usage=usage)
+    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()
+    for test in all_tests():
+        if not options.names_only:
+            test.generate_shader_test()
+        print(test.filename())
+
+
+if __name__ == '__main__':
+    main()
-- 
2.5.5



More information about the Piglit mailing list