[Piglit] [RFC 04/12] builtin_function.py: Split module into 4 modules

Dylan Baker baker.dylan.c at gmail.com
Mon Dec 8 17:11:25 PST 2014


This patch splits the builtin_function module into 4 separate modules.
The first module contains the math functions used by builtin_function,
the second contains the glsl_types, the third some shared functions for
creating tests, and finally the original file, with only the GLSL
function definitions.

This patch is intended to allow code to be shared between the
builtin_function and builtin_function_fp64 tests, with the assumption
that the math and glsl_types might be useful for other generators.

Signed-off-by: Dylan Baker <dylanx.c.baker at intel.com>
---
 generated_tests/CMakeLists.txt                   |  12 +-
 generated_tests/builtin_function.py              | 548 +++--------------------
 generated_tests/builtin_function_fp64.py         |   2 +-
 generated_tests/builtins/__init__.py             |   0
 generated_tests/builtins/generators.py           | 124 +++++
 generated_tests/builtins/glsl_types.py           | 180 ++++++++
 generated_tests/builtins/math.py                 | 219 +++++++++
 generated_tests/gen_builtin_uniform_tests.py     |  44 +-
 generated_tests/gen_constant_array_size_tests.py |  32 +-
 9 files changed, 639 insertions(+), 522 deletions(-)
 create mode 100644 generated_tests/builtins/__init__.py
 create mode 100644 generated_tests/builtins/generators.py
 create mode 100644 generated_tests/builtins/glsl_types.py
 create mode 100644 generated_tests/builtins/math.py

diff --git a/generated_tests/CMakeLists.txt b/generated_tests/CMakeLists.txt
index 6c8eaf9..96a551e 100644
--- a/generated_tests/CMakeLists.txt
+++ b/generated_tests/CMakeLists.txt
@@ -32,11 +32,19 @@ piglit_make_generated_tests(
 piglit_make_generated_tests(
 	builtin_uniform_tests.list
 	gen_builtin_uniform_tests.py
-	builtin_function.py)
+	builtin_function.py
+	builtins/glsl_types.py
+	builtins/generators.py
+	builtins/math.py
+	)
 piglit_make_generated_tests(
 	constant_array_size_tests.list
 	gen_constant_array_size_tests.py
-	builtin_function.py)
+	builtin_function.py
+	builtins/glsl_types.py
+	builtins/generators.py
+	builtins/math.py
+	)
 piglit_make_generated_tests(
 	const_builtin_equal_tests.list
 	gen_const_builtin_equal_tests.py
diff --git a/generated_tests/builtin_function.py b/generated_tests/builtin_function.py
index 7f9fae1..1d96b65 100644
--- a/generated_tests/builtin_function.py
+++ b/generated_tests/builtin_function.py
@@ -1,6 +1,6 @@
 # coding=utf-8
 #
-# Copyright © 2011 Intel Corporation
+# Copyright © 2011, 2014 Intel Corporation
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
 # copy of this software and associated documentation files (the "Software"),
@@ -48,256 +48,13 @@
 # swizzling, the function call operator, assignment, and the sequence
 # operator.
 
-import collections
 import itertools
-import numpy as np
-
-
-# Floating point types used by Python and numpy
-FLOATING_TYPES = (float, np.float64, np.float32)
-
-# Due to a bug in the Windows implementation of numpy, there are
-# multiple int32 types (and multiple uint32 types).  So we have to
-# find them all when doing isinstance checks.  The following code will
-# create two-element tuples on numpy implementations that have the
-# bug, and one-element tuples on numpy implementations that don't.
-INT32_TYPES = tuple(set([np.int32, type(np.abs(np.int32(1)))]))
-UINT32_TYPES = tuple(set([np.uint32,
-                          type(np.dot(np.uint32(0), np.uint32(0)))]))
-
-
-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 __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_int    = GlslBuiltinType('int',    None,       1, 1, 110)
-glsl_uint   = GlslBuiltinType('uint',   None,       1, 1, 130)
-glsl_float  = GlslBuiltinType('float',  None,       1, 1, 110)
-glsl_vec2   = GlslBuiltinType('vec2',   glsl_float, 1, 2, 110)
-glsl_vec3   = GlslBuiltinType('vec3',   glsl_float, 1, 3, 110)
-glsl_vec4   = GlslBuiltinType('vec4',   glsl_float, 1, 4, 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_ivec2  = GlslBuiltinType('ivec2',  glsl_int,   1, 2, 110)
-glsl_ivec3  = GlslBuiltinType('ivec3',  glsl_int,   1, 3, 110)
-glsl_ivec4  = GlslBuiltinType('ivec4',  glsl_int,   1, 4, 110)
-glsl_uvec2  = GlslBuiltinType('uvec2',  glsl_uint,  1, 2, 130)
-glsl_uvec3  = GlslBuiltinType('uvec3',  glsl_uint,  1, 3, 130)
-glsl_uvec4  = GlslBuiltinType('uvec4',  glsl_uint,  1, 4, 130)
-glsl_mat2   = GlslBuiltinType('mat2',   glsl_float, 2, 2, 110)
-glsl_mat3   = GlslBuiltinType('mat3',   glsl_float, 3, 3, 110)
-glsl_mat4   = GlslBuiltinType('mat4',   glsl_float, 4, 4, 110)
-glsl_mat2x2 = glsl_mat2
-glsl_mat3x2 = GlslBuiltinType('mat3x2', glsl_float, 3, 2, 120)
-glsl_mat4x2 = GlslBuiltinType('mat4x2', glsl_float, 4, 2, 120)
-glsl_mat2x3 = GlslBuiltinType('mat2x3', glsl_float, 2, 3, 120)
-glsl_mat3x3 = glsl_mat3
-glsl_mat4x3 = GlslBuiltinType('mat4x3', glsl_float, 4, 3, 120)
-glsl_mat2x4 = GlslBuiltinType('mat2x4', glsl_float, 2, 4, 120)
-glsl_mat3x4 = GlslBuiltinType('mat3x4', glsl_float, 3, 4, 120)
-glsl_mat4x4 = glsl_mat4
-
-
-# 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 float32 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, FLOATING_TYPES):
-        return glsl_float
-    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:
-        assert isinstance(value, np.ndarray)
-        if len(value.shape) == 1:
-            # Vector
-            vector_length = value.shape[0]
-            assert 2 <= vector_length <= 4
-            if value.dtype in FLOATING_TYPES:
-                return (glsl_vec2, glsl_vec3, glsl_vec4)[vector_length - 2]
-            elif value.dtype == bool:
-                return (glsl_bvec2, glsl_bvec3, glsl_bvec4)[vector_length - 2]
-            elif value.dtype in INT32_TYPES:
-                return (glsl_ivec2, glsl_ivec3, glsl_ivec4)[vector_length - 2]
-            elif value.dtype in UINT32_TYPES:
-                return (glsl_uvec2, glsl_uvec3, glsl_uvec4)[vector_length - 2]
-            else:
-                raise Exception(
-                    'Unexpected vector base type {0}'.format(value.dtype))
-        else:
-            # Matrix
-            assert value.dtype in FLOATING_TYPES
-            assert len(value.shape) == 2
-            matrix_rows = value.shape[0]
-            assert 2 <= matrix_rows <= 4
-            matrix_columns = value.shape[1]
-            assert 2 <= matrix_columns <= 4
-            matrix_types = ((glsl_mat2x2, glsl_mat2x3, glsl_mat2x4),
-                            (glsl_mat3x2, glsl_mat3x3, glsl_mat3x4),
-                            (glsl_mat4x2, glsl_mat4x3, glsl_mat4x4))
-            return matrix_types[matrix_columns - 2][matrix_rows - 2]
-
-
-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]
-    elif column_major.dtype in UINT32_TYPES:
-        values = [repr(x) + 'u' for x in column_major]
-    else:
-        values = [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))
-
-
-def round_to_32_bits(value):
-    """If value is a floating point type, round it down to 32 bits.
-    Otherwise return it unchanged.
-    """
-    if isinstance(value, float):
-        return np.float32(value)
-    elif isinstance(value, np.ndarray) and value.dtype == np.float64:
-        return np.array(value, dtype=np.float32)
-    else:
-        return value
-
 
-def extend_to_64_bits(value):
-    """If value is a floating point type, extend it to 64 bits.
-    Otherwise return it unchanged.
-    """
-    if isinstance(value, np.float32):
-        return np.float64(value)
-    elif isinstance(value, np.ndarray) and value.dtype == np.float32:
-        return np.array(value, dtype=np.float64)
-    else:
-        return value
+import numpy as np
 
+from builtins import glsl_types
+from builtins import math
+from builtins import generators
 
 # Dictionary containing the test vectors.  Each entry in the
 # dictionary represents a single overload of a single built-in
@@ -309,196 +66,12 @@ def extend_to_64_bits(value):
 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_int:
-        # 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_uint:
-        return x // y
-    else:
-        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
-    if glsl_type_of(x).base_type != glsl_type_of(y).base_type:
-        y = _change_signedness(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
-    if glsl_type_of(x).base_type != glsl_type_of(y).base_type:
-        y = _change_signedness(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 _normalize(x):
-    return x/np.linalg.norm(x)
-
-
-def _faceforward(N, I, Nref):
-    if np.dot(Nref, I) < 0.0:
-        return N
-    else:
-        return -N
-
-
-def _reflect(I, N):
-    return I-2*np.dot(N, I)*N
-
-
-def _refract(I, N, eta):
-    k = 1.0-eta*eta*(1.0-np.dot(N, I)*np.dot(N, I))
-    if k < 0.0:
-        return I*0.0
-    else:
-        return eta*I-(eta*np.dot(N, I)+np.sqrt(k))*N
-
-
-def _change_signedness(x):
-    """Change signed integer types to unsigned integer types and vice
-    versa."""
-    if isinstance(x, INT32_TYPES):
-        return np.uint32(x)
-    elif isinstance(x, UINT32_TYPES):
-        return np.int32(x)
-    elif isinstance(x, np.ndarray):
-        if (x.dtype in INT32_TYPES):
-            return np.array(x, dtype=np.uint32)
-        elif (x.dtype in UINT32_TYPES):
-            return np.array(x, dtype=np.int32)
-    raise Exception('Unexpected type passed to _change_signedness')
-
-
 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]
+    types = [glsl_types.glsl_type_of(arguments[i]) for
+             i in argument_indices_to_match]
     return all(x == types[0] for x in types)
 
 
@@ -595,15 +168,18 @@ def _simulate_function(test_inputs, python_equivalent, tolerance_function):
     """
     test_vectors = []
     for inputs in test_inputs:
-        expected_output = round_to_32_bits(
-            python_equivalent(*[extend_to_64_bits(x) for x in inputs]))
+        expected_output = generators.round_to_32_bits(
+            python_equivalent(
+                *[generators.extend_to_64_bits(x) for x in inputs]))
         if expected_output is not None:
-            if glsl_type_of(expected_output).base_type != glsl_float:
+            if (glsl_types.glsl_type_of(expected_output).base_type !=
+                    glsl_types.GLSL_FLOAT):
                 tolerance = np.float32(0.0)
             else:
                 tolerance = np.float32(
                     tolerance_function(inputs, expected_output))
-            test_vectors.append(TestVector(inputs, expected_output, tolerance))
+            test_vectors.append(
+                generators.TestVector(inputs, expected_output, tolerance))
     return test_vectors
 
 
@@ -667,7 +243,7 @@ def _vectorize_test_vectors(test_vectors, scalar_arg_indices, vector_length):
         result = np.array([tv.result for tv in test_vectors])
         tolerance = np.float32(
             np.linalg.norm([tv.tolerance for tv in test_vectors]))
-        return TestVector(arguments, result, tolerance)
+        return generators.TestVector(arguments, result, tolerance)
     vectorized_test_vectors = []
     groups = make_groups(test_vectors)
     for key in sorted(groups.keys()):
@@ -695,12 +271,13 @@ def _store_test_vector(test_suite_dict, name, glsl_version, extension, test_vect
         arg_indices = xrange(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)
+    rettype = glsl_types.glsl_type_of(test_vector.result)
+    argtypes = tuple(
+        glsl_types.glsl_type_of(arg) for arg in test_vector.arguments)
     adjusted_glsl_version = max(
         glsl_version, rettype.version_introduced,
         *[t.version_introduced for t in argtypes])
-    signature = Signature(
+    signature = generators.Signature(
         name, template, adjusted_glsl_version, extension, rettype, argtypes)
     if signature not in test_suite_dict:
         test_suite_dict[signature] = []
@@ -736,7 +313,8 @@ def make_arguments(input_generators):
     values are passed into OpenGL.
     """
     input_generators = [
-        [round_to_32_bits(x) for x in seq] for seq in input_generators]
+        [generators.round_to_32_bits(x) for x in seq]
+        for seq in input_generators]
     return list(itertools.product(*input_generators))
 
 
@@ -812,7 +390,7 @@ def _make_componentwise_test_vectors(test_suite_dict):
     f('acos', 1, 110, np.arccos, None, [np.linspace(-1.0, 1.0, 4)],
       _trig_tolerance)
     f('atan', 1, 110, np.arctan, None, [atan_inputs], _trig_tolerance)
-    f('atan', 2, 110, _arctan2, None, [atan_inputs, atan_inputs],
+    f('atan', 2, 110, math.arctan2, None, [atan_inputs, atan_inputs],
       _trig_tolerance)
     f('sinh', 1, 130, np.sinh, None, [np.linspace(-2.0, 2.0, 4)],
       _trig_tolerance)
@@ -824,11 +402,11 @@ def _make_componentwise_test_vectors(test_suite_dict):
     f('acosh', 1, 130, np.arccosh, None, [acosh_inputs], _trig_tolerance)
     f('atanh', 1, 130, np.arctanh, None, [np.linspace(-0.99, 0.99, 4)],
       _trig_tolerance)
-    f('pow', 2, 110, _pow, None, [np.linspace(0.0, 2.0, 4),
+    f('pow', 2, 110, math.pow, None, [np.linspace(0.0, 2.0, 4),
       np.linspace(-2.0, 2.0, 4)])
     f('exp', 1, 110, np.exp, None, [np.linspace(-2.0, 2.0, 4)])
     f('log', 1, 110, np.log, None, [np.linspace(0.01, 2.0, 4)])
-    f('exp2', 1, 110, _exp2, None, [np.linspace(-2.0, 2.0, 4)])
+    f('exp2', 1, 110, math.exp2, None, [np.linspace(-2.0, 2.0, 4)])
     f('log2', 1, 110, np.log2, None, [np.linspace(0.01, 2.0, 4)])
     f('sqrt', 1, 110, np.sqrt, None, [np.linspace(0.0, 2.0, 4)])
     f('inversesqrt', 1, 110, lambda x: 1.0/np.sqrt(x), None,
@@ -838,7 +416,7 @@ def _make_componentwise_test_vectors(test_suite_dict):
     f('sign', 1, 110, np.sign, None, [np.linspace(-1.5, 1.5, 5)])
     f('sign', 1, 130, np.sign, None, [ints])
     f('floor', 1, 110, np.floor, None, [np.linspace(-2.0, 2.0, 4)])
-    f('trunc', 1, 130, _trunc, None, [np.linspace(-2.0, 2.0, 8)])
+    f('trunc', 1, 130, math.trunc, None, [np.linspace(-2.0, 2.0, 8)])
 
     # Note: the direction of rounding used by round() is not specified
     # for half-integer values, so we test it over a range that doesn't
@@ -880,18 +458,18 @@ def _make_componentwise_test_vectors(test_suite_dict):
       extension="AMD_shader_trinary_minmax")
     f('max3', 2, 130, max, None, [uints, uints, uints],
       extension="AMD_shader_trinary_minmax")
-    f('mid3', 2, 110, _mid3, None,
+    f('mid3', 2, 110, math.mid3, None,
       [np.linspace(-2.0, 2.0, 4), np.linspace(-2.0, 2.0, 4),
        np.linspace(-2.0, 2.0, 4)],
       extension="AMD_shader_trinary_minmax")
-    f('mid3', 2, 130, _mid3, None, [ints, ints, ints],
+    f('mid3', 2, 130, math.mid3, None, [ints, ints, ints],
       extension="AMD_shader_trinary_minmax")
-    f('mid3', 2, 130, _mid3, None, [uints, uints, uints],
+    f('mid3', 2, 130, math.mid3, None, [uints, uints, uints],
       extension="AMD_shader_trinary_minmax")
-    f('clamp', 3, 110, _clamp, [1, 2], [np.linspace(-2.0, 2.0, 4),
+    f('clamp', 3, 110, math.clamp, [1, 2], [np.linspace(-2.0, 2.0, 4),
       np.linspace(-1.5, 1.5, 3), np.linspace(-1.5, 1.5, 3)])
-    f('clamp', 3, 130, _clamp, [1, 2], [ints, ints, ints])
-    f('clamp', 3, 130, _clamp, [1, 2], [uints, uints, uints])
+    f('clamp', 3, 130, math.clamp, [1, 2], [ints, ints, ints])
+    f('clamp', 3, 130, math.clamp, [1, 2], [uints, uints, uints])
     f('mix', 3, 110, lambda x, y, a: x*(1-a)+y*a, [2],
       [np.linspace(-2.0, 2.0, 2), np.linspace(-3.0, 3.0, 2),
        np.linspace(0.0, 1.0, 4)])
@@ -899,7 +477,7 @@ def _make_componentwise_test_vectors(test_suite_dict):
       [np.linspace(-2.0, 2.0, 2), np.linspace(-3.0, 3.0, 2), bools])
     f('step', 2, 110, lambda edge, x: 0.0 if x < edge else 1.0, [0],
       [np.linspace(-2.0, 2.0, 4), np.linspace(-2.0, 2.0, 4)])
-    f('smoothstep', 3, 110, _smoothstep, [0, 1],
+    f('smoothstep', 3, 110, math.smoothstep, [0, 1],
       [np.linspace(-1.9, 1.9, 4), np.linspace(-1.9, 1.9, 4),
        np.linspace(-2.0, 2.0, 4)])
 _make_componentwise_test_vectors(test_suite)
@@ -984,8 +562,8 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         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)
+        x_type = glsl_types.glsl_type_of(x)
+        y_type = glsl_types.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:
@@ -1000,8 +578,8 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         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)
+        x_type = glsl_types.glsl_type_of(x)
+        y_type = glsl_types.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:
@@ -1031,11 +609,11 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         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_int, glsl_uint):
+        x_type = glsl_types.glsl_type_of(x)
+        y_type = glsl_types.glsl_type_of(y)
+        if x_type.base_type not in (glsl_types.GLSL_INT, glsl_types.GLSL_UINT):
             return False
-        if y_type.base_type not in (glsl_int, glsl_uint):
+        if y_type.base_type not in (glsl_types.GLSL_INT, glsl_types.GLSL_UINT):
             return False
         if y_type.is_scalar:
             return True
@@ -1050,8 +628,8 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         into a large enough type.
 
         """
-        x_type = glsl_type_of(x)
-        y_type = glsl_type_of(y)
+        x_type = glsl_types.glsl_type_of(x)
+        y_type = glsl_types.glsl_type_of(y)
         if x_type.base_type != y_type.base_type:
             return False
         if y_type.is_scalar:
@@ -1066,8 +644,8 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         matrices with the same base type, and the vector/matrix sizes
         are properly matched, and that y is scalar
         """
-        x_type = glsl_type_of(x)
-        y_type = glsl_type_of(y)
+        x_type = glsl_types.glsl_type_of(x)
+        y_type = glsl_types.glsl_type_of(y)
         if x_type.base_type != y_type.base_type:
             return False
         if y_type.is_scalar:
@@ -1158,7 +736,7 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
         np.array([-0.03, -0.85, -0.94]),
         np.array([1.67, 0.66, 1.87]),
         ]
-    norm_floats_vecs = [_normalize(x) for x in nz_floats_vecs]
+    norm_floats_vecs = [math.normalize(x) for x in nz_floats_vecs]
     squaremats = [
         np.array([[ 1.60,  0.76],
                   [ 1.53, -1.00]]),  # mat2
@@ -1263,18 +841,18 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='{0};\n  result -= {1}')
-    f('op-assign-mult', 2, 110, _multiply, match_assignment_multiply,
+    f('op-assign-mult', 2, 110, math.multiply, match_assignment_multiply,
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='{0};\n  result *= {1}')
-    f('op-assign-div', 2, 110, _divide, match_assignment_operators,
+    f('op-assign-div', 2, 110, math.divide, match_assignment_operators,
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='{0};\n  result /= {1}')
-    f('op-assign-div-large', 2, 130, _divide, match_assignment_operators,
+    f('op-assign-div-large', 2, 130, math.divide, match_assignment_operators,
       [large_uints, large_uints+small_uints],
       template='{0};\n  result /= {1}')
-    f('op-assign-mod', 2, 130, _modulus, match_assignment_operators,
+    f('op-assign-mod', 2, 130, math.modulus, match_assignment_operators,
       [ints+ivecs+uints+uvecs, ints+ivecs+uints+uvecs],
       template='{0};\n result %= {1}')
     f('op-assign-bitand', 2, 130, lambda x, y: x & y,
@@ -1289,11 +867,11 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
       match_assignment_operators,
       [ints+ivecs+uints+uvecs, ints+ivecs+uints+uvecs],
       template='{0};\n  result ^= {1}')
-    f('op-assign-lshift', 2, 130, _lshift, match_shift,
+    f('op-assign-lshift', 2, 130, math.lshift, match_shift,
       [small_ints+small_ivecs+small_uints+small_uvecs,
        small_ints+small_ivecs+small_uints+small_uvecs],
       template='{0};  result <<= {1}')
-    f('op-assign-rshift', 2, 130, _rshift, match_shift,
+    f('op-assign-rshift', 2, 130, math.rshift, match_shift,
       [small_ints+small_ivecs+small_uints+small_uvecs,
        small_ints+small_ivecs+small_uints+small_uvecs],
       template='{0};  result >>= {1}')
@@ -1305,17 +883,17 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='({0} - {1})')
-    f('op-mult', 2, 110, _multiply, match_multiply,
+    f('op-mult', 2, 110, math.multiply, match_multiply,
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='({0} * {1})')
-    f('op-div', 2, 110, _divide, match_simple_binop,
+    f('op-div', 2, 110, math.divide, match_simple_binop,
       [floats+vecs+mats+ints+ivecs+uints+uvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs],
       template='({0} / {1})')
-    f('op-div-large', 2, 130, _divide, match_simple_binop,
+    f('op-div-large', 2, 130, math.divide, match_simple_binop,
       [large_uints, large_uints+small_uints], template='({0} / {1})')
-    f('op-mod', 2, 130, _modulus, match_simple_binop,
+    f('op-mod', 2, 130, math.modulus, match_simple_binop,
       [ints+ivecs+uints+uvecs, ints+ivecs+uints+uvecs], template='({0} % {1})')
     f('op-uplus', 1, 110, lambda x: +x, None,
       [floats+vecs+mats+ints+ivecs+uints+uvecs], template='(+ {0})')
@@ -1329,11 +907,11 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
       [ints+uints+floats, ints+uints+floats], template='({0} >= {1})')
     f('op-le', 2, 110, lambda x, y: x <= y, match_args(0, 1),
       [ints+uints+floats, ints+uints+floats], template='({0} <= {1})')
-    f('op-eq', 2, 110, _equal, match_args(0, 1),
+    f('op-eq', 2, 110, math.equal, match_args(0, 1),
       [floats+vecs+mats+ints+ivecs+uints+uvecs+bools+bvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs+bools+bvecs],
       template='({0} == {1})')
-    f('op-ne', 2, 110, _not_equal, match_args(0, 1),
+    f('op-ne', 2, 110, math.not_equal, match_args(0, 1),
       [floats+vecs+mats+ints+ivecs+uints+uvecs+bools+bvecs,
        floats+vecs+mats+ints+ivecs+uints+uvecs+bools+bvecs],
       template='({0} != {1})')
@@ -1350,11 +928,11 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
       template='({0} ? {1} : {2})')
     f('op-complement', 1, 130, lambda x: ~x, None, [ints+ivecs+uints+uvecs],
       template='(~ {0})')
-    f('op-lshift', 2, 130, _lshift, match_shift,
+    f('op-lshift', 2, 130, math.lshift, match_shift,
       [small_ints+small_ivecs+small_uints+small_uvecs,
        small_ints+small_ivecs+small_uints+small_uvecs],
       template='({0} << {1})')
-    f('op-rshift', 2, 130, _rshift, match_shift,
+    f('op-rshift', 2, 130, math.rshift, match_shift,
       [small_ints+small_ivecs+small_uints+small_uvecs,
        small_ints+small_ivecs+small_uints+small_uvecs],
       template='({0} >> {1})')
@@ -1427,12 +1005,12 @@ def _make_vector_or_matrix_test_vectors(test_suite_dict):
     f('dot', 2, 110, np.dot, match_args(0, 1), [floats+vecs, floats+vecs])
     f('cross', 2, 110, np.cross, match_args(0, 1), [vec3s, vec3s],
       _cross_product_tolerance)
-    f('normalize', 1, 110, _normalize, None, [nz_floats_vecs])
-    f('faceforward', 3, 110, _faceforward, match_args(0, 1, 2),
+    f('normalize', 1, 110, math.normalize, None, [nz_floats_vecs])
+    f('faceforward', 3, 110, math.faceforward, match_args(0, 1, 2),
       [floats+vecs, floats+vecs, floats+vecs])
-    f('reflect', 2, 110, _reflect, match_args(0, 1),
+    f('reflect', 2, 110, math.reflect, match_args(0, 1),
       [floats+vecs, norm_floats_vecs])
-    f('refract', 3, 110, _refract, match_args(0, 1),
+    f('refract', 3, 110, math.refract, match_args(0, 1),
       [norm_floats_vecs, norm_floats_vecs, [0.5, 2.0]])
 
     # Note: technically matrixCompMult operates componentwise.
diff --git a/generated_tests/builtin_function_fp64.py b/generated_tests/builtin_function_fp64.py
index 6b98ec7..992323f 100644
--- a/generated_tests/builtin_function_fp64.py
+++ b/generated_tests/builtin_function_fp64.py
@@ -1,6 +1,6 @@
 # coding=utf-8
 #
-# Copyright © 2011 Intel Corporation
+# Copyright © 2011, 2014 Intel Corporation
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
 # copy of this software and associated documentation files (the "Software"),
diff --git a/generated_tests/builtins/__init__.py b/generated_tests/builtins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/generated_tests/builtins/generators.py b/generated_tests/builtins/generators.py
new file mode 100644
index 0000000..b556e77
--- /dev/null
+++ b/generated_tests/builtins/generators.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2011, 2014 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 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.
+
+"""Helper classes and methods for generating tests."""
+
+from __future__ import absolute_import
+import collections
+
+import numpy as np
+
+from . import glsl_types
+
+
+# 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 float32 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 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]
+    elif column_major.dtype in glsl_types.UINT32_TYPES:
+        values = [repr(x) + 'u' for x in column_major]
+    else:
+        values = [repr(x) for x in column_major]
+    if len(column_major) == 1:
+        return values[0]
+    else:
+        return '{0}({1})'.format(glsl_types.glsl_type_of(value),
+                                 ', '.join(values))
+
+
+def round_to_32_bits(value):
+    """If value is a floating point type, round it down to 32 bits.
+    Otherwise return it unchanged.
+    """
+    if isinstance(value, float):
+        return np.float32(value)
+    elif isinstance(value, np.ndarray) and value.dtype == np.float64:
+        return np.array(value, dtype=np.float32)
+    else:
+        return value
+
+
+def extend_to_64_bits(value):
+    """If value is a floating point type, extend it to 64 bits.
+    Otherwise return it unchanged.
+    """
+    if isinstance(value, np.float32):
+        return np.float64(value)
+    elif isinstance(value, np.ndarray) and value.dtype == np.float32:
+        return np.array(value, dtype=np.float64)
+    else:
+        return value
diff --git a/generated_tests/builtins/glsl_types.py b/generated_tests/builtins/glsl_types.py
new file mode 100644
index 0000000..7d8e361
--- /dev/null
+++ b/generated_tests/builtins/glsl_types.py
@@ -0,0 +1,180 @@
+# 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.
+
+"""Provides python instances equivalent to GLSL builtin types."""
+
+import numpy as np
+
+# Floating point types used by Python and numpy
+FLOATING_TYPES = (float, np.float64, np.float32)
+
+# Due to a bug in the Windows implementation of numpy, there are
+# multiple int32 types (and multiple uint32 types).  So we have to
+# find them all when doing isinstance checks.  The following code will
+# create two-element tuples on numpy implementations that have the
+# bug, and one-element tuples on numpy implementations that don't.
+INT32_TYPES = tuple(set([np.int32, type(np.abs(np.int32(1)))]))
+UINT32_TYPES = tuple(set([np.uint32,
+                          type(np.dot(np.uint32(0), np.uint32(0)))]))
+
+
+class GlslBuiltinType(object):
+
+    """Class representing a GLSL built-in type."""
+
+    # pylint: disable=too-many-arguments
+    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):
+        """The base type.
+
+        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 __str__(self):
+        return self.__name
+
+    def __repr__(self):
+        return 'glsl_{0}'.format(self.__name)
+
+
+# Concrete declarations of GlslBuiltinType
+# pylint: disable=bad-whitespace
+GLSL_BOOL   = GlslBuiltinType('bool',   None,       1, 1, 110)
+GLSL_INT    = GlslBuiltinType('int',    None,       1, 1, 110)
+GLSL_UINT   = GlslBuiltinType('uint',   None,       1, 1, 130)
+GLSL_FLOAT  = GlslBuiltinType('float',  None,       1, 1, 110)
+GLSL_VEC2   = GlslBuiltinType('vec2',   GLSL_FLOAT, 1, 2, 110)
+GLSL_VEC3   = GlslBuiltinType('vec3',   GLSL_FLOAT, 1, 3, 110)
+GLSL_VEC4   = GlslBuiltinType('vec4',   GLSL_FLOAT, 1, 4, 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_IVEC2  = GlslBuiltinType('ivec2',  GLSL_INT,   1, 2, 110)
+GLSL_IVEC3  = GlslBuiltinType('ivec3',  GLSL_INT,   1, 3, 110)
+GLSL_IVEC4  = GlslBuiltinType('ivec4',  GLSL_INT,   1, 4, 110)
+GLSL_UVEC2  = GlslBuiltinType('uvec2',  GLSL_UINT,  1, 2, 130)
+GLSL_UVEC3  = GlslBuiltinType('uvec3',  GLSL_UINT,  1, 3, 130)
+GLSL_UVEC4  = GlslBuiltinType('uvec4',  GLSL_UINT,  1, 4, 130)
+GLSL_MAT2   = GlslBuiltinType('mat2',   GLSL_FLOAT, 2, 2, 110)
+GLSL_MAT3   = GlslBuiltinType('mat3',   GLSL_FLOAT, 3, 3, 110)
+GLSL_MAT4   = GlslBuiltinType('mat4',   GLSL_FLOAT, 4, 4, 110)
+GLSL_MAT2X2 = GLSL_MAT2
+GLSL_MAT3X2 = GlslBuiltinType('mat3x2', GLSL_FLOAT, 3, 2, 120)
+GLSL_MAT4X2 = GlslBuiltinType('mat4x2', GLSL_FLOAT, 4, 2, 120)
+GLSL_MAT2X3 = GlslBuiltinType('mat2x3', GLSL_FLOAT, 2, 3, 120)
+GLSL_MAT3X3 = GLSL_MAT3
+GLSL_MAT4X3 = GlslBuiltinType('mat4x3', GLSL_FLOAT, 4, 3, 120)
+GLSL_MAT2X4 = GlslBuiltinType('mat2x4', GLSL_FLOAT, 2, 4, 120)
+GLSL_MAT3X4 = GlslBuiltinType('mat3x4', GLSL_FLOAT, 3, 4, 120)
+GLSL_MAT4X4 = GLSL_MAT4
+
+
+def glsl_type_of(value):  # pylint: disable=too-many-return-statements
+    """Factory function that returns a GlslBuiltinType for native types."""
+    if isinstance(value, FLOATING_TYPES):
+        return GLSL_FLOAT
+    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:
+        assert isinstance(value, np.ndarray)
+        if len(value.shape) == 1:
+            # Vector
+            vector_length = value.shape[0]
+            assert 2 <= vector_length <= 4
+            if value.dtype in FLOATING_TYPES:
+                return (GLSL_VEC2, GLSL_VEC3, GLSL_VEC4)[vector_length - 2]
+            elif value.dtype == bool:
+                return (GLSL_BVEC2, GLSL_BVEC3, GLSL_BVEC4)[vector_length - 2]
+            elif value.dtype in INT32_TYPES:
+                return (GLSL_IVEC2, GLSL_IVEC3, GLSL_IVEC4)[vector_length - 2]
+            elif value.dtype in UINT32_TYPES:
+                return (GLSL_UVEC2, GLSL_UVEC3, GLSL_UVEC4)[vector_length - 2]
+            else:
+                raise Exception(
+                    'Unexpected vector base type {0}'.format(value.dtype))
+        else:
+            # Matrix
+            assert value.dtype in FLOATING_TYPES
+            assert len(value.shape) == 2
+            matrix_rows = value.shape[0]
+            assert 2 <= matrix_rows <= 4
+            matrix_columns = value.shape[1]
+            assert 2 <= matrix_columns <= 4
+            matrix_types = ((GLSL_MAT2X2, GLSL_MAT2X3, GLSL_MAT2X4),
+                            (GLSL_MAT3X2, GLSL_MAT3X3, GLSL_MAT3X4),
+                            (GLSL_MAT4X2, GLSL_MAT4X3, GLSL_MAT4X4))
+            return matrix_types[matrix_columns - 2][matrix_rows - 2]
diff --git a/generated_tests/builtins/math.py b/generated_tests/builtins/math.py
new file mode 100644
index 0000000..403bcd3
--- /dev/null
+++ b/generated_tests/builtins/math.py
@@ -0,0 +1,219 @@
+# Copyright (c) 2011, 2014 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 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.
+
+"""Functions that simulate GLSL built-in functions.
+
+s 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.
+
+"""
+# Don't worry about docstrings for obvious mathc functions
+# Also don't worry about invalid-names, since there are lots of operator names
+# that are 1 letter
+# pylint: disable=missing-docstring,invalid-name,redefined-builtin
+
+from __future__ import absolute_import
+
+import numpy as np
+
+from . import glsl_types
+from . import generators
+
+
+def multiply(x, y):
+    x_type = glsl_types.glsl_type_of(x)
+    y_type = glsl_types.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 generators.column_major_values(y)):
+        # Division by zero is undefined.
+        return None
+    if glsl_types.glsl_type_of(x).base_type == glsl_types.GLSL_INT:
+        # 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_types.glsl_type_of(x).base_type == glsl_types.GLSL_UINT:
+        return x // y
+    else:
+        return x / y
+
+
+def modulus(x, y):
+    if any(x_element < 0 for x_element in generators.column_major_values(x)):
+        # Modulus operation with a negative first operand is
+        # undefined.
+        return None
+    if any(y_element <= 0 for y_element in generators.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
+               generators.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.
+    if (glsl_types.glsl_type_of(x).base_type !=
+            glsl_types.glsl_type_of(y).base_type):
+        y = _change_signedness(y)
+    result = x << y
+
+    # Shifting should always produce a result with the same base type
+    # as the left argument.
+    assert (glsl_types.glsl_type_of(result).base_type ==
+            glsl_types.glsl_type_of(x).base_type)
+
+    return result
+
+
+def rshift(x, y):
+    if not all(0 <= y_element < 32 for y_element in
+               generators.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.
+    if (glsl_types.glsl_type_of(x).base_type !=
+            glsl_types.glsl_type_of(y).base_type):
+        y = _change_signedness(y)
+    result = x >> y
+
+    # Shifting should always produce a result with the same base type
+    # as the left argument.
+    assert (glsl_types.glsl_type_of(result).base_type ==
+            glsl_types.glsl_type_of(x).base_type)
+
+    return result
+
+
+def equal(x, y):
+    return all(generators.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, min_val, max_val):
+    if min_val > max_val:
+        return None
+    return min(max(x, min_val), max_val)
+
+
+def mid3(x, y, z):
+    # Inefficient, but obvious
+    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 normalize(x):
+    return x / np.linalg.norm(x)
+
+
+def faceforward(N, I, Nref):
+    if np.dot(Nref, I) < 0.0:
+        return N
+    else:
+        return -N
+
+
+def reflect(I, N):
+    return I - 2 * np.dot(N, I) * N
+
+
+def refract(I, N, eta):
+    k = 1.0 - eta * eta * (1.0 - np.dot(N, I) * np.dot(N, I))
+    if k < 0.0:
+        return I * 0.0
+    else:
+        return eta * I -(eta * np.dot(N, I) + np.sqrt(k)) * N
+
+
+def _change_signedness(x):
+    """Change signed integer types to unsigned integer types and vice
+    versa."""
+    if isinstance(x, glsl_types.INT32_TYPES):
+        return np.uint32(x)
+    elif isinstance(x, glsl_types.UINT32_TYPES):
+        return np.int32(x)
+    elif isinstance(x, np.ndarray):
+        if (x.dtype in glsl_types.INT32_TYPES):
+            return np.array(x, dtype=np.uint32)
+        elif (x.dtype in glsl_types.UINT32_TYPES):
+            return np.array(x, dtype=np.int32)
+    raise Exception('Unexpected type passed to _change_signedness')
diff --git a/generated_tests/gen_builtin_uniform_tests.py b/generated_tests/gen_builtin_uniform_tests.py
index 369db77..4e83bb6 100644
--- a/generated_tests/gen_builtin_uniform_tests.py
+++ b/generated_tests/gen_builtin_uniform_tests.py
@@ -1,6 +1,6 @@
 # coding=utf-8
 #
-# Copyright © 2011 Intel Corporation
+# Copyright © 2011, 2014 Intel Corporation
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
 # copy of this software and associated documentation files (the "Software"),
@@ -22,7 +22,7 @@
 # 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.
+built-in function, based on the test vectors computed by glsl_types_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
@@ -43,14 +43,15 @@ doesn't generate them.
 
 """
 
+from __future__ import absolute_import
 import abc
 import optparse
 import os
-import os.path
 
 import numpy as np
 
-import builtin_function as builtin
+from builtins import glsl_types, generators
+import builtin_function
 
 
 def compute_offset_and_scale(test_vectors):
@@ -92,7 +93,7 @@ def shader_runner_type(glsl_type):
     Boolean values and vectors are converted to ints, and square
     matrices are written in "matNxN" form.
     """
-    if glsl_type.base_type == builtin.glsl_bool:
+    if glsl_type.base_type == glsl_types.GLSL_BOOL:
         if glsl_type.is_scalar:
             return 'int'
         else:
@@ -162,7 +163,7 @@ class BoolComparator(Comparator):
         floats representing the expected color produced by the test.
         """
         value = value*1.0  # convert bools to floats
-        value = builtin.column_major_values(value)
+        value = generators.column_major_values(value)
         value += [0.0] * self.__padding
         return value
 
@@ -186,7 +187,7 @@ class BoolIfComparator(Comparator):
           output_var = vecp(0.0, 0.0, 1.0, 1.0);
     """
     def __init__(self, signature):
-        assert signature.rettype == builtin.glsl_bool
+        assert signature.rettype == glsl_types.GLSL_BOOL
         self.__padding = 4 - signature.rettype.num_rows
 
     def make_result_handler(self, invocation, output_var):
@@ -246,7 +247,7 @@ class IntComparator(Comparator):
     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(builtin.column_major_values(
+            shader_runner_format(generators.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)
@@ -314,7 +315,7 @@ class FloatComparator(Comparator):
     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(builtin.column_major_values(
+            shader_runner_format(generators.column_major_values(
                 test_vector.result)))
         test += 'uniform float tolerance {0}\n'.format(
             shader_runner_format([test_vector.tolerance]))
@@ -334,25 +335,25 @@ class ShaderTest(object):
     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
+        glsl_types_function.test_suite dict), and test_vectors is the
+        list of test vectors for testing the given glsl_types (the
+        corresponding value from the glsl_types_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
+        by using it in an if statement--this only works for glsl_typess
         returning bool.
         """
         self._signature = signature
         self._test_vectors = test_vectors
         if use_if:
             self._comparator = BoolIfComparator(signature)
-        elif signature.rettype.base_type == builtin.glsl_bool:
+        elif signature.rettype.base_type == glsl_types.GLSL_BOOL:
             self._comparator = BoolComparator(signature)
-        elif signature.rettype.base_type == builtin.glsl_float:
+        elif signature.rettype.base_type == glsl_types.GLSL_FLOAT:
             self._comparator = FloatComparator(signature)
-        elif signature.rettype.base_type in (builtin.glsl_int,
-                                             builtin.glsl_uint):
+        elif signature.rettype.base_type in (glsl_types.GLSL_INT,
+                                             glsl_types.GLSL_UINT):
             self._comparator = IntComparator(signature)
         else:
             raise Exception('Unexpected rettype {0}'.format(signature.rettype))
@@ -466,8 +467,9 @@ class ShaderTest(object):
             for i in xrange(len(test_vector.arguments)):
                 test += 'uniform {0} arg{1} {2}\n'.format(
                     shader_runner_type(self._signature.argtypes[i]),
-                    i, shader_runner_format(
-                        builtin.column_major_values(test_vector.arguments[i])))
+                    i,
+                    shader_runner_format(generators.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(
@@ -686,8 +688,8 @@ fb tex 2d 0
 
 def all_tests():
     for use_if in [False, True]:
-        for signature, test_vectors in sorted(builtin.test_suite.items()):
-            if use_if and signature.rettype != builtin.glsl_bool:
+        for signature, test_vectors in sorted(builtin_function.test_suite.items()):
+            if use_if and signature.rettype != glsl_types.GLSL_BOOL:
                 continue
             yield VertexShaderTest(signature, test_vectors, use_if)
             yield GeometryShaderTest(signature, test_vectors, use_if)
diff --git a/generated_tests/gen_constant_array_size_tests.py b/generated_tests/gen_constant_array_size_tests.py
index fcbd05a..d165fee 100644
--- a/generated_tests/gen_constant_array_size_tests.py
+++ b/generated_tests/gen_constant_array_size_tests.py
@@ -34,12 +34,14 @@
 # With the optional argument --names-only, it only outputs the names
 # of the files; it doesn't generate them.
 
-from builtin_function import *
 import abc
 import optparse
 import os
 import os.path
 
+import builtin_function
+from builtins import glsl_types, generators
+
 
 class ParserTest(object):
     """Class used to build a test of a single built-in.  This is an
@@ -103,8 +105,8 @@ class ParserTest(object):
         correct result for the given test vector, and false if not.
         """
         invocation = self.__signature.template.format(
-            *[glsl_constant(x) for x in test_vector.arguments])
-        if self.__signature.rettype.base_type == glsl_float:
+            *[generators.glsl_constant(x) for x in test_vector.arguments])
+        if self.__signature.rettype.base_type == glsl_types.GLSL_FLOAT:
             # Test floating-point values within tolerance
             if self.__signature.name == 'distance':
                 # Don't use the distance() function to test itself.
@@ -121,15 +123,16 @@ class ParserTest(object):
                 for col in xrange(self.__signature.rettype.num_cols):
                     terms.append('pow(distance({0}[{1}], {2}), 2)'.format(
                         invocation, col,
-                        glsl_constant(test_vector.result[:, col])))
+                        generators.glsl_constant(test_vector.result[:, col])))
                 rss_distance = ' + '.join(terms)
                 sq_tolerance = test_vector.tolerance * test_vector.tolerance
                 return '{0} <= {1}'.format(
-                    rss_distance, glsl_constant(sq_tolerance))
+                    rss_distance, generators.glsl_constant(sq_tolerance))
             else:
                 return 'distance({0}, {1}) <= {2}'.format(
-                    invocation, glsl_constant(test_vector.result),
-                    glsl_constant(test_vector.tolerance))
+                    invocation,
+                    generators.glsl_constant(test_vector.result),
+                    generators.glsl_constant(test_vector.tolerance))
         else:
             # Test non-floating point values exactly
             assert not self.__signature.rettype.is_matrix
@@ -140,14 +143,16 @@ class ParserTest(object):
                 for row in xrange(self.__signature.rettype.num_rows):
                     terms.append('{0}[{1}] == {2}'.format(
                         invocation, row,
-                        glsl_constant(test_vector.result[row])))
+                        generators.glsl_constant(test_vector.result[row])))
                 return ' && '.join(terms)
             elif self.__signature.rettype.is_vector:
                 return 'all(equal({0}, {1}))'.format(
-                    invocation, glsl_constant(test_vector.result))
+                    invocation,
+                    generators.glsl_constant(test_vector.result))
             else:
                 return '{0} == {1}'.format(
-                    invocation, glsl_constant(test_vector.result))
+                    invocation,
+                    generators.glsl_constant(test_vector.result))
 
     def make_shader(self):
         """Generate the shader code necessary to test the built-in."""
@@ -197,8 +202,9 @@ class ParserTest(object):
         for test_vector in self.__test_vectors:
             parser_test += ' * {0} => {1}\n'.format(
                 self.__signature.template.format(
-                    *[glsl_constant(arg) for arg in test_vector.arguments]),
-                glsl_constant(test_vector.result))
+                    *[generators.glsl_constant(arg) for
+                      arg in test_vector.arguments]),
+                generators.glsl_constant(test_vector.result))
         parser_test += ' */\n'
         parser_test += self.make_shader()
         filename = self.filename()
@@ -246,7 +252,7 @@ class FragmentParserTest(ParserTest):
 
 
 def all_tests():
-    for signature, test_vectors in sorted(test_suite.items()):
+    for signature, test_vectors in sorted(builtin_function.test_suite.items()):
         # Assignment operators other than = cannot be used in the constant
         # array size tests
         if not signature.name.startswith('op-assign'):
-- 
2.2.0



More information about the Piglit mailing list