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

Ian Romanick idr at freedesktop.org
Wed Sep 24 16:11:42 PDT 2014


On 09/24/2014 01:51 PM, Dylan Baker wrote:
> On Wednesday, September 24, 2014 12:10:17 PM Ian Romanick wrote:
>> On 09/24/2014 11:04 AM, Dylan Baker wrote:
>>> Hi Ian,
>>>
>>> I have quite a few comments for you, a good deal of them are style
>>> comments. We use PEP8 style in piglit for python code, so there's that.
>>> I also have some portability comments for you.
>>>
>>> I probably have more comments, but I my ADD is flaring up. I'm sure most
>>> of the style comments apply to more places. The biggest problem is this
>>> is 1700 lines of largely uncommented code, It would take me a lot of
>>> time to figure out what all of it does, could you add some comments?
>>>
>>> Dylan
>>>
>>> On Wednesday, September 24, 2014 09:47:38 AM 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
>>>
>>> Imports are grouped by: built-ins, 3rd party modules, and finally local
>>> modules. You should move mako imports below textwrap
>>
>> ok
>>
>>>> +
>>>> +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"
>>>> +]
>>>
>>> Do not pad lists like this. Generally in python you either write a list
>>> on a single line, or one element per line. It is legal to have multiple
>>> elements per line, but PEP8 specifies exactly one space between elements
>>> in containers. We usually do one element per line.
>>
>> I'm not going to do that.  It makes it harder to read, and it makes it
>> take more lines.  Grouping and formatting them by type-of-thing is correct.
>>
>>> On a second note, any variables at the module scope are expected to be
>>> constants, if they are not constants they should be wrapped in a
>>> function. The pattern usually used is to wrap everything in a main()
>>> function and then use the if __name__ == '__main__': pattern. Have a
>>> look at piglit-print-commands.py for a simple example of this pattern.
>>> If they are constants they need to be all caps (ie: ALL130_TYPES)
>>
>> ok
>>
>>>> +
>>>> +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",
>>>> +}
>>>
>>> Dictionaries also do not get multiple spaces, no spaces before the ':',
>>> exactly one after the ':'
>>
>> I'm also not going to do that.  It is more important for the names in
>> groups to line up.
>>
>>>> +
>>>> +def align(offset, alignment):
>>>> +    return ((offset + alignment - 1) / alignment) * alignment
>>>
>>> I would suggest importing division from __future__, which adds // and /
>>> as floating and integer division respectively. This is the default in
>>> python3, and makes the code much easier to understand.
> 
> is math.ceil() what you're looking for?

No... I think some examples will make it more clear:

align(31, 16) => 32
align(32, 16) => 32
align(33, 16) => 48

>> Hmm... so there may be a just plain better way to do this function.  I
>> want to return either 'offset' or 'offset' rounded up to the next
>> multiple of 'alignment'.
>>
>>> toplevel classes and functions get exactly two spaces between them
> 
> If a class or function is defined at the module level (the class or def
> keyword is not indented) then there should be two newlines before and
> after. Example:
> 
> MY_CONST = 1
> 
> 
> def some_func():
> 	pass
> 
> 
> class SomeClass(object):
> 	pass
> 
> 
>>
>> I don't know what you're suggesting here.
>>
>>>> +
>>>> +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:
>>>
>>> Classes should *always* inherit from object if they have no other parent
>>> class, otherwise you get old-style classes with are not python3
>>> compatible, and have other oddities
>>
>> ok
>>
>>>> +    __metaclass__ = abc.ABCMeta
>>>> +
>>>> +    @abc.abstractproperty
>>>> +    def layout_string(self):
>>>> +        """Get the string used in a layout qualifier to select this set of
>>>> +           layout rules."""
>>>
>>> Don't indent docstrings on subsequent lines
>>> Also, if you wrap more than one line the final """ goes on a new line
>>> with a blank line above it
>>>
>>> ie:
>>>
>>> """ Return some data
>>>
>>> I am a more detailed description
>>>
>>> """
>>
>> That is awful.  Text that doesn't line up is unreadable by humans.
> 
> That is python style. Ideally the first line should not exceed 79
> characters and shouldn't wrap, although that is at your discretion. The
> problem with indenting is that when you use pydoc to read the docstrings
> it removes the leading """ and dedents it. so your string would end up
> like this:
> 
> Get the string used in a layout qualifier to select this set of
>     layout rules.  

PEP257 seems to indicate the algorithm is smarter and more complicated
than that... but also probably not worth sqawbling over. :)

>>>> +        return NotImplemented
>>>
>>> Do not return from an abc.abstract* decorated property. If these
>>> properties as not overwritten the inheriting class will raise a runtime
>>> exception instead of initializing, so all this adds is unreachable code.
>>
>> Is this documented somewhere?  All of the examples I found did this.  If
>> there are better examples, I'd like to know... so that I can use them
>> next time. :)
> 
> docs.python.org is really good. They don't alway have great examples,
> but they have fairly straight forward explanations of what the built-ins
> do.
> 
>>
>>>> +
>>>> +    @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)?"""
>>>
>>> this look more like a XXX: style comment than a docstring
>>
>> It's specifying the question that the method answers.
> 
> okay.

But I went ahead and made it more clear anyway.

>>>> +        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)
>>>
>>> You don't need to use the parens here.
>>
>> So,
>>
>>             c, r = matrix_dimensions(type)
>>
>> does the right thing?  Interesting.
> 
> Yes, python handles explosions properly
> 
>>
>>>> +            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))   
>>>
>>> Don't ever raise a BaseException. Preferably you would subclass here so
>>> you can catch it later if you want, or at the very least raise an
>>> Exception.
>>
>> I'm not expecting that the exceptions will ever be handled.  I just
>> wanted to get some semi-useful debug messages when something went wrong.
>>  The Python documentation for exceptions isn't very good for these
>> cases, so it was really difficult for me to know what to do.
>>
>> So, is
>>
>>             raise Exception("Unknown type {}".format(type))
>>
>> better?
> 
> Yes. The problem with BaseException is that it is also the base class
> for exceptions like KeyboardInterupt. So if down the road someone did
> decide to handle them, they would catch literally every exception.
> Exception is the base class for "normal" exceptions

Okay... that makes sense.

>>>> +
>>>> +        s = 0
>>>> +        fields = struct_types[type]
>>>> +        for (t, n) in fields:
>>>
>>> It's really better to explicitly use either dict.items() or
>>> dict.iteritems() than to rely on the __iter__ method. On top of that you
>>> should use iteritems (and iterkeys, and itervalues) unless you are
>>> modifying the container in place, it saves memory
>>
>> fields isn't a dict.  It's just a list of tuples.
> 
> never mind then.
> 
>>
>>>> +            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"]
>>>
>>> Generally functions are grouped together above classes in the file
>>
>> ok
>>
>>>> +
>>>> +
>>>> +def isvector(type):
>>>> +    return type in [ "vec2",  "vec3",  "vec4",
>>>> +                     "ivec2", "ivec3", "ivec4",
>>>> +                     "uvec2", "uvec3", "uvec4",
>>>> +                     "bvec2", "bvec3", "bvec4",
>>>> +                     "dvec2", "dvec3", "dvec4" ]
>>>
>>> No spaces around '[' and ']' in lists. Also, one space between each
>>> element
>>
>> I can delete the spaces around '[' and ']'.
> 
> I'll settle for that
> 
>>
>>>> +
>>>> +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" ]
>>>
>>> same here
>>>
>>>> +
>>>> +
>>>> +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))
>>>
>>> dedent this by one level
>>
>> This is where the auto-indenter in emacs put it.  I think becuase it's
>> inside two ( ?
> 
> This is one of those weird python things (Vim's auto indenter does the
> same thing). I think I can explain this best with examples
> 
> Correct:
> my_function(here,
>      are,
> 	 some,
> 	 arguments)
> 
> 
> Correct:
> def my_function(here,
>          are,
> 		 some,
> 		 arguments):
> 	""" I am a function """
> 	print(I do stuff)
> 
> Incorrect:
> my_function(here,
>         are,
> 		some,
> 		arguments)
> 
> Hopefully you can understand from this when double-indent vs single
> indent is used. Auto-indenters just aren't very good at guessing what
> you're thinking.
> 
>>
>>>> +        elif ismatrix(type):
>>>> +            return self.matrix_stride(type, row_major)
>>>> +
>>>> +        global struct_types
>>>
>>> yuck, do not use global, ever. If you're using global you're doing it
>>> wrong. You don't need this anyway because struct_types is in a higher
>>> scope and is visible and mutable in this scope.
>>
>> That's the behavior that I would have expected.  I tried that, and it
>> did not work.  It complained that struct_types was used before being
>> set.  I'll try it again...
> 
> Interesting. It being in the top scope I would have expected it to work.
> I'll pull the patch down and see what's wrong

I figured it out.  In a (much!) earlier version struct_types didn't
exist until down in the "main" code.  At some point I moved it above,
and I could have removed all the global business then.  Order matters.

>>>> +        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:
>>>
>>> again, fields.itertiems()
>>>
>>>> +            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
>>>
>>> get rid of this
>>>
>>>> +
>>>> +    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)
>>>
>>> This is a strange class. It looks to me like what you really want do do
>>> is subclass dict() and overide the __getitem__ and __setitem__ magic
>>> methods.
>>
>> Perhaps... I'll look into that.
>>
>>>> +
>>>> +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
>>>
>>> No using global
>>>
>>>> +
>>>> +    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)
>>>
>>> No need for the parens here
>>>
>>>> +
>>>> +
>>>> +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 these are long you might consider using izip from the itertools
>>> module. Same principle as the iteritems() vs items() thing
>>
>> These lists shouldn't be more than a dozen or so items each.
> 
> At that point it's your call. izip is still probably slightly faster,
> but there's also an extra import
> 
>>
>>>> +        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
>>>
>>> no
>>>
>>>> +
>>>> +    # 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
>>>
>>> no
>>>
>>>> +
>>>> +    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)
>>>
>>> don't add extra spaces
>>>
>>>> +
>>>> +        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."""
>>>
>>> or you could just call hash() on the string object...
>>
>> I thought about that, but this is one part where I want the result to be
>> predictable.  See
>> http://stackoverflow.com/questions/793761/built-in-python-hash-function
> 
> okay.
> 
>>
>>>> +
>>>> +    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
>>>
>>> You don't need to return None from a python function. They return None
>>> unless an explicit yield or return statement is made
>>
>> ok
>>
>>>> +
>>>> +
>>>> +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:
>>>> +        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: 198 bytes
Desc: OpenPGP digital signature
URL: <http://lists.freedesktop.org/archives/piglit/attachments/20140924/c39441e2/attachment-0001.sig>


More information about the Piglit mailing list