[Piglit] [PATCH 1/5] arb_uniform_buffer_object: Random test generation infrastructure

Dylan Baker baker.dylan.c at gmail.com
Tue Nov 10 12:06:09 PST 2015


On Wed, Sep 24, 2014 at 09:47:38AM -0700, Ian Romanick wrote:
> From: Ian Romanick <ian.d.romanick at intel.com>
> 
> This is the core of the random test generation infrastructure.  This
> Python script can be used stand-alone to generate fully random tests, or
> it can be called from other Python code to generate tests in a more
> directed manner.  Examples of both uses are coming in future patches.
> 
> Signed-off-by: Ian Romanick <ian.d.romanick at intel.com>
> ---
>  generated_tests/random_ubo.py | 1702 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 1702 insertions(+)
>  create mode 100644 generated_tests/random_ubo.py
> 
> diff --git a/generated_tests/random_ubo.py b/generated_tests/random_ubo.py
> new file mode 100644
> index 0000000..f47f2b8
> --- /dev/null
> +++ b/generated_tests/random_ubo.py
> @@ -0,0 +1,1702 @@
> +#!/usr/bin/env python2
> +
> +# Copyright (c) 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.
> +
> +import random
> +import abc
> +import collections
> +import struct
> +import sys
> +from mako.template import Template
> +from textwrap import dedent
> +
> +struct_types = dict()
> +
> +all130_types = [
> +    "float",  "vec2",  "vec3",  "vec4",
> +    "int",    "ivec2", "ivec3", "ivec4",
> +    "uint",   "uvec2", "uvec3", "uvec4",
> +    "bool",   "bvec2", "bvec3", "bvec4",
> +
> +    "mat2",    "mat2x3",  "mat2x4",
> +    "mat3x2",  "mat3",    "mat3x4",
> +    "mat4x2",  "mat4x3",  "mat4"
> +]
> +
> +double_types = [
> +    "double", "dvec2", "dvec3", "dvec4",
> +
> +    "dmat2",   "dmat2x3", "dmat2x4",
> +    "dmat3x2", "dmat3",   "dmat3x4",
> +    "dmat4x2", "dmat4x3", "dmat4"
> +]
> +
> +all400_types = all130_types + double_types
> +
> +# All known types, including the redundant NxN matrix types.
> +all_types = [ "mat2x2",  "mat3x3",  "mat4x4",
> +             "dmat2x2", "dmat3x3", "dmat4x4"] + all400_types
> +
> +type_enum = {
> +    'float': "GL_FLOAT",
> +    'vec2':  "GL_FLOAT_VEC2",
> +    'vec3':  "GL_FLOAT_VEC3",
> +    'vec4':  "GL_FLOAT_VEC4",
> +
> +    'double': "GL_DOUBLE",
> +    'dvec2':  "GL_DOUBLE_VEC2",
> +    'dvec3':  "GL_DOUBLE_VEC3",
> +    'dvec4':  "GL_DOUBLE_VEC4",
> +
> +    'int':   "GL_INT",
> +    'ivec2': "GL_INT_VEC2",
> +    'ivec3': "GL_INT_VEC3",
> +    'ivec4': "GL_INT_VEC4",
> +
> +    'uint':  "GL_UNSIGNED_INT",
> +    'uvec2': "GL_UNSIGNED_INT_VEC2",
> +    'uvec3': "GL_UNSIGNED_INT_VEC3",
> +    'uvec4': "GL_UNSIGNED_INT_VEC4",
> +
> +    'bool':  "GL_BOOL",
> +    'bvec2': "GL_BOOL_VEC2",
> +    'bvec3': "GL_BOOL_VEC3",
> +    'bvec4': "GL_BOOL_VEC4",
> +
> +    'mat2':   "GL_FLOAT_MAT2",
> +    'mat2x2': "GL_FLOAT_MAT2",
> +    'mat2x3': "GL_FLOAT_MAT2x3",
> +    'mat2x4': "GL_FLOAT_MAT2x4",
> +    
> +    'mat3':   "GL_FLOAT_MAT3",
> +    'mat3x2': "GL_FLOAT_MAT3x2",
> +    'mat3x3': "GL_FLOAT_MAT3",
> +    'mat3x4': "GL_FLOAT_MAT3x4",
> +
> +    'mat4':   "GL_FLOAT_MAT4",
> +    'mat4x2': "GL_FLOAT_MAT4x2",
> +    'mat4x3': "GL_FLOAT_MAT4x3",
> +    'mat4x4': "GL_FLOAT_MAT4",
> +
> +    'dmat2':   "GL_DOUBLE_MAT2",
> +    'dmat2x2': "GL_DOUBLE_MAT2",
> +    'dmat2x3': "GL_DOUBLE_MAT2x3",
> +    'dmat2x4': "GL_DOUBLE_MAT2x4",
> +    
> +    'dmat3':   "GL_DOUBLE_MAT3",
> +    'dmat3x2': "GL_DOUBLE_MAT3x2",
> +    'dmat3x3': "GL_DOUBLE_MAT3",
> +    'dmat3x4': "GL_DOUBLE_MAT3x4",
> +
> +    'dmat4':   "GL_DOUBLE_MAT4",
> +    'dmat4x2': "GL_DOUBLE_MAT4x2",
> +    'dmat4x3': "GL_DOUBLE_MAT4x3",
> +    'dmat4x4': "GL_DOUBLE_MAT4",
> +}
> +
> +def align(offset, alignment):
> +    return ((offset + alignment - 1) / alignment) * alignment
> +
> +def array_elements(type):
> +    if "[" not in type:
> +        return 0
> +
> +    # Is there a better way to do this?
> +    return int(type.split("[")[1].split("]")[0])
> +
> +def matrix_dimensions(type):
> +    if "x" in type:
> +        s = type[-3:].split("x")
> +        return (int(s[0]), int(s[1]))
> +    else:
> +        d = int(type[-1:])
> +        return (d, d)
> +    
> +class packing_rules:
> +    __metaclass__ = abc.ABCMeta
> +
> +    @abc.abstractproperty
> +    def layout_string(self):
> +        """Get the string used in a layout qualifier to select this set of
> +           layout rules."""
> +        return NotImplemented
> +
> +    @abc.abstractproperty
> +    def fixed_offsets(self):
> +        """Do fields in this layout have fixed locations (e.g., std140) or can
> +           they vary among implementations (e.g., shared or packed)?"""
> +        return NotImplemented
> +
> +    @abc.abstractmethod
> +    def base_alignment(self, type, row_major):
> +        """Determine the base alignment, in bytes, of the named type"""
> +        return NotImplemented
> +
> +    @abc.abstractmethod
> +    def matrix_stride(self, type, row_major):
> +        """Determine the stride, in bytes, from one indexable vector of the
> +           matrix (column or row depending on the orientation) to the next."""
> +        return NotImplemented
> +
> +    @abc.abstractmethod
> +    def array_stride(self, type, row_major):
> +        """Determine the stride, in bytes, from one array element to the next.
> +           If the type is not an array type, zero is returned."""
> +        return NotImplemented
> +
> +    def size(self, type, row_major):
> +        if "[" in type:
> +            return self.array_stride(type, row_major) * array_elements(type)
> +
> +        if type in ["float", "bool", "int", "uint"]:
> +            return 4
> +
> +        if type == "double":
> +            return 8
> +
> +        if type in ["vec2", "bvec2", "ivec2", "uvec2"]:
> +            return 2 * 4
> +
> +        if type == "dvec2":
> +            return 2 * 8
> +
> +        if type in ["vec3", "bvec3", "ivec3", "uvec3"]:
> +            return 3 * 4
> +
> +        if type == "dvec3":
> +            return 3 * 8
> +
> +        if type in ["vec4", "bvec4", "ivec4", "uvec4"]:
> +            return 4 * 4
> +
> +        if type == "dvec4":
> +            return 4 * 8
> +
> +        if "mat" in type:
> +            (c, r) = matrix_dimensions(type)
> +            if not row_major:
> +                return c * self.matrix_stride(type, row_major)
> +            else:
> +                return r * self.matrix_stride(type, row_major)
> +
> +        global struct_types
> +        if type not in struct_types:
> +            raise BaseException("Unknown type {}".format(type))   
> +
> +        s = 0
> +        fields = struct_types[type]
> +        for (t, n) in fields:
> +            a = self.base_alignment(t, row_major)
> +
> +            s = align(s, a) + self.size(t, row_major)
> +
> +        s = align(s, self.base_alignment(type, row_major))
> +        return s
> +
> +
> +def isscalar(type):
> +    return type in ["float", "bool", "int", "uint", "double"]
> +
> +
> +def isvector(type):
> +    return type in [ "vec2",  "vec3",  "vec4",
> +                     "ivec2", "ivec3", "ivec4",
> +                     "uvec2", "uvec3", "uvec4",
> +                     "bvec2", "bvec3", "bvec4",
> +                     "dvec2", "dvec3", "dvec4" ]
> +
> +def ismatrix(type):
> +    return type in [ "mat2",    "mat3",    "mat4",
> +                     "mat2x2",  "mat2x3",  "mat2x4",
> +                     "mat3x2",  "mat3x3",  "mat3x4",
> +                     "mat4x2",  "mat4x3",  "mat4x4",
> +                     "dmat2",   "dmat3",   "dmat4",
> +                     "dmat2x2", "dmat2x3", "dmat2x4",
> +                     "dmat3x2", "dmat3x3", "dmat3x4",
> +                     "dmat4x2", "dmat4x3", "dmat4x4" ]
> +
> +
> +def isarray(type):
> +    return "[" in type
> +
> +
> +def isstructure(type):
> +    return not (isscalar(type) or isvector(type) or ismatrix(type) or
> +                isarray(type))
> +
> +
> +def vector_size(type):
> +    if isvector(type):
> +        return int(type[-1:])
> +
> +    raise BaseException("Non-vector type {}".format(type))
> +
> +
> +def basic_machine_units(type):
> +    if type in ["float", "bool", "int", "uint"]:
> +        return 4
> +
> +    if type == "double":
> +        return 8
> +
> +    raise BaseException("Non-scalar type {}".format(type))
> +
> +
> +def array_base_type(type):
> +    if not isarray(type):
> +        raise BaseException("Non-array type {}".format(type))
> +
> +    return type.split("[")[0]
> +
> +
> +def component_type(type):
> +    if isscalar(type):
> +        return type
> +    elif isvector(type):
> +        if type[0] == 'v':
> +            return "float"
> +        elif type[0] == 'i':
> +            return "int"
> +        elif type[0] == 'u':
> +            return "uint"
> +        elif type[0] == 'b':
> +            return "bool"
> +        elif type[0] == 'd':
> +            return "double"
> +        else:
> +            raise BaseException("Unknown vector type {}".format(type))
> +    elif ismatrix(type):
> +        # Should this return the vector type or the scalar type?
> +        raise BaseException("Add support for matrix types when necessary.")
> +
> +    raise BaseException("Invalid type {}.  Perhaps a structure?".format(type))
> +
> +
> +class std140_packing_rules(packing_rules):
> +    def layout_string(self):
> +        return "std140"
> +
> +    def fixed_offsets(self):
> +        return True
> +
> +    def base_alignment(self, type, row_major):
> +        # (4) If the member is an array of scalars or vectors, the base
> +        #     alignment and array stride are set to match the base alignment
> +        #     of a single array element, according to rules (1), (2), and (3),
> +        #     and rounded up to the base alignment of a vec4. The array may
> +        #     have padding at the end; the base offset of the member following
> +        #     the array is rounded up to the next multiple of the base
> +        #     alignment.
> +
> +        if isarray(type):
> +            return max(16,
> +                       self.base_alignment(array_base_type(type), row_major))
> +
> +        # (1) If the member is a scalar consuming <N> basic machine units, the
> +        #     base alignment is <N>.
> +
> +        if isscalar(type):
> +            return basic_machine_units(type)
> +
> +        if isvector(type):
> +            # (2) If the member is a two- or four-component vector with
> +            #     components consuming <N> basic machine units, the base
> +            #     alignment is 2<N> or 4<N>, respectively.
> +            #
> +            # (3) If the member is a three-component vector with components
> +            #     consuming <N> basic machine units, the base alignment is
> +            #     4<N>.
> +
> +            components = vector_size(type)
> +            if components == 2 or components == 4:
> +                return components * basic_machine_units(component_type(type))
> +            elif components == 3:
> +                return 4 * basic_machine_units(component_type(type))
> +
> +            raise BaseException("Invalid vector size {} for type {}".format(
> +                    components,
> +                    type))
> +        elif ismatrix(type):
> +            return self.matrix_stride(type, row_major)
> +
> +        global struct_types
> +        if type not in struct_types:
> +            raise BaseException("Unknown type {}".format(type))   
> +
> +        # (9) If the member is a structure, the base alignment of the
> +        #     structure is <N>, where <N> is the largest base alignment value
> +        #     of any of its members, and rounded up to the base alignment of a
> +        #     vec4. The individual members of this sub-structure are then
> +        #     assigned offsets by applying this set of rules recursively,
> +        #     where the base offset of the first member of the sub-structure
> +        #     is equal to the aligned offset of the structure. The structure
> +        #     may have padding at the end; the base offset of the member
> +        #     following the sub-structure is rounded up to the next multiple
> +        #     of the base alignment of the structure.
> +
> +        a = 16
> +        fields = struct_types[type]
> +        for (field_type, field_name) in fields:
> +            a = max(a, self.base_alignment(field_type, row_major))
> +
> +        return a
> +
> +
> +    def matrix_stride(self, type, row_major):
> +        (c, r) = matrix_dimensions(type)
> +        if not row_major:
> +            # (4) If the member is an array of scalars or vectors, the base
> +            #     alignment and array stride are set to match the base
> +            #     alignment of a single array element, according to rules (1),
> +            #     (2), and (3), and rounded up to the base alignment of a
> +            #     vec4. The array may have padding at the end; the base offset
> +            #     of the member following the array is rounded up to the next
> +            #     multiple of the base alignment.
> +            #
> +            # (5) If the member is a column-major matrix with <C> columns and
> +            #     <R> rows, the matrix is stored identically to an array of
> +            #     <C> column vectors with <R> components each, according to
> +            #     rule (4).
> +
> +            if type[0] == 'd':
> +                return max(16, self.base_alignment("dvec{}".format(r), False))
> +            else:
> +                return max(16, self.base_alignment("vec{}".format(r), False))
> +        else:
> +            # (7) If the member is a row-major matrix with <C> columns and <R>
> +            #     rows, the matrix is stored identically to an array of <R>
> +            #     row vectors with <C> components each, according to rule (4).
> +
> +            if type[0] == 'd':
> +                return max(16, self.base_alignment("dvec{}".format(c), False))
> +            else:
> +                return max(16, self.base_alignment("vec{}".format(c), False))
> +
> +
> +    def array_stride(self, type, row_major):
> +        base_type = array_base_type(type)
> +
> +        if not isstructure(base_type):
> +            # (4) If the member is an array of scalars or vectors, the base
> +            #     alignment and array stride are set to match the base
> +            #     alignment of a single array element, according to rules (1),
> +            #     (2), and (3), and rounded up to the base alignment of a
> +            #     vec4. The array may have padding at the end; the base offset
> +            #     of the member following the array is rounded up to the next
> +            #     multiple of the base alignment.
> +            return max(16,
> +                       max(self.base_alignment(base_type, row_major),
> +                           self.size(base_type, row_major)))
> +        else:
> +            # (9) If the member is a structure, the base alignment of the
> +            #     structure is <N>, where <N> is the largest base alignment
> +            #     value of any of its members, and rounded up to the base
> +            #     alignment of a vec4. The individual members of this
> +            #     sub-structure are then assigned offsets by applying this set
> +            #     of rules recursively, where the base offset of the first
> +            #     member of the sub-structure is equal to the aligned offset
> +            #     of the structure. The structure may have padding at the end;
> +            #     the base offset of the member following the sub-structure is
> +            #     rounded up to the next multiple of the base alignment of the
> +            #     structure.
> +            #
> +            # (10) If the member is an array of <S> structures, the <S> elements
> +            #     of the array are laid out in order, according to rule (9).
> +
> +            return align(self.size(base_type, row_major),
> +                         self.base_alignment(base_type, row_major))         
> +
> +
> +class shared_packing_rules(std140_packing_rules):
> +    def layout_string(self):
> +        return "shared"
> +
> +    def fixed_offsets(self):
> +        return False
> +
> +
> +def iterate_structures(fields, types_seen=[], types_yielded=[]):
> +    """Given a list of fields, yields the structures in the fields in proper
> +       declaration order.  Detects recurrsion in the types and raises an
> +       exception."""
> +
> +    global struct_types
> +
> +    for (type, name) in fields:
> +        if isarray(type):
> +            type = array_base_type(type)
> +
> +        if not isstructure(type):
> +            continue
> +
> +        if type in types_seen:
> +            raise BaseException("Type recurrsion involving {}".format(type))
> +
> +        for t in iterate_structures(struct_types[type],
> +                                    types_seen + [type],
> +                                    types_yielded):
> +            yield t
> +
> +        if type not in types_yielded:
> +            types_yielded.append(type)
> +            yield type
> +
> +
> +class unique_name_dict:
> +    def __init__(self):
> +        self.names = {}
> +
> +    def trim_name(self, type):
> +        if isarray(type):
> +            t = array_base_type(type)
> +        else:
> +            t = type
> +
> +        if ismatrix(t):
> +            # Canonicalize matrix type names.
> +            (c, r) = matrix_dimensions(t)
> +
> +            name = "mat{}x{}".format(c, r)
> +            if t[0] == "d":
> +                name = "d" + name
> +
> +            return name
> +        elif isscalar(t):
> +            return t
> +        elif isvector:
> +            return t.strip("1234")
> +        else:
> +            # Assume it must be a structure.
> +            return t
> +        
> +    def add_type(self, type):
> +        if isarray(type):
> +            t = array_base_type(type)
> +        else:
> +            t = type
> +
> +        if isvector(t):
> +            base = "{}v".format(component_type(t)[0])
> +        elif ismatrix(t):
> +            (c, r) = matrix_dimensions(t)
> +
> +            if t[0] == 'd':
> +                base = "dm{}{}_".format(c, r)
> +            else:
> +                base = "m{}{}_".format(c, r)
> +        elif isscalar(t):
> +            base = t[0]
> +        elif t[0] == "S":
> +            base = "s{}_".format(t[1:])
> +        else:
> +            raise BaseException("Malformed type name {}".format(t))
> +
> +        self.names[self.trim_name(t)] = (base, 1)
> +        return
> +
> +    def get_name(self, type):
> +        t = self.trim_name(type)
> +        if t not in self.names:
> +            self.add_type(type)
> +
> +        (base, count) = self.names[t]
> +        self.names[t] = (base, count + 1)
> +
> +        return "{}{}".format(base, count)
> +
> +def select_basic_type(types, names):
> +    t = random.choice(types)
> +    return (t, names.get_name(t))
> +
> +def generate_struct_of_basic_types(types, names):
> +    return [select_basic_type(types, names)
> +            for i in xrange(0, random.randint(1,12))]
> +
> +def generate_member_from_description(description, builtin_types, names):
> +    global struct_types
> +    global all_types
> +
> +    if len(description) == 0:
> +        return select_basic_type(builtin_types, names)
> +
> +    item = description[0]
> +    if item == "array":
> +        (base_type, name) = generate_member_from_description(
> +            description[1:],
> +            builtin_types,
> +            names)
> +
> +        # If we're making an array of something that can be "big," try to make
> +        # the array a little smaller.
> +
> +        if ismatrix(base_type) or isarray(base_type) or isstructure(base_type):
> +            size = random.choice([2, 3, 5, 7])
> +        else:
> +            size = random.choice([3, 5, 7, 11, 13])
> +
> +        t = "{}[{}]".format(base_type, size)
> +        return (t, name)
> +    elif item == "struct":
> +        fields = generate_struct_of_basic_types(builtin_types, names)
> +        random.shuffle(fields)
> +
> +        # Peek ahead.  If the next item in the description is a built-in type,
> +        # then all of the remaining items must be built-in types.  Generate a
> +        # list of these.
> +
> +        if len(description) > 1 and description[1] in all_types:
> +            required_fields = [generate_member_from_description([i],
> +                                                                builtin_types,
> +                                                                names)
> +                               for i in description[1:]]
> +
> +        else:
> +            required_fields = [generate_member_from_description(
> +                    description[1:],
> +                    builtin_types,
> +                    names)]
> +
> +        # Pick a random spot in the list of "common" fields and insert all of
> +        # the required fields there.
> +
> +        j = random.randint(0, len(fields))
> +        f = fields[:j] + required_fields + fields[j:]
> +
> +        struct_name = "S{}".format(len(struct_types) + 1)
> +        struct_types[struct_name] = f
> +
> +        field_name = names.get_name(struct_name)
> +        return (struct_name, field_name)
> +    elif item in all_types:
> +        return (item, names.get_name(item))
> +    elif item in ["row_major", "column_major", "#column_major"]:
> +        # While "row_major" and "column_major" are valid requirements, they
> +        # are not processed here.  Just skip over them for now.
> +        return generate_member_from_description(description[1:],
> +                                                builtin_types,
> +                                                names)
> +
> +    raise BaseException("Invalid UBO member description {}".format(item))
> +
> +
> +def generate_ubo(description_list, builtin_types):
> +    layouts = dict()
> +    names = unique_name_dict()
> +
> +    fields = []
> +
> +    for desc in description_list:
> +        m = generate_member_from_description(desc, builtin_types, names)
> +        fields.append(m)
> +
> +        if desc[0] in ["row_major", "column_major", "#column_major"]:
> +            layouts[m[1]] = desc[0]
> +
> +    fields.extend(generate_struct_of_basic_types(builtin_types, names))
> +    random.shuffle(fields)
> +
> +    required_layouts = []
> +    for (field_type, field_name) in fields:
> +        if field_name in layouts:
> +            required_layouts.append(layouts[field_name])
> +        else:
> +            required_layouts.append(None)
> +
> +    return (fields, required_layouts)
> +
> +
> +def generate_layouts(fields, required_layouts, allow_row_major_structure):
> +    if required_layouts == None:
> +        required_layouts = [None] * len(fields)
> +
> +    layouts = []
> +    for ((type, name), lay) in zip(fields, required_layouts):
> +        if isarray(type):
> +            type = array_base_type(type)
> +
> +        if lay:
> +            layouts.append(lay)
> +        elif isstructure(type) and not allow_row_major_structure:
> +            # This would work-around a bug in NVIDIA closed source drivers.
> +            # They do not propogate row-major down into structures.
> +
> +            layouts.append("#column_major")
> +        elif ismatrix(type) or isstructure(type):
> +            # Choose a random matrix orientation.  The #column_major are
> +            # ignored when the UBO is emitted, but when a the UBO is
> +            # re-emitted with a default row-major layout, these become
> +            # "column_major".
> +
> +            layouts.append(random.choice(["#column_major",
> +                                          "#column_major",
> +                                          "#column_major",
> +                                          "row_major",
> +                                          "row_major",
> +                                          "column_major"]))
> +        else:
> +            layouts.append("")
> +    return layouts
> +
> +def layout_invert_default(l):
> +    if l == "row_major":
> +        return "#row_major"
> +    elif l == "column_major" or l == "#column_major":
> +        return "column_major"
> +    elif l == "":
> +        return ""
> +    else:
> +        raise BaseException("Invalid layout {}".format(l))
> +
> +def generate_layouts_for_default_row_major(layouts):
> +    """Generate a new list of layouts that should be the same but assumes the
> +       default matrix layout is row-major (instead of column-major)."""
> +    return [layout_invert_default(l) for l in layouts]
> +
> +
> +def fields_to_glsl_struct(type):
> +    global struct_types
> +
> +    # The longest type name will have the form 'dmatCxR[##]' for 11
> +    # characters.  Use this to set the spacing between the field type and the
> +    # field name.
> +
> +    structure_template = Template(dedent("""\
> +    struct ${struct_name} {
> +    % for (field_type, field_name) in fields:
> +        ${"{:<11}".format(field_type)} ${field_name};
> +    % endfor
> +    };
> +    """))
> +
> +    return structure_template.render(struct_name=type, fields=struct_types[type])
> +
> +
> +def iterate_all_struct_fields(type,
> +                              name_from_API_base,
> +                              name_from_shader_base,
> +                              packing,
> +                              offset,
> +                              row_major):
> +    global struct_types
> +
> +    for (field_type, field_name) in struct_types[type]:
> +        name_from_shader = "{}.{}".format(name_from_shader_base, field_name)
> +        name_from_API =    "{}.{}".format(name_from_API_base,    field_name)
> +
> +        if isarray(field_type):
> +            base_type = array_base_type(field_type)
> +
> +            if isstructure(base_type):
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    "",
> +                    offset,
> +                    row_major)
> +
> +                astride = packing.array_stride(field_type, row_major)
> +                array_member_align = packing.base_alignment(
> +                    field_type,
> +                    row_major)
> +
> +                for i in xrange(array_elements(field_type)):
> +
> +                    name_from_API_with_index = "{}[{}]".format(
> +                        name_from_API,
> +                        i)
> +                    name_from_shader_with_index = "{}[{}]".format(
> +                        name_from_shader,
> +                        i)
> +
> +                    o = align(offset, array_member_align) + (astride * i)
> +
> +                    yield block_member(
> +                        name_from_shader_with_index,
> +                        name_from_API_with_index,
> +                        base_type,
> +                        "",
> +                        o,
> +                        row_major)
> +
> +                    for x in iterate_all_struct_fields(base_type,
> +                                                       name_from_API_with_index,
> +                                                       name_from_shader_with_index,
> +                                                       packing,
> +                                                       o,
> +                                                       row_major):
> +                        yield x
> +
> +                        a = packing.base_alignment(x.GLSL_type, row_major)
> +                        o = align(o, a) + packing.size(x.GLSL_type, row_major)
> +
> +            elif ismatrix(base_type):
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    "",
> +                    offset,
> +                    row_major)
> +            else:
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    "",
> +                    offset,
> +                    False)
> +        elif isstructure(field_type):
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                "",
> +                offset,
> +                row_major)
> +
> +            a = packing.base_alignment(field_type, row_major)
> +
> +            for x in iterate_all_struct_fields(field_type,
> +                                               name_from_API,
> +                                               name_from_shader,
> +                                               packing,
> +                                               align(offset, a),
> +                                               row_major):
> +                yield x
> +
> +        elif ismatrix(field_type):
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                "",
> +                offset,
> +                row_major)
> +        else:
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                "",
> +                offset,
> +                False)
> +
> +        a = packing.base_alignment(field_type, row_major)
> +        offset = align(offset, a) + packing.size(field_type, row_major)
> +
> +class block_member:
> +    def __init__(self,
> +                 GLSL_name,
> +                 API_name,
> +                 GLSL_type,
> +                 explicit_layout,
> +                 offset,
> +                 row_major):
> +        self.GLSL_name = GLSL_name
> +        self.GLSL_type = GLSL_type
> +
> +        self.API_name = API_name
> +
> +        self.explicit_layout = explicit_layout
> +        self.offset = offset
> +        self.row_major = row_major
> +
> +        if isarray(GLSL_type):
> +            base_type = array_base_type(GLSL_type)
> +
> +            if isstructure(base_type):
> +                self.API_type = None
> +            else:
> +                self.API_type = type_enum[base_type];
> +
> +            self.size = array_elements(GLSL_type)
> +        elif isstructure(GLSL_type):
> +            self.API_type = None
> +            self.size = 1
> +        else:
> +            self.API_type = type_enum[GLSL_type];
> +            self.size = 1
> +
> +    def struct_nesting(self):
> +        if "." in self.GLSL_name:
> +            # If the block has an instance name, the API name will use the
> +            # block name instead of the instance name.  As a result,
> +            # GLSL_name and API_name will be different.
> +            #
> +            # The first "." is for the block instance name, so it does not count
> +            # as structure nesting.
> +
> +            if self.GLSL_name != self.API_name:
> +                return collections.Counter(self.GLSL_name)["."] - 1 
> +            else:
> +                return collections.Counter(self.GLSL_name)["."]
> +        else:
> +            return 0
> +
> +    def isscalar(self):
> +        return isscalar(self.GLSL_type)
> +
> +    def isvector(self):
> +        return isscalar(self.GLSL_type)
> +
> +    def ismatrix(self):
> +        return isscalar(self.GLSL_type)
> +
> +    def vector_size(self):
> +        return vector_size(self.GLSL_type)
> +
> +    def component_type(self):
> +        return component_type(self.GLSL_type)
> +
> +
> +def iterate_all_block_members(fields,
> +                              field_layouts,
> +                              block_name,
> +                              instance_name,
> +                              packing,
> +                              row_major):
> +
> +    offset = 0
> +
> +    if len(instance_name) > 0:
> +        fmt = "{base}.{field}"
> +    else:
> +        fmt = "{field}"
> +
> +    for ((field_type, field_name), l) in zip(fields, field_layouts):
> +        name_from_shader = fmt.format(base=instance_name, field=field_name)
> +        name_from_API =    fmt.format(base=block_name,    field=field_name)
> +
> +        if l == "row_major":
> +            field_row_major = True
> +        elif l == "column_major":
> +            field_row_major = False
> +        else:
> +            field_row_major = row_major
> +            
> +        if isarray(field_type):
> +            base_type = array_base_type(field_type)
> +
> +            if isstructure(base_type):
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    l,
> +                    offset,
> +                    field_row_major)
> +
> +                astride = packing.array_stride(field_type, field_row_major)
> +                array_member_align = packing.base_alignment(
> +                    field_type,
> +                    field_row_major)
> +
> +                for i in xrange(array_elements(field_type)):
> +                    name_from_API_with_index = "{}[{}]".format(
> +                        name_from_API,
> +                        i)
> +                    name_from_shader_with_index = "{}[{}]".format(
> +                        name_from_shader,
> +                        i)
> +
> +                    o = align(offset, array_member_align) + (astride * i)
> +
> +                    yield block_member(
> +                        name_from_shader_with_index,
> +                        name_from_API_with_index,
> +                        base_type,
> +                        l,
> +                        o,
> +                        field_row_major)
> +
> +                    for x in iterate_all_struct_fields(base_type,
> +                                                       name_from_API_with_index,
> +                                                       name_from_shader_with_index,
> +                                                       packing,
> +                                                       o,
> +                                                       field_row_major):
> +                        yield x
> +
> +                        a = packing.base_alignment(x.GLSL_type, row_major)
> +                        o = align(o, a) + packing.size(x.GLSL_type, row_major)
> +
> +            elif ismatrix(base_type):
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    l,
> +                    offset,
> +                    field_row_major)
> +            else:
> +                yield block_member(
> +                    name_from_shader,
> +                    name_from_API,
> +                    field_type,
> +                    "",
> +                    offset,
> +                    False)
> +        elif isstructure(field_type):
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                l,
> +                offset,
> +                field_row_major)
> +
> +            a = packing.base_alignment(field_type, field_row_major)
> +
> +            for x in iterate_all_struct_fields(field_type,
> +                                               name_from_API,
> +                                               name_from_shader,
> +                                               packing,
> +                                               align(offset, a),
> +                                               field_row_major):
> +                yield x
> +
> +        elif ismatrix(field_type):
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                l,
> +                offset,
> +                field_row_major)
> +        elif isvector(field_type) or isscalar(field_type):
> +            yield block_member(
> +                name_from_shader,
> +                name_from_API,
> +                field_type,
> +                "",
> +                offset,
> +                False)
> +        else:
> +            raise BaseException("Malformed type name {}".format(field_type))
> +
> +        a = packing.base_alignment(field_type, field_row_major)
> +        offset = align(offset, a) + packing.size(field_type, field_row_major)
> +
> +
> +def hash_string(string):
> +    """The djb2 string hash algorithm from the old comp.lang.c days.  Not a
> +       terrific hash, but we just need a pseudorandom number based on the
> +       string.  This will do."""
> +
> +    h = 5381
> +
> +    for c in string:
> +        h = h * 33 + ord(c)
> +
> +    return h & 0x0ffffffff
> +
> +
> +def random_data(type, name, offset):
> +    """Generate pseudorandom data.  The data generated is based on the type,
> +       name of the field, and offset of the member in the UBO."""
> +
> +    if isscalar(type):
> +        h = hash_string("{}@{}".format(offset, name))
> +
> +        if type == "int":
> +            return str(h - 0x7fffffff)
> +        elif type == "uint":
> +            return str(h)
> +        elif type == "bool":
> +            return str(int((h & 8) == 0))
> +        elif type == "float" or type == "double":
> +            return str(float(h - 0x7fffffff) / 65535.0)
> +        else:
> +            raise BaseException("Unknown scalar type {}".format(type))
> +
> +    if isvector(type):
> +        scalar = component_type(type)
> +
> +        x = [random_data(scalar, name, offset + (i * 3))
> +             for i in xrange(vector_size(type))]
> +        return " ".join(x)            
> +
> +    if ismatrix(type):
> +        (r, c) = matrix_dimensions(type)
> +
> +        x = [random_data("float", name, offset + (i * 7))
> +             for i in xrange(r * c)]
> +        return " ".join(x)            
> +
> +    return None
> +
> +
> +def generate_test_vectors(fields,
> +                          field_layouts,
> +                          block_name,
> +                          instance_name,
> +                          packing,
> +                          row_major):
> +    test_vectors = []
> +
> +    for m in iterate_all_block_members(fields,
> +                                       field_layouts,
> +                                       block_name,
> +                                       instance_name,
> +                                       packing,
> +                                       row_major):
> +        a = packing.base_alignment(m.GLSL_type, m.row_major)
> +
> +        if isarray(m.GLSL_type):
> +            base_type = array_base_type(m.GLSL_type)
> +            astride = packing.array_stride(m.GLSL_type, m.row_major)
> +            name = m.API_name + "[0]"
> +        else:
> +            base_type = m.GLSL_type
> +            astride = 0
> +            name = m.API_name
> +
> +        if ismatrix(base_type):
> +            test_vectors.append((
> +                    name,
> +                    m.API_type, 
> +                    m.size,
> +                    align(m.offset, a),
> +                    astride,
> +                    packing.matrix_stride(base_type, m.row_major),
> +                    int(m.row_major)))
> +        elif isvector(base_type) or isscalar(base_type):
> +            test_vectors.append((
> +                    name,
> +                    m.API_type, 
> +                    m.size,
> +                    align(m.offset, a),
> +                    astride,
> +                    0,
> +                    0))
> + 
> +    return test_vectors
> +
> +
> +def scalar_derp(type, name, offset, data):
> +    if type == "bool":
> +        if int(data) == 0:
> +            return name
> +        else:
> +            return "!" + name
> +    elif type == "uint":
> +        return "{} != {}u".format(name, data)
> +    elif type == "int":
> +        return "{} != {}".format(name, data)
> +    elif type == "float":
> +        bits = fudge_data_for_setter(data, "float")
> +        return "!float_match({}, {}, {}u)".format(name, data, bits)
> +    elif type == "double":
> +        bits = fudge_data_for_setter(data, "double")
> +
> +        # 0xHHHHHHHHLLLLLLLL
> +        # 012345678901234567
> +
> +        hi = "0x" + bits[2:9]
> +        lo = "0x" + bits[10:17]
> +
> +        return "!double_match({}, uvec2({}, {}))".format(name, lo, hi)
> +    else:
> +        raise BaseException("Unknown scalar type {}".format(type))
> +
> +
> +def vector_derp(type, name, offset, data):
> +    scalar = component_type(type)
> +    components = [ "x", "y", "z", "w" ]
> +
> +    return [scalar_derp(scalar,
> +                 "{}.{}".format(name, components[i]),
> +                 offset,
> +                 data[i])
> +            for i in xrange(vector_size(type))]
> +
> +
> +def matrix_derp(type, name, offset, data):
> +    (c, r) = matrix_dimensions(type)
> +
> +    if type[0] == 'd':
> +        column_type = "dvec{}".format(r)
> +    else:
> +        column_type = "vec{}".format(r)
> +
> +    data_pairs = []
> +
> +    for i in xrange(c):
> +        data_pairs.extend(vector_derp(
> +                column_type,
> +                "{}[{}]".format(name, i),
> +                offset,
> +                data[(i * r):(i * r) + r]))
> +
> +    return data_pairs
> +
> +
> +def fudge_type_for_setter(type):
> +    if type[0] == 'b':
> +        if type == "bool":
> +            return "int"
> +        else:
> +            return "i" + type[1:]
> +    else:
> +        return type
> +
> +
> +def fudge_data_for_setter(raw_data, type):
> +    if type in ["float", "vec2",   "vec3",   "vec4",
> +                "mat2",  "mat2x2", "mat2x3", "mat2x4",
> +                "mat3",  "mat3x2", "mat3x3", "mat3x4",
> +                "mat4",  "mat4x2", "mat4x3", "mat4x4"]:
> +        fudged_data = []
> +
> +        for d in raw_data.split(" "):
> +            p = struct.pack('!f', float(d))
> +            u = struct.unpack('!I', p)[0]
> +            fudged_data.append(hex(u))
> +
> +        return " ".join(fudged_data)
> +    elif type in ["double", "dvec2",   "dvec3",   "dvec4",
> +                  "dmat2",  "dmat2x2", "dmat2x3", "dmat2x4",
> +                  "dmat3",  "dmat3x2", "dmat3x3", "dmat3x4",
> +                  "dmat4",  "dmat4x2", "dmat4x3", "dmat4x4"]:
> +        fudged_data = []
> +
> +        for d in raw_data.split(" "):
> +            p = struct.pack('!d', float(d))
> +            u = struct.unpack('!Q', p)[0]
> +            
> +            # Sometimes the hex() generates a spurious "L" at the end of the
> +            # string.  Only take the first 18 characters to omit the unwanted
> +            # "L".  I believe this occurs when bit 63 is set.
> +
> +            fudged_data.append(hex(u)[0:18])
> +
> +        return " ".join(fudged_data)
> +    else:
> +        return raw_data
> +
> +
> +def generate_data_pairs(uniform_blocks, packing):
> +    checkers = []
> +    setters = []
> +
> +    for (block_name,
> +         instance_name,
> +         global_layout,
> +         block_layout,
> +         fields,
> +         field_layouts) in uniform_blocks:
> +        for m in iterate_all_block_members(
> +            fields,
> +            field_layouts,
> +            block_name,
> +            instance_name,
> +            packing,
> +            block_row_major_default(global_layout, block_layout)):
> +
> +            if m.API_type:
> +                if isarray(m.GLSL_type):
> +                    base_type = array_base_type(m.GLSL_type)
> +
> +                    astride = packing.array_stride(m.GLSL_type, m.row_major)
> +
> +                    for i in xrange(array_elements(m.GLSL_type)):
> +
> +                        name = "{}[{}]".format(m.GLSL_name, i)
> +                        offset = m.offset + (i * astride)
> +
> +                        raw_data = random_data(base_type, m.GLSL_name, offset)
> +                        setters.append(
> +                            (fudge_type_for_setter(base_type),
> +                             "{}[{}]".format(m.API_name, i),
> +                             fudge_data_for_setter(raw_data, base_type)))
> +
> +                        data = raw_data.split(" ")
> +
> +                        if isscalar(base_type):
> +                            checkers.append(scalar_derp(base_type,
> +                                                        name,
> +                                                        offset,
> +                                                        data[0]))
> +                        elif isvector(base_type):
> +                            checkers.extend(vector_derp(base_type,
> +                                                        name,
> +                                                        offset,
> +                                                        data))
> +                        elif ismatrix(base_type):
> +                            checkers.extend(matrix_derp(base_type,
> +                                                        name,
> +                                                        offset,
> +                                                        data))
> +                else:
> +                    raw_data = random_data(m.GLSL_type, m.GLSL_name, m.offset)
> +                    setters.append((fudge_type_for_setter(m.GLSL_type),
> +                                    m.API_name,
> +                                    fudge_data_for_setter(raw_data,
> +                                                          m.GLSL_type)))
> +
> +                    data = raw_data.split(" ")
> +
> +                    if isscalar(m.GLSL_type):
> +                        checkers.append(scalar_derp(m.GLSL_type,
> +                                                    m.GLSL_name,
> +                                                    m.offset,
> +                                                    data[0]))
> +                    elif isvector(m.GLSL_type):
> +                        checkers.extend(vector_derp(m.GLSL_type,
> +                                                    m.GLSL_name,
> +                                                    m.offset,
> +                                                    data))
> +                    elif ismatrix(m.GLSL_type):
> +                        checkers.extend(matrix_derp(m.GLSL_type,
> +                                                    m.GLSL_name,
> +                                                    m.offset,
> +                                                    data))
> +
> +    return (checkers, setters)
> +
> +
> +def pretty_format_type_data(packing, type, offset, row_major):
> +    a = packing.base_alignment(type, row_major)
> +    aligned_offset = align(offset, a)
> +    size = packing.size(type, row_major)
> +
> +    row_major_str = "-"
> +    mstride = "-"
> +    astride = "-"
> +
> +    if isarray(type):
> +        astride = packing.array_stride(type, row_major)
> +
> +        base_type = array_base_type(type)
> +        if ismatrix(base_type) and row_major:
> +            if row_major:
> +                row_major_str = "yes"
> +            else:
> +                row_major_str = "no"
> +
> +            mstride = packing.matrix_stride(base_type, row_major)
> +    else:
> +        if ismatrix(type):
> +            if row_major:
> +                row_major_str = "yes"
> +            else:
> +                row_major_str = "no"
> +
> +            mstride = packing.matrix_stride(type, row_major)
> +
> +    return "{base_align:>3}  {base_offset:>4}  {aligned_offset:>5}  {padded_size:>6}  {row_major:^5}  {array_stride:>6}  {matrix_stride:>6}".format(
> +        base_align=a,
> +        base_offset=offset,
> +        aligned_offset=aligned_offset,
> +        padded_size=size,
> +        row_major=row_major_str,
> +        array_stride=astride,
> +        matrix_stride=mstride
> +        )
> +
> +
> +def pretty_format_member(m, packing):
> +    # If the name ends in an array subscript, emit a special line to note that
> +    # the following fields are the contents of an element of an array of
> +    # structures.
> +
> +    if m.GLSL_name[-1] == "]":
> +        n = m.struct_nesting() + 1
> +        indent = "//  " + ("  " * n)
> +
> +        return "{indent}[{index}".format(indent=indent,
> +                                         index=m.GLSL_name.split("[")[-1])
> +
> +    # Strip off everything before the last period.
> +    name = m.GLSL_name.split(".")[-1]
> +
> +    n = m.struct_nesting()
> +    if n > 0:
> +        indent = "//  " + ("  " * n)
> +        field_str = "{indent}{type:<11} {name:<20}".format(
> +            indent=indent,
> +            type=m.GLSL_type,
> +            name=name)[0:31]
> +    else:
> +        field_str = "    {type:<11}{name};{padding}//   ".format(
> +            type=m.GLSL_type,
> +            name=name,
> +            padding="          "[len(name):])
> +
> +    data_str = pretty_format_type_data(
> +        packing,
> +        m.GLSL_type,
> +        m.offset,
> +        m.row_major)
> +
> +    # If there is an explicit layout for the member, prepend it to the member
> +    # declaration.  This also means that the member must be contained directly
> +    # in the UBO (i.e., not nested in a struct), so no additional indentation
> +    # is necessary.
> +
> +    if m.explicit_layout and "#" not in m.explicit_layout:
> +        return "    layout({layout})\n{field}{data}".format(
> +            layout=m.explicit_layout,
> +            field=field_str,
> +            data=data_str)
> +    else:
> +        return "{field}{data}".format(field=field_str, data=data_str)
> +
> +
> +def block_row_major_default(global_layout, block_layout):
> +    row_major = False
> +
> +    if global_layout and "row_major" in global_layout:
> +        row_major = True
> +            
> +    if  block_layout:
> +        if "row_major" in block_layout:
> +            row_major = True
> +        elif "column_major" in block_layout:
> +            # The block layout can override a previous global layout.
> +            row_major = False
> +
> +    return row_major
> +
> +
> +def generate_block_list(glsl_version, packing, ubo_fields, layouts):
> +    blocks = [("UB1", "", None, packing.layout_string(), ubo_fields, layouts)]
> +
> +    # If the GLSL version is at least 1.50, UBO functionality is significantly
> +    # extended.
> +    #
> +    # 1. Uniform blocks can have instance names.  The existence of the name
> +    #    changes the way the block is queried through the API (with the block
> +    #    name) and the way it is accessed by the shader (with the instance
> +    #    name).
> +    #
> +    # 2. Uniform blocks can be grouped in arrays.  UBO arrays must have an
> +    #    instance name.
> +    #
> +    # This is used to make the tests dramatically more complex.  Each UBO is
> +    # emitted three times.
> +    #
> +    # 1. Without an instance name.
> +    #
> +    # 2. With an instance name and the per-block matrix layout switched to
> +    #    row_major.  The declared layout of the individual fields is modified
> +    #    so that this block has the same layout as the previous block.
> +    #
> +    # 3. With an instance name and an array size.  The per-block matrix layout
> +    #    is empty, but the global matrix layout is changed to row_major.  This
> +    #    block should have the same layout as the previous two.
> +
> +    if glsl_version >= 150:
> +        inverted_layouts = [layout_invert_default(l) for l in layouts]
> +
> +        blocks.append(("UB2",
> +                       "ub2",
> +                       None,
> +                       packing.layout_string() + ", row_major",
> +                       ubo_fields,
> +                       inverted_layouts))
> +
> +        blocks.append(("UB3",
> +                       "ub3",
> +        # Disabled to work around Mesa bug #83508.
> +        #               "ub3[2]",
> +                       packing.layout_string() + ", row_major",
> +                       None,
> +                       ubo_fields,
> +                       inverted_layouts))
> +
> +    return blocks
> +
> +
> +def emit_shader_test(blocks, packing, glsl_version, extensions):
> +
> +    structures = []
> +    test_vectors = []
> +
> +    for (block_name,
> +         instance_name,
> +         global_layout,
> +         block_layout,
> +         fields,
> +         field_layouts) in blocks:
> +
> +        structures.extend([s for s in iterate_structures(fields)])
> +        
> +        test_vectors.extend(generate_test_vectors(
> +                fields,
> +                field_layouts,
> +                block_name,
> +                instance_name,
> +                packing,
> +                block_row_major_default(global_layout, block_layout)))
> +
> +
> +    (checkers, setters) = generate_data_pairs(blocks, packing)
> +
> +    # If the GLSL version is at least 1.40, UBOs are already supported, and we
> +    # don't need to enable the extension.
> +
> +    if glsl_version >= 140 and "GL_ARB_uniform_buffer_object" in extensions:
> +        extensions.remove("GL_ARB_uniform_buffer_object")
> +
> +    t = Template(dedent("""\
> +    [require]
> +    GLSL >= ${glsl_version / 100}.${glsl_version % 100}
> +    % for ext in extensions:
> +    ${ext}
> +    % endfor
> +
> +    # Do NOT edit the following lines.
> +    # GLSL ${glsl_version}
> +    # EXTENSIONS ${extensions}
> +    # PACKING ${packing.layout_string()}
> +    % for s in structures:
> +    # STRUCT ("${s}", ${struct_types[s]})
> +    % endfor
> +    % for b in uniform_blocks:
> +    # UBO ${b}
> +    % endfor
> +    # DATA END
> +
> +    [vertex shader]
> +    % for ext in extensions:
> +    #extension ${ext}: require
> +    % endfor
> +    #extension GL_ARB_shader_bit_encoding: enable
> +    #extension GL_ARB_gpu_shader5: enable
> +
> +    precision highp float;
> +    % for s in structures:
> +
> +    struct ${s} {
> +        % for (field_type, field_name) in struct_types[s]:
> +        ${"{:<11}".format(field_type)} ${field_name};
> +        % endfor
> +    };
> +    % endfor
> +
> +    % for (block_name, instance_name, global_layout, block_layout, fields, field_layouts) in uniform_blocks:
> +    % if global_layout:
> +    layout(${global_layout}) uniform;
> +
> +    % endif 
> +    % if block_layout:
> +    layout(${block_layout})
> +    % endif
> +    uniform ${block_name} {
> +                              // base   base  align  padded  row-   array   matrix
> +                              // align  off.  off.   size    major  stride  stride
> +    % for m in iterate_all_block_members(fields, field_layouts, block_name, instance_name, packing, block_row_major_default(global_layout, block_layout)):
> +    ${pretty_format_member(m, packing)}
> +    % endfor
> +    } ${instance_name};
> +    % endfor
> +
> +    flat out int vertex_pass;
> +    in vec4 piglit_vertex;
> +
> +    #if defined(GL_ARB_shader_bit_encoding) || defined(GL_ARB_gpu_shader5) || __VERSION__ >= 430
> +    bool float_match(float u, float f, uint bits) { return floatBitsToUint(u) == bits; }
> +    #else
> +    bool float_match(float u, float f, uint bits) { return u == f; }
> +    #endif
> +    % if glsl_version >= 400 or "GL_ARB_gpu_shader_fp64" in extensions:
> +
> +    bool double_match(double u, uvec2 bits) { return unpackDouble2x32(u) == bits; }
> +    %endif
> +
> +    void main()
> +    {
> +        /* std140 (or shared) layout prevents any fields or blocks from being
> +         * eliminated.  Section 2.11.6 of the OpenGL ES 3.0 spec makes this
> +         * explicit, but desktop GL specs only imply it.
> +         */
> +        bool pass = true;
> +
> +    % for i in xrange(len(checkers)):
> +        % if i % 5 == 0:
> +        if (${checkers[i]})
> +            pass = false;
> +        % endif
> +    % endfor
> +
> +        vertex_pass = int(pass);
> +        gl_Position = piglit_vertex;
> +    }
> +
> +    [fragment shader]
> +    precision highp float;
> +
> +    out vec4 piglit_fragcolor;
> +    flat in int vertex_pass;
> +
> +    void main()
> +    {
> +        piglit_fragcolor = bool(vertex_pass) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
> +    }
> +
> +    [test]
> +    link success
> +    % for (name, type, size, offset, astride, mstride, row_major) in test_vectors:
> +
> +    active uniform ${name} GL_UNIFORM_TYPE ${type}
> +    active uniform ${name} GL_UNIFORM_SIZE ${size}
> +    active uniform ${name} GL_UNIFORM_OFFSET ${offset}
> +    active uniform ${name} GL_UNIFORM_ARRAY_STRIDE ${astride}
> +    active uniform ${name} GL_UNIFORM_MATRIX_STRIDE ${mstride}
> +    active uniform ${name} GL_UNIFORM_IS_ROW_MAJOR ${row_major}
> +    % endfor
> +
> +    % for (type, name, data) in setters:
> +    uniform ${type} ${name} ${data}
> +    % endfor
> +
> +    draw rect -1 -1 2 2
> +    probe all rgba 0.0 1.0 0.0 1.0"""))
> +
> +    return t.render(glsl_version=glsl_version,
> +                    extensions=extensions,
> +                    structures=structures,
> +                    test_vectors=test_vectors,
> +                    uniform_blocks=blocks,
> +                    packing=packing,
> +                    iterate_all_block_members=iterate_all_block_members,
> +                    pretty_format_member=pretty_format_member,
> +                    block_row_major_default=block_row_major_default,
> +                    struct_types=struct_types,
> +                    checkers=checkers,
> +                    setters=setters)
> +
> +
> +def generate_file_name(requirements, packing):
> +    prefix = packing.layout_string() + "-"
> +    suffix = ".shader_test"
> +
> +    body = "-and-".join(["-".join(req) for req in requirements])
> +
> +    return prefix + body + suffix
> +
> +
> +if __name__ == "__main__":    
> +    if len(sys.argv) > 1:
> +        max_glsl_version = int(sys.argv[1])
> +    else:
> +        max_glsl_version = 130
> +
> +    if len(sys.argv) > 2:
> +        extensions = sys.argv[2:]
> +    else:
> +        extensions = []
> +
> +    available_versions = [v for v in [130, 140, 150, 400, 430]
> +                          if v <= max_glsl_version]
> +
> +    # Pick a random GLSL version from the available set of possible versions.
> +    glsl_version = random.choice(available_versions)
> +
> +    # Use the GLSL version filter out some extensions that are redundant.
> +    if glsl_version >= 140 and "GL_ARB_uniform_buffer_object" in extensions:

Should these be <= not >= ?

> +        extensions.remove("GL_ARB_uniform_buffer_object")
> +
> +    if glsl_version >= 400 and "GL_ARB_gpu_shader_fp64" in extensions:
> +        extensions.remove("GL_ARB_gpu_shader_fp64")
> +
> +    if glsl_version >= 430 and "GL_ARB_arrays_of_arrays" in extensions:
> +        extensions.remove("GL_ARB_arrays_of_arrays")
> +
> +    # Pick a random subset of the remaining extensions.
> +    num_ext = len(extensions)
> +    if num_ext > 0:
> +        random.shuffle(extensions)
> +        r = random.randint(0, num_ext)
> +        extensions = extensions[:r]
> +
> +    # Based on the GLSL version and the set of extensions, pick the set of
> +    # possible data types.
> +    if glsl_version < 400:
> +        types = all130_types
> +    else:
> +        types = all400_types
> +
> +    if "GL_ARB_gpu_shader_fp64" in extensions:
> +        types.extend(double_types)
> +
> +    # Based on the GLSL version, pick a set of packing rules
> +    # FINISHME: Add support for std430_packing_rules() soon.
> +    packing = random.choice([std140_packing_rules(), shared_packing_rules()])
> +
> +    # Based on the GLSL version and the set of available extensions, pick
> +    # some required combinations of data structures to include in the UBO.
> +    arrays_of_arrays = (glsl_version >= 430 or
> +                        "GL_ARB_arrays_of_arrays" in extensions)
> +
> +    allow_row_major_structure = glsl_version >= 150
> +
> +    requirements = []
> +    for i in [1, 2]:
> +        x = [random.choice(["array", "struct"])]
> +
> +        for j in [1, 2, 3]:
> +            # If arrays-of-arrays are not supported, don't allow "array" to be
> +            # picked twice in a row.
> +
> +            if x[-1] == "array" and not arrays_of_arrays:
> +                x.append("struct")
> +            else:
> +                x.append(random.choice(["array", "struct"]))
> +
> +        if "struct" in x and allow_row_major_structure:
> +            ordering = random.choice([None,
> +                                      None,
> +                                      None,
> +                                      None,
> +                                      "column_major",
> +                                      "#column_major",
> +                                      "row_major",
> +                                      "row_major"])
> +            if ordering:
> +                x = [ordering] + x
> +
> +        requirements.append(x)
> +
> +    if glsl_version < 140:
> +        extensions.append("GL_ARB_uniform_buffer_object")
> +
> +    # Generate the test!
> +    (fields, required_layouts) = generate_ubo(requirements, types)
> +
> +    layouts = generate_layouts(
> +        fields,
> +        required_layouts,
> +        allow_row_major_structure)
> +
> +    blocks = generate_block_list(
> +        glsl_version,
> +        packing,
> +        fields,
> +        layouts)
> +
> +    print emit_shader_test(
> +        blocks,
> +        packing,
> +        glsl_version,
> +        extensions)
> -- 
> 1.8.1.4
> 
> _______________________________________________
> Piglit mailing list
> Piglit at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/piglit
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/piglit/attachments/20151110/68ff7577/attachment-0001.sig>


More information about the Piglit mailing list