On Wed, Sep 24, 2014 at 09:47:38AM -0700, Ian Romanick wrote: > From: Ian Romanick <[email protected]> > > 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 <[email protected]> > --- > 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
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/piglit
signature.asc
Description: PGP signature
_______________________________________________ Piglit mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/piglit
