4 # Copyright (c) 2014 Intel Corporation
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
8 # in the Software without restriction, including without limitation the rights
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 # copies of the Software, and to permit persons to whom the Software is
11 # furnished to do so, subject to the following conditions:
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 from __future__
import print_function
30 from textwrap
import dedent
31 from mako
.template
import Template
33 # Module-local global variable that tracks all of the structure types that
34 # have been generated since ever.
38 "float", "vec2", "vec3", "vec4",
39 "int", "ivec2", "ivec3", "ivec4",
40 "uint", "uvec2", "uvec3", "uvec4",
41 "bool", "bvec2", "bvec3", "bvec4",
43 "mat2", "mat2x3", "mat2x4",
44 "mat3x2", "mat3", "mat3x4",
45 "mat4x2", "mat4x3", "mat4"
49 "double", "dvec2", "dvec3", "dvec4",
51 "dmat2", "dmat2x3", "dmat2x4",
52 "dmat3x2", "dmat3", "dmat3x4",
53 "dmat4x2", "dmat4x3", "dmat4"
56 ALL400_TYPES
= ALL130_TYPES
+ DOUBLE_TYPES
58 # All known types, including the redundant NxN matrix types.
59 ALL_TYPES
= [ "mat2x2", "mat3x3", "mat4x4",
60 "dmat2x2", "dmat3x3", "dmat4x4"] + ALL400_TYPES
62 def align(offset
, alignment
):
63 return ((offset
+ alignment
- 1) / alignment
) * alignment
67 """Return true if the type is a known scalar type from any GLSL version."""
68 return type in ["float", "bool", "int", "uint", "double"]
72 """Return true if the type is a known vector type from any GLSL version."""
73 return type in ["vec2", "vec3", "vec4",
74 "ivec2", "ivec3", "ivec4",
75 "uvec2", "uvec3", "uvec4",
76 "bvec2", "bvec3", "bvec4",
77 "dvec2", "dvec3", "dvec4"]
81 """Return true if the type is a known matrix type from any GLSL version."""
82 return type in ["mat2", "mat3", "mat4",
83 "mat2x2", "mat2x3", "mat2x4",
84 "mat3x2", "mat3x3", "mat3x4",
85 "mat4x2", "mat4x3", "mat4x4",
86 "dmat2", "dmat3", "dmat4",
87 "dmat2x2", "dmat2x3", "dmat2x4",
88 "dmat3x2", "dmat3x3", "dmat3x4",
89 "dmat4x2", "dmat4x3", "dmat4x4"]
93 """Return true if the type name appears to be an array type."""
97 def isstructure(type):
98 """Return true if the type name appears to be a structure type.
100 This function works by process of elimination. If the named type is not a
101 scalar, vector, matrix, or array, then it must be a structure.
103 NOTE: If the type is an array-of-structure type, this function will return
106 return not (isscalar(type) or isvector(type) or ismatrix(type) or
110 def vector_size(type):
111 """Return the number of elements in a vector type."""
113 return int(type[-1:])
115 raise Exception("Non-vector type {}".format(type))
118 def matrix_dimensions(type):
119 """Return the dimensions of an array type.
121 The tuple returned is (columns, rows).
123 if not ismatrix(type):
124 raise Exception("Non-matrix type {}".format(type))
127 s
= type[-3:].split("x")
128 return int(s
[0]), int(s
[1])
134 def array_elements(type):
135 """Return the number of elements in an array type.
137 For non-array types zero is returned.
142 # Is there a better way to do this?
143 return int(type.split("[")[1].split("]")[0])
145 def basic_machine_units(type):
146 """Return the size in 'basic machine units' of a scalar type."""
147 if type in ["float", "bool", "int", "uint"]:
153 raise Exception("Non-scalar type {}".format(type))
156 def array_base_type(type):
157 """Return the type of an element from an array type."""
158 if not isarray(type):
159 raise Exception("Non-array type {}".format(type))
161 return type[:type.index("[")] + type[type.index("]")+1:]
163 def without_array(type):
164 """Return the type of an element with all arrays removed."""
165 if not isarray(type):
166 raise Exception("Non-array type {}".format(type))
168 return type.split("[")[0]
170 def arrays_of_arrays_size(type):
171 """Return the total number of elements in an array of arrays."""
172 if not isarray(type):
173 raise Exception("Non-array type {}".format(type))
177 while (isarray(base_type
)):
178 size
= size
* array_elements(base_type
)
179 base_type
= array_base_type(base_type
)
183 def vector_base_type(type):
184 """Return the type of an element from a vector."""
185 if not isvector(type):
186 raise Exception("Non-vector type {}".format(type))
199 raise Exception("Unknown vector type {}".format(type))
202 def iterate_structures(fields
, types_seen
=[], types_yielded
=[]):
203 """Given a list of fields, yields the structures in the fields in proper
204 declaration order. Detects recurrsion in the types and raises an
207 for (type, name
) in fields
:
209 type = without_array(type)
211 if not isstructure(type):
214 if type in types_seen
:
215 raise Exception("Type recurrsion involving {}".format(type))
217 for t
in iterate_structures(struct_types
[type],
222 if type not in types_yielded
:
223 types_yielded
.append(type)
227 def select_basic_type(types
, names
):
228 """Return a random type with a matching field name.
230 From the list of supplied types, picks a random type. Using supplied
231 unique_name_dict, a name is also chosen. The two are returned as a tuple.
233 t
= random
.choice(types
)
234 return t
, names
.get_name(t
)
237 def generate_struct_of_basic_types(types
, names
):
238 """Return a sequence of random types with unique field names."""
239 return [select_basic_type(types
, names
)
240 for i
in xrange(0, random
.randint(1,12))]
243 def generate_member_from_description(description
, builtin_types
, names
):
244 """Generate a single block member for a list of required attributes.
246 The list of required attributes is a sequence of 'struct' and 'array'
247 perhaps terminated with the name of a GLSL basic type. In addition to any
248 required subfields, structures generated in the sequence will be populated
249 with a random selection of available GLSL types named in builtin_types.
251 A matrix orientation can also be listed as the first requirement.
252 However, that is not handled by this function, and it will be ignored.
254 The tuple returned is (member_type, member_name).
256 if len(description
) == 0:
257 return select_basic_type(builtin_types
, names
)
259 item
= description
[0]
261 base_type
, name
= generate_member_from_description(
266 # If we're making an array of something that can be "big," try to make
267 # the array a little smaller.
269 if ismatrix(base_type
) or isarray(base_type
) or isstructure(base_type
):
270 size
= random
.choice([2, 3, 5, 7])
272 size
= random
.choice([3, 5, 7, 11, 13])
274 t
= "{}[{}]".format(base_type
, size
)
276 elif item
== "struct":
277 fields
= generate_struct_of_basic_types(builtin_types
, names
)
278 random
.shuffle(fields
)
280 # Peek ahead. If the next item in the description is a built-in type,
281 # then all of the remaining items must be built-in types. Generate a
284 if len(description
) > 1 and description
[1] in ALL_TYPES
:
285 required_fields
= [generate_member_from_description([i
],
288 for i
in description
[1:]]
291 required_fields
= [generate_member_from_description(
296 # Pick a random spot in the list of "common" fields and insert all of
297 # the required fields there.
299 j
= random
.randint(0, len(fields
))
300 f
= fields
[:j
] + required_fields
+ fields
[j
:]
302 struct_name
= "S{}".format(len(struct_types
) + 1)
303 struct_types
[struct_name
] = f
305 field_name
= names
.get_name(struct_name
)
306 return struct_name
, field_name
307 elif item
in ALL_TYPES
:
308 return item
, names
.get_name(item
)
309 elif item
in ["row_major", "column_major", "#column_major"]:
310 # While "row_major" and "column_major" are valid requirements, they
311 # are not processed here. Just skip over them for now.
312 return generate_member_from_description(description
[1:],
316 raise Exception("Invalid UBO member description {}".format(item
))
319 def generate_ubo(description_list
, builtin_types
):
320 """Generate a uniform block for a list of requirements.
322 Each member of description_list is taken to be a sequence of requirements
323 for a single field in the uniform block. These are processed by
324 generate_member_from_description.
326 In addition to the list of required fields, the uniform block is also
327 populated with a random selection of available GLSL types named in
331 names
= unique_name_dict()
335 for desc
in description_list
:
336 m
= generate_member_from_description(desc
, builtin_types
, names
)
339 if desc
[0] in ["row_major", "column_major", "#column_major"]:
340 layouts
[m
[1]] = desc
[0]
342 fields
.extend(generate_struct_of_basic_types(builtin_types
, names
))
343 random
.shuffle(fields
)
345 required_layouts
= []
346 for (field_type
, field_name
) in fields
:
347 if field_name
in layouts
:
348 required_layouts
.append(layouts
[field_name
])
350 required_layouts
.append(None)
352 return fields
, required_layouts
355 def generate_layouts(fields
, required_layouts
, allow_row_major_structure
):
356 """Generate a directed random sequence of layout qualifiers.
358 Elements in requred_layouts that are non-None will have the same value in
359 the final sequence. Remaining elements matching entries in fields that
360 are either structure or matrix types will have random layouts assigned.
362 If allow_row_major_structure is not set, only matrix types will have
363 random layout assignments. Explicit layouts in required_layouts are
366 if required_layouts
is None:
367 required_layouts
= [None] * len(fields
)
370 for ((type, name
), lay
) in zip(fields
, required_layouts
):
372 type = without_array(type)
376 elif isstructure(type) and not allow_row_major_structure
:
377 # This would work-around a bug in NVIDIA closed source drivers.
378 # They do not propagate row-major down into structures.
380 layouts
.append("#column_major")
381 elif ismatrix(type) or isstructure(type):
382 # Choose a random matrix orientation. The #column_major are
383 # ignored when the UBO is emitted, but when a the UBO is
384 # re-emitted with a default row-major layout, these become
387 layouts
.append(random
.choice(["#column_major",
398 def relayout_for_default_row_major(l
):
399 """Return a new layout for an assumed default layout of row-major."""
402 elif l
== "column_major" or l
== "#column_major":
403 return "column_major"
407 raise Exception("Invalid layout {}".format(l
))
410 def fields_to_glsl_struct(type):
411 """From a name structure type, generate a GLSL definition of that
414 # The longest type name will have the form 'dmatCxR[##]' for 11
415 # characters. Use this to set the spacing between the field type and the
418 structure_template
= Template(dedent("""\
419 struct ${struct_name} {
420 % for (field_type, field_name) in fields:
421 ${"{:<11}".format(field_type)} ${field_name};
426 return structure_template
.render(struct_name
=type, fields
=struct_types
[type])
428 def iterate_struct_array_recursive(field_type
,
437 astride
= packing
.array_stride(field_type
, field_row_major
)
438 array_member_align
= packing
.base_alignment(field_type
, field_row_major
)
440 element_t
= array_base_type(field_type
)
442 for i
in xrange(array_elements(field_type
)):
443 name_from_API_with_index
= "{}[{}]".format(name_from_API
, i
)
444 name_from_shader_with_index
= "{}[{}]".format(name_from_shader
, i
)
446 if isarray(element_t
):
447 inner_aoa_size
= arrays_of_arrays_size(element_t
)
448 o
= align(offset
, array_member_align
) + (astride
* i
* inner_aoa_size
)
450 for x
in iterate_struct_array_recursive(element_t
,
451 name_from_API_with_index
,
452 name_from_shader_with_index
,
460 a
= packing
.base_alignment(x
.GLSL_type
, row_major
)
461 o
= align(o
, a
) + packing
.size(x
.GLSL_type
, row_major
)
463 o
= align(offset
, array_member_align
) + (astride
* i
)
465 name_from_shader_with_index
,
466 name_from_API_with_index
,
471 for x
in iterate_all_recursive(struct_types
[element_t
],
473 name_from_API_with_index
,
474 name_from_shader_with_index
,
480 a
= packing
.base_alignment(x
.GLSL_type
, row_major
)
481 o
= align(o
, a
) + packing
.size(x
.GLSL_type
, row_major
)
483 def iterate_all_recursive(fields
,
486 name_from_shader_base
,
490 """In-order recursive iteration of all subfields of a structure type
492 fields -- List of fields in the uniform block or structure
493 field_layouts -- Layout for each field in the uniform block (None for
495 name_from_API_base -- Base name of the member as it would be accessed via
497 name_from_shader_base -- Base name of the member as it would be accessed via
499 packing -- Packing rules used to layout the type
500 offset -- Aligned base offset of the member
501 row_major -- True if the current matrix orientation is row-major
503 At each step of the iteration will yield a block_member object
504 representing the member.
506 Basic GLSL types will be visited once. Arrays of basic GLSL types will
507 also be visited once.
509 A structure will be visited once, then each field in the structure will be
512 An array of structures will be visited once, then each element of the
513 array will be visited. Since each element is a structure, each field will
516 Consider the following structure:
527 If name_from_API_base is "x", the members would be visited in the
528 following order: "x.s", "x.s[0]", "x.s[0].i", "x.s[0].f[0]", "x.s[1]",
529 "x.s[1].i", "x.s[1].f[0]".
531 See also iterate_all_block_members.
533 if field_layouts
is None:
534 field_layouts
= [""] * len(fields
)
536 if len(name_from_shader_base
) > 0:
537 fmt
= "{base}.{field}"
541 for ((field_type
, field_name
), explicit_layout
) in zip(fields
,
543 name_from_shader
= fmt
.format(
544 base
=name_from_shader_base
,
546 name_from_API
= fmt
.format(
547 base
=name_from_API_base
,
550 if explicit_layout
== "row_major":
551 field_row_major
= True
552 elif explicit_layout
== "column_major":
553 field_row_major
= False
555 field_row_major
= row_major
557 if isarray(field_type
):
558 base_type
= without_array(field_type
)
560 if isstructure(base_type
):
569 for x
in iterate_struct_array_recursive(field_type
,
579 elif ismatrix(base_type
):
595 elif isstructure(field_type
):
604 a
= packing
.base_alignment(field_type
, field_row_major
)
606 for x
in iterate_all_recursive(struct_types
[field_type
],
615 elif ismatrix(field_type
):
623 elif isvector(field_type
) or isscalar(field_type
):
632 raise Exception("Malformed type name {}".format(field_type
))
634 a
= packing
.base_alignment(field_type
, field_row_major
)
635 offset
= align(offset
, a
) + packing
.size(field_type
, field_row_major
)
638 def iterate_all_block_members(fields
,
644 """In-order recursive iteration of all subfields of a block
646 fields -- List of fields in the uniform block
647 field_layouts -- Layout for each field in the uniform block
648 block_name - Name of the uniform block
649 instance_name - Instance name associated with the block. May be None.
650 packing -- Packing rules used to layout the type
651 row_major -- True if the default block matrix orientation is row-major
653 At each step of the iteration will yield a block_member object
654 representing the member.
656 Basic GLSL types will be visited once. Arrays of basic GLSL types will
657 also be visited once.
659 A structure will be visited once, then each field in the structure will be
662 An array of structures will be visited once, then each element of the
663 array will be visited. Since each element is a structure, each field will
666 Consider the following block:
677 The members would be visited in the following order: s, s[0], s[0].i,
678 s[0].f[0], s[1], s[1].i, s[1].f[0].
680 for x
in iterate_all_recursive(fields
,
690 def hash_string(string
):
691 """The djb2 string hash algorithm from the old comp.lang.c days. Not a
692 terrific hash, but we just need a pseudorandom number based on the string.
695 This is used instead of the built-in hash function so that the results
696 will be consistent on other systems. See
697 http://stackoverflow.com/questions/793761/built-in-python-hash-function.
704 return h
& 0x0ffffffff
707 def random_data(type, name
, offset
):
708 """Generate pseudorandom data.
710 The data generated is based on the type, name of the field, and offset of
711 the member in the UBO.
714 h
= hash_string("{}@{}".format(offset
, name
))
717 return str(h
- 0x7fffffff)
721 return str(int((h
& 8) == 0))
722 elif type == "float" or type == "double":
723 return str(float(h
- 0x7fffffff) / 65535.0)
725 raise Exception("Unknown scalar type {}".format(type))
728 scalar
= vector_base_type(type)
730 x
= [random_data(scalar
, name
, offset
+ (i
* 3))
731 for i
in xrange(vector_size(type))]
735 r
, c
= matrix_dimensions(type)
737 x
= [random_data("float", name
, offset
+ (i
* 7))
738 for i
in xrange(r
* c
)]
742 def generate_test_vectors(fields
,
750 for m
in iterate_all_block_members(fields
,
756 a
= packing
.base_alignment(m
.GLSL_type
, m
.row_major
)
758 if isarray(m
.GLSL_type
):
759 size
= array_elements(m
.GLSL_type
)
760 base_type
= array_base_type(m
.GLSL_type
)
761 astride
= packing
.array_stride(m
.GLSL_type
, m
.row_major
)
762 name
= m
.API_name
+ "[0]"
763 while(isarray(base_type
)):
764 size
= array_elements(base_type
)
765 base_type
= array_base_type(base_type
)
768 base_type
= m
.GLSL_type
773 if ismatrix(base_type
):
774 test_vectors
.append((
780 packing
.matrix_stride(base_type
, m
.row_major
),
782 elif isvector(base_type
) or isscalar(base_type
):
783 test_vectors
.append((
795 def scalar_derp(type, name
, data
):
796 """Return a GLSL code string to compare a scalar with its expected value."""
803 return "{} != {}u".format(name
, data
)
805 return "{} != {}".format(name
, data
)
806 elif type == "float":
807 # Not all implementations support the bit-cast operators that are used
808 # to do bit-exact comparisons. For this reason float_match needs the
809 # float data and the bit-exact data.
811 bits
= bit_exact_data(data
, "float")
812 return "!float_match({}, {}, {}u)".format(name
, data
, bits
)
813 elif type == "double":
814 bits
= bit_exact_data(data
, "double")
819 hi
= "0x" + bits
[2:10]
820 lo
= "0x" + bits
[10:18]
822 return "!double_match({}, uvec2({}, {}))".format(name
, lo
, hi
)
824 raise Exception("Unknown scalar type {}".format(type))
827 def vector_derp(type, name
, data
):
828 """Return a list of GLSL code strings that compare each field of a vector
831 scalar
= vector_base_type(type)
832 components
= ["x", "y", "z", "w"]
834 return [scalar_derp(scalar
,
835 "{}.{}".format(name
, "xyzw"[i
]),
837 for i
in xrange(vector_size(type))]
840 def matrix_derp(type, name
, data
):
841 """Return a list of GLSL code strings that compare each field of a matrix
844 c
, r
= matrix_dimensions(type)
847 column_type
= "dvec{}".format(r
)
849 column_type
= "vec{}".format(r
)
854 data_pairs
.extend(vector_derp(
856 "{}[{}]".format(name
, i
),
857 data
[(i
* r
):(i
* r
) + r
]))
862 def fudge_type_for_setter(type):
863 """Return the correct type for the GL API to set the uniform.
865 Most GLSL data types are set using a the enum name for the actual type.
866 The sole exception is bool and related vector types. For bool types, the
867 same-sized (scalar or vector) integer type is used.
873 return "i" + type[1:]
878 def bit_exact_data(raw_data
, type):
879 """Several places in the test want bit-exact data for all types.
880 For integer types, this is just the raw data. For floating point (both
881 single and double precission) types, the bit-exact data is the hex
882 representation of the IEEE 754 encoding.
884 if type in ["float", "vec2", "vec3", "vec4",
885 "mat2", "mat2x2", "mat2x3", "mat2x4",
886 "mat3", "mat3x2", "mat3x3", "mat3x4",
887 "mat4", "mat4x2", "mat4x3", "mat4x4"]:
890 for d
in raw_data
.split(" "):
891 p
= struct
.pack('!f', float(d
))
892 u
= struct
.unpack('!I', p
)[0]
893 exact_data
.append(hex(u
))
895 return " ".join(exact_data
)
896 elif type in ["double", "dvec2", "dvec3", "dvec4",
897 "dmat2", "dmat2x2", "dmat2x3", "dmat2x4",
898 "dmat3", "dmat3x2", "dmat3x3", "dmat3x4",
899 "dmat4", "dmat4x2", "dmat4x3", "dmat4x4"]:
902 for d
in raw_data
.split(" "):
903 p
= struct
.pack('!d', float(d
))
904 u
= struct
.unpack('!Q', p
)[0]
906 # Sometimes the hex() generates a spurious "L" at the end of the
907 # string. Only take the first 18 characters to omit the unwanted
908 # "L". I believe this occurs when bit 63 is set.
910 exact_data
.append(hex(u
)[0:18])
912 return " ".join(exact_data
)
916 def generate_array_data_pairs(name
, api_name
, element_type
, row_major
, offset
, packing
, setters
, checkers
):
917 base_type
= array_base_type(element_type
)
919 astride
= packing
.array_stride(element_type
, row_major
)
921 for i
in xrange(array_elements(element_type
)):
923 name_with_index
= "{}[{}]".format(name
, i
)
924 api_name_with_index
= "{}[{}]".format(api_name
, i
)
926 if isarray(base_type
):
927 offset
= offset
+ (i
* arrays_of_arrays_size(base_type
) * astride
)
928 generate_array_data_pairs(name_with_index
, api_name_with_index
, base_type
, row_major
, offset
, packing
, setters
, checkers
)
930 offset
= offset
+ (i
* astride
)
931 raw_data
= random_data(base_type
, name_with_index
, offset
)
933 (fudge_type_for_setter(base_type
),
935 bit_exact_data(raw_data
, base_type
)))
937 data
= raw_data
.split(" ")
939 if isscalar(base_type
):
940 checkers
.append(scalar_derp(base_type
,
943 elif isvector(base_type
):
944 checkers
.extend(vector_derp(base_type
,
947 elif ismatrix(base_type
):
948 checkers
.extend(matrix_derp(base_type
,
952 def generate_data_pairs(uniform_blocks
, packing
):
953 """Return GLSL code to test values and shader_runner code to set them.
955 For each element in each uniform block, two lists are generated, and these
956 lists are returned in a tuple of (checkers, setters). The "checkers" list
957 is a list of GLSL comparisons that return true if the contents of the
958 uniform block element DOES NOT match the expected value.
960 The "setters" list is a list of shader_runner "uniform" statements to set
961 each uniform block member to an expected value.
971 field_layouts
) in uniform_blocks
:
972 for m
in iterate_all_block_members(
978 block_row_major_default(global_layout
, block_layout
)):
981 if isarray(m
.GLSL_type
):
982 generate_array_data_pairs(m
.GLSL_name
, m
.API_name
, m
.GLSL_type
, m
.row_major
, m
.offset
, packing
, setters
, checkers
)
984 raw_data
= random_data(m
.GLSL_type
, m
.GLSL_name
, m
.offset
)
985 setters
.append((fudge_type_for_setter(m
.GLSL_type
),
987 bit_exact_data(raw_data
, m
.GLSL_type
)))
989 data
= raw_data
.split(" ")
991 if isscalar(m
.GLSL_type
):
992 checkers
.append(scalar_derp(m
.GLSL_type
,
995 elif isvector(m
.GLSL_type
):
996 checkers
.extend(vector_derp(m
.GLSL_type
,
999 elif ismatrix(m
.GLSL_type
):
1000 checkers
.extend(matrix_derp(m
.GLSL_type
,
1004 return checkers
, setters
1007 def pretty_format_type_data(packing
, type, offset
, row_major
):
1008 """Return a string describing the layout of the type
1010 This string is intended to be a comment embedded in the GLSL shader code
1013 a
= packing
.base_alignment(type, row_major
)
1014 aligned_offset
= align(offset
, a
)
1015 size
= packing
.size(type, row_major
)
1022 astride
= packing
.array_stride(type, row_major
)
1024 base_type
= without_array(type)
1025 if ismatrix(base_type
) and row_major
:
1027 row_major_str
= "yes"
1029 row_major_str
= "no"
1031 mstride
= packing
.matrix_stride(base_type
, row_major
)
1035 row_major_str
= "yes"
1037 row_major_str
= "no"
1039 mstride
= packing
.matrix_stride(type, row_major
)
1041 return "{base_align:>3} {base_offset:>4} {aligned_offset:>5} {padded_size:>6} {row_major:^5} {array_stride:>6} {matrix_stride:>6}".format(
1044 aligned_offset
=aligned_offset
,
1046 row_major
=row_major_str
,
1047 array_stride
=astride
,
1048 matrix_stride
=mstride
1052 def pretty_format_member(m
, packing
):
1053 """Return a string of GLSL code to declare the member.
1055 m -- a block_member object
1056 packing -- packing rules
1058 If the member is "nested" inside a structure, the string returned will be
1059 a comment simply describing the layout of the submember.
1061 # If the name ends in an array subscript, emit a special line to note that
1062 # the following fields are the contents of an element of an array of
1064 if m
.GLSL_name
[-1] == "]":
1065 n
= m
.struct_nesting() + 1
1066 indent
= "// " + (" " * n
)
1068 return "{indent}{index}".format(indent
=indent
,
1069 index
=m
.GLSL_name
[m
.GLSL_name
.index("["):])
1071 # Strip off everything before the last period.
1072 name
= m
.GLSL_name
.split(".")[-1]
1074 n
= m
.struct_nesting()
1076 indent
= "// " + (" " * n
)
1077 field_str
= "{indent}{type:<11} {name:<20}".format(
1082 field_str
= " {type:<11}{name};{padding}// ".format(
1085 padding
=" "[len(name
):])
1087 data_str
= pretty_format_type_data(
1093 # If there is an explicit layout for the member, prepend it to the member
1094 # declaration. This also means that the member must be contained directly
1095 # in the UBO (i.e., not nested in a struct), so no additional indentation
1098 if m
.explicit_layout
and "#" not in m
.explicit_layout
:
1099 return " layout({layout})\n{field}{data}".format(
1100 layout
=m
.explicit_layout
,
1104 return "{field}{data}".format(field
=field_str
, data
=data_str
)
1107 def block_row_major_default(global_layout
, block_layout
):
1108 """Return true if the default matrix layout for the block is row-major."""
1109 row_major
= global_layout
is not None and "row_major" in global_layout
1112 if "row_major" in block_layout
:
1114 elif "column_major" in block_layout
:
1115 # The block layout can override a previous global layout.
1121 def generate_block_list(glsl_version
, packing
, ubo_fields
, layouts
):
1122 """Return list of uniform blocks to be passed to emit_shader_test.
1124 The supplied ubo_fields and layouts are reformatted as an element in a
1125 list of blocks that can be consumed by emit_shader_test. Depending on the
1126 value of glsl_version, additional blocks may be added to the list. The
1127 new blocks will have the same layout as the original, but the layout will
1128 be specified in different ways (e.g., by using a global layout qualifier).
1131 blocks
= [("UB1", "", None, packing
.layout_string(), ubo_fields
, layouts
)]
1133 # If the GLSL version is at least 1.50, UBO functionality is significantly
1136 # 1. Uniform blocks can have instance names. The existence of the name
1137 # changes the way the block is queried through the API (with the block
1138 # name) and the way it is accessed by the shader (with the instance
1141 # 2. Uniform blocks can be grouped in arrays. UBO arrays must have an
1144 # This is used to make the tests dramatically more complex. Each UBO is
1145 # emitted three times.
1147 # 1. Without an instance name.
1149 # 2. With an instance name and the per-block matrix layout switched to
1150 # row_major. The declared layout of the individual fields is modified
1151 # so that this block has the same layout as the previous block.
1153 # 3. With an instance name and an array size. The per-block matrix layout
1154 # is empty, but the global matrix layout is changed to row_major. This
1155 # block should have the same layout as the previous two.
1157 if glsl_version
>= 150:
1158 inverted_layouts
= [relayout_for_default_row_major(l
) for l
in layouts
]
1160 blocks
.append(("UB2",
1163 packing
.layout_string() + ", row_major",
1167 blocks
.append(("UB3",
1169 # Disabled to work around Mesa bug #83508.
1171 packing
.layout_string() + ", row_major",
1179 def emit_shader_test(blocks
, packing
, glsl_version
, extensions
):
1188 field_layouts
) in blocks
:
1190 structures
.extend([s
for s
in iterate_structures(fields
)])
1192 test_vectors
.extend(generate_test_vectors(
1198 block_row_major_default(global_layout
, block_layout
)))
1201 checkers
, setters
= generate_data_pairs(blocks
, packing
)
1203 # If the GLSL version is at least 1.40, UBOs are already supported, and we
1204 # don't need to enable the extension.
1206 if glsl_version
>= 140 and "GL_ARB_uniform_buffer_object" in extensions
:
1207 extensions
.remove("GL_ARB_uniform_buffer_object")
1209 t
= Template(dedent("""\
1211 GLSL >= ${glsl_version / 100}.${glsl_version % 100}
1212 % for ext in extensions:
1216 # Do NOT edit the following lines.
1217 # GLSL ${glsl_version}
1218 # EXTENSIONS ${extensions}
1219 # PACKING ${packing.layout_string()}
1220 % for s in structures:
1221 # STRUCT ("${s}", ${struct_types[s]})
1223 % for b in uniform_blocks:
1229 % for ext in extensions:
1230 #extension ${ext}: require
1232 #extension GL_ARB_shader_bit_encoding: enable
1233 #extension GL_ARB_gpu_shader5: enable
1235 precision highp float;
1236 % for s in structures:
1239 % for (field_type, field_name) in struct_types[s]:
1240 ${"{:<11}".format(field_type)} ${field_name};
1245 % for (block_name, instance_name, global_layout, block_layout, fields, field_layouts) in uniform_blocks:
1247 layout(${global_layout}) uniform;
1251 layout(${block_layout})
1253 uniform ${block_name} {
1254 // base base align padded row- array matrix
1255 // align off. off. size major stride stride
1256 % for m in iterate_all_block_members(fields, field_layouts, block_name, instance_name, packing, block_row_major_default(global_layout, block_layout)):
1257 ${pretty_format_member(m, packing)}
1262 flat out int vertex_pass;
1263 in vec4 piglit_vertex;
1265 #if defined(GL_ARB_shader_bit_encoding) || defined(GL_ARB_gpu_shader5) || __VERSION__ >= 430
1266 bool float_match(float u, float f, uint bits) { return floatBitsToUint(u) == bits; }
1268 bool float_match(float u, float f, uint bits) { return u == f; }
1270 % if glsl_version >= 400 or "GL_ARB_gpu_shader_fp64" in extensions:
1272 bool double_match(double u, uvec2 bits) { return unpackDouble2x32(u) == bits; }
1277 /* std140 (or shared) layout prevents any fields or blocks from being
1278 * eliminated. Section 2.11.6 of the OpenGL ES 3.0 spec makes this
1279 * explicit, but desktop GL specs only imply it.
1283 % for i in xrange(len(checkers)):
1290 vertex_pass = int(pass);
1291 gl_Position = piglit_vertex;
1295 precision highp float;
1297 out vec4 piglit_fragcolor;
1298 flat in int vertex_pass;
1302 piglit_fragcolor = bool(vertex_pass) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
1307 % for (name, type, size, offset, astride, mstride, row_major) in test_vectors:
1309 active uniform ${name} GL_UNIFORM_TYPE ${type}
1310 active uniform ${name} GL_UNIFORM_SIZE ${size}
1311 % if packing.fixed_offsets():
1312 active uniform ${name} GL_UNIFORM_OFFSET ${offset}
1313 active uniform ${name} GL_UNIFORM_ARRAY_STRIDE ${astride}
1314 active uniform ${name} GL_UNIFORM_MATRIX_STRIDE ${mstride}
1316 active uniform ${name} GL_UNIFORM_IS_ROW_MAJOR ${row_major}
1319 % for (type, name, data) in setters:
1320 uniform ${type} ${name} ${data}
1324 probe all rgba 0.0 1.0 0.0 1.0"""))
1326 return t
.render(glsl_version
=glsl_version
,
1327 extensions
=extensions
,
1328 structures
=structures
,
1329 test_vectors
=test_vectors
,
1330 uniform_blocks
=blocks
,
1332 iterate_all_block_members
=iterate_all_block_members
,
1333 pretty_format_member
=pretty_format_member
,
1334 block_row_major_default
=block_row_major_default
,
1335 struct_types
=struct_types
,
1340 def generate_file_name(requirements
, packing
):
1341 """Based on the required UBO elements and the packing, generate a
1342 plausible file name for the test case.
1344 prefix
= packing
.layout_string() + "-"
1345 suffix
= ".shader_test"
1347 body
= "-and-".join(["-".join(req
) for req
in requirements
])
1349 return prefix
+ body
+ suffix
1352 class packing_rules(object):
1353 __metaclass__
= abc
.ABCMeta
1355 @abc.abstractproperty
1356 def layout_string(self
):
1357 """Get the string used in a layout qualifier to select this set of
1361 @abc.abstractproperty
1362 def fixed_offsets(self
):
1363 """Return true if fields in this layout have fixed locations (e.g.,
1364 std140) or false if they can vary among implementations (e.g., shared
1369 def base_alignment(self
, type, row_major
):
1370 """Determine the base alignment, in bytes, of the named type"""
1373 def matrix_stride(self
, type, row_major
):
1374 """Determine the stride, in bytes, from one indexable vector of the
1375 matrix (column or row depending on the orientation) to the next.
1379 def array_stride(self
, type, row_major
):
1380 """Determine the stride, in bytes, from one array element to the next.
1381 If the type is not an array type, zero is returned.
1384 def size(self
, type, row_major
):
1385 """Determine the size, in bytes, of the specified type.
1387 Applying the packing rules and the row-major setting, determine the
1388 size, in bytes, of the entire data type. This will include padded
1389 after the data element as required by the GLSL specification.
1392 return self
.array_stride(type, row_major
) * arrays_of_arrays_size(type)
1394 if type in ["float", "bool", "int", "uint"]:
1397 if type == "double":
1400 if type in ["vec2", "bvec2", "ivec2", "uvec2"]:
1406 if type in ["vec3", "bvec3", "ivec3", "uvec3"]:
1412 if type in ["vec4", "bvec4", "ivec4", "uvec4"]:
1419 c
, r
= matrix_dimensions(type)
1421 return c
* self
.matrix_stride(type, row_major
)
1423 return r
* self
.matrix_stride(type, row_major
)
1425 if type not in struct_types
:
1426 raise Exception("Unknown type {}".format(type))
1429 fields
= struct_types
[type]
1430 for (t
, n
) in fields
:
1431 a
= self
.base_alignment(t
, row_major
)
1433 s
= align(s
, a
) + self
.size(t
, row_major
)
1435 s
= align(s
, self
.base_alignment(type, row_major
))
1439 class std140_packing_rules(packing_rules
):
1440 def layout_string(self
):
1443 def fixed_offsets(self
):
1446 def base_alignment(self
, type, row_major
):
1447 # (4) If the member is an array of scalars or vectors, the base
1448 # alignment and array stride are set to match the base alignment
1449 # of a single array element, according to rules (1), (2), and (3),
1450 # and rounded up to the base alignment of a vec4. The array may
1451 # have padding at the end; the base offset of the member following
1452 # the array is rounded up to the next multiple of the base
1457 self
.base_alignment(array_base_type(type), row_major
))
1459 # (1) If the member is a scalar consuming <N> basic machine units, the
1460 # base alignment is <N>.
1463 return basic_machine_units(type)
1466 # (2) If the member is a two- or four-component vector with
1467 # components consuming <N> basic machine units, the base
1468 # alignment is 2<N> or 4<N>, respectively.
1470 # (3) If the member is a three-component vector with components
1471 # consuming <N> basic machine units, the base alignment is
1474 components
= vector_size(type)
1475 if components
== 2 or components
== 4:
1476 return components
* basic_machine_units(vector_base_type(type))
1477 elif components
== 3:
1478 return 4 * basic_machine_units(vector_base_type(type))
1480 raise Exception("Invalid vector size {} for type {}".format(
1483 elif ismatrix(type):
1484 return self
.matrix_stride(type, row_major
)
1486 if type not in struct_types
:
1487 raise Exception("Unknown type {}".format(type))
1489 # (9) If the member is a structure, the base alignment of the
1490 # structure is <N>, where <N> is the largest base alignment value
1491 # of any of its members, and rounded up to the base alignment of a
1492 # vec4. The individual members of this sub-structure are then
1493 # assigned offsets by applying this set of rules recursively,
1494 # where the base offset of the first member of the sub-structure
1495 # is equal to the aligned offset of the structure. The structure
1496 # may have padding at the end; the base offset of the member
1497 # following the sub-structure is rounded up to the next multiple
1498 # of the base alignment of the structure.
1501 fields
= struct_types
[type]
1502 for (field_type
, field_name
) in fields
:
1503 a
= max(a
, self
.base_alignment(field_type
, row_major
))
1507 def matrix_stride(self
, type, row_major
):
1508 c
, r
= matrix_dimensions(type)
1510 # (4) If the member is an array of scalars or vectors, the base
1511 # alignment and array stride are set to match the base
1512 # alignment of a single array element, according to rules (1),
1513 # (2), and (3), and rounded up to the base alignment of a
1514 # vec4. The array may have padding at the end; the base offset
1515 # of the member following the array is rounded up to the next
1516 # multiple of the base alignment.
1518 # (5) If the member is a column-major matrix with <C> columns and
1519 # <R> rows, the matrix is stored identically to an array of
1520 # <C> column vectors with <R> components each, according to
1524 return max(16, self
.base_alignment("dvec{}".format(r
), False))
1526 return max(16, self
.base_alignment("vec{}".format(r
), False))
1528 # (7) If the member is a row-major matrix with <C> columns and <R>
1529 # rows, the matrix is stored identically to an array of <R>
1530 # row vectors with <C> components each, according to rule (4).
1533 return max(16, self
.base_alignment("dvec{}".format(c
), False))
1535 return max(16, self
.base_alignment("vec{}".format(c
), False))
1537 def array_stride(self
, type, row_major
):
1538 base_type
= without_array(type)
1540 if not isstructure(base_type
):
1541 # (4) If the member is an array of scalars or vectors, the base
1542 # alignment and array stride are set to match the base
1543 # alignment of a single array element, according to rules (1),
1544 # (2), and (3), and rounded up to the base alignment of a
1545 # vec4. The array may have padding at the end; the base offset
1546 # of the member following the array is rounded up to the next
1547 # multiple of the base alignment.
1549 max(self
.base_alignment(base_type
, row_major
),
1550 self
.size(base_type
, row_major
)))
1552 # (9) If the member is a structure, the base alignment of the
1553 # structure is <N>, where <N> is the largest base alignment
1554 # value of any of its members, and rounded up to the base
1555 # alignment of a vec4. The individual members of this
1556 # sub-structure are then assigned offsets by applying this set
1557 # of rules recursively, where the base offset of the first
1558 # member of the sub-structure is equal to the aligned offset
1559 # of the structure. The structure may have padding at the end;
1560 # the base offset of the member following the sub-structure is
1561 # rounded up to the next multiple of the base alignment of the
1564 # (10) If the member is an array of <S> structures, the <S> elements
1565 # of the array are laid out in order, according to rule (9).
1567 return align(self
.size(base_type
, row_major
),
1568 self
.base_alignment(base_type
, row_major
))
1571 class shared_packing_rules(std140_packing_rules
):
1572 def layout_string(self
):
1575 def fixed_offsets(self
):
1579 class unique_name_dict
:
1580 """Helper class to generate a unique name for each field.
1582 Fields for each particular type have names based on the type. Each name
1583 is suffixed by a number, and the number is incremented each time a field
1584 of that type is created. For example, the first int field will be named
1585 "i1", and the second will be named "i2".
1591 def trim_name(self
, type):
1592 """Internal method to reduce a type name to a canonical form
1594 These names are used to track the type in the self.names dict.
1596 Array types have the arrayness stripped. Vector types have vector
1597 count stripped. Matrix types are converted to canonical "mat#x#"
1598 form. All other type names are unmodified.
1601 t
= without_array(type)
1606 # Canonicalize matrix type names.
1607 c
, r
= matrix_dimensions(t
)
1609 name
= "mat{}x{}".format(c
, r
)
1617 return t
.strip("1234")
1619 # Assume it must be a structure.
1622 def add_type(self
, type):
1623 """Internal method to add a previously unknown type to the dictionary
1625 type -- Actual GLSL type name
1627 Generates the base name for fields of this type. Adds a tuple to dict
1628 (indexed by "type") of (base_name, 1).
1631 t
= without_array(type)
1636 base
= "{}v".format(vector_base_type(t
)[0])
1638 c
, r
= matrix_dimensions(t
)
1641 base
= "dm{}{}_".format(c
, r
)
1643 base
= "m{}{}_".format(c
, r
)
1647 base
= "s{}_".format(t
[1:])
1649 raise Exception("Malformed type name {}".format(t
))
1651 self
.names
[self
.trim_name(t
)] = (base
, 1)
1654 def get_name(self
, type):
1655 """Return a unique name for a field of the specified type."""
1656 t
= self
.trim_name(type)
1657 if t
not in self
.names
:
1660 base
, count
= self
.names
[t
]
1661 self
.names
[t
] = (base
, count
+ 1)
1663 return "{}{}".format(base
, count
)
1666 class block_member(object):
1667 """Helper class to track all of the layout information for a single
1668 (sub-)member of a uniform block."""
1671 'float': "GL_FLOAT",
1672 'vec2': "GL_FLOAT_VEC2",
1673 'vec3': "GL_FLOAT_VEC3",
1674 'vec4': "GL_FLOAT_VEC4",
1676 'double': "GL_DOUBLE",
1677 'dvec2': "GL_DOUBLE_VEC2",
1678 'dvec3': "GL_DOUBLE_VEC3",
1679 'dvec4': "GL_DOUBLE_VEC4",
1682 'ivec2': "GL_INT_VEC2",
1683 'ivec3': "GL_INT_VEC3",
1684 'ivec4': "GL_INT_VEC4",
1686 'uint': "GL_UNSIGNED_INT",
1687 'uvec2': "GL_UNSIGNED_INT_VEC2",
1688 'uvec3': "GL_UNSIGNED_INT_VEC3",
1689 'uvec4': "GL_UNSIGNED_INT_VEC4",
1692 'bvec2': "GL_BOOL_VEC2",
1693 'bvec3': "GL_BOOL_VEC3",
1694 'bvec4': "GL_BOOL_VEC4",
1696 'mat2': "GL_FLOAT_MAT2",
1697 'mat2x2': "GL_FLOAT_MAT2",
1698 'mat2x3': "GL_FLOAT_MAT2x3",
1699 'mat2x4': "GL_FLOAT_MAT2x4",
1701 'mat3': "GL_FLOAT_MAT3",
1702 'mat3x2': "GL_FLOAT_MAT3x2",
1703 'mat3x3': "GL_FLOAT_MAT3",
1704 'mat3x4': "GL_FLOAT_MAT3x4",
1706 'mat4': "GL_FLOAT_MAT4",
1707 'mat4x2': "GL_FLOAT_MAT4x2",
1708 'mat4x3': "GL_FLOAT_MAT4x3",
1709 'mat4x4': "GL_FLOAT_MAT4",
1711 'dmat2': "GL_DOUBLE_MAT2",
1712 'dmat2x2': "GL_DOUBLE_MAT2",
1713 'dmat2x3': "GL_DOUBLE_MAT2x3",
1714 'dmat2x4': "GL_DOUBLE_MAT2x4",
1716 'dmat3': "GL_DOUBLE_MAT3",
1717 'dmat3x2': "GL_DOUBLE_MAT3x2",
1718 'dmat3x3': "GL_DOUBLE_MAT3",
1719 'dmat3x4': "GL_DOUBLE_MAT3x4",
1721 'dmat4': "GL_DOUBLE_MAT4",
1722 'dmat4x2': "GL_DOUBLE_MAT4x2",
1723 'dmat4x3': "GL_DOUBLE_MAT4x3",
1724 'dmat4x4': "GL_DOUBLE_MAT4"
1734 self
.GLSL_name
= GLSL_name
1735 self
.GLSL_type
= GLSL_type
1737 self
.API_name
= API_name
1739 self
.explicit_layout
= explicit_layout
1740 self
.offset
= offset
1741 self
.row_major
= row_major
1743 if isarray(GLSL_type
):
1744 base_type
= without_array(GLSL_type
)
1745 if isstructure(base_type
):
1746 self
.API_type
= None
1748 self
.API_type
= self
.TYPE_ENUM
[base_type
];
1750 self
.size
= array_elements(GLSL_type
)
1751 elif isstructure(GLSL_type
):
1752 self
.API_type
= None
1755 self
.API_type
= self
.TYPE_ENUM
[GLSL_type
];
1758 def struct_nesting(self
):
1759 if "." in self
.GLSL_name
:
1760 # If the block has an instance name, the API name will use the
1761 # block name instead of the instance name. As a result,
1762 # GLSL_name and API_name will be different.
1764 # The first "." is for the block instance name, so it does not count
1765 # as structure nesting.
1767 if self
.GLSL_name
!= self
.API_name
:
1768 return collections
.Counter(self
.GLSL_name
)["."] - 1
1770 return collections
.Counter(self
.GLSL_name
)["."]
1775 if __name__
== "__main__":
1776 if len(sys
.argv
) > 1:
1777 max_glsl_version
= int(sys
.argv
[1])
1779 max_glsl_version
= 130
1781 if len(sys
.argv
) > 2:
1782 extensions
= sys
.argv
[2:]
1786 # Pick a random GLSL version from the available set of possible versions.
1787 glsl_version
= random
.choice([v
for v
in [130, 140, 150, 400, 430]
1788 if v
<= max_glsl_version
])
1790 # Use the GLSL version filter out some extensions that are redundant.
1791 if glsl_version
>= 140 and "GL_ARB_uniform_buffer_object" in extensions
:
1792 extensions
.remove("GL_ARB_uniform_buffer_object")
1794 if glsl_version
>= 400 and "GL_ARB_gpu_shader_fp64" in extensions
:
1795 extensions
.remove("GL_ARB_gpu_shader_fp64")
1797 if glsl_version
>= 430 and "GL_ARB_arrays_of_arrays" in extensions
:
1798 extensions
.remove("GL_ARB_arrays_of_arrays")
1800 # Pick a random subset of the remaining extensions.
1801 num_ext
= len(extensions
)
1803 random
.shuffle(extensions
)
1804 r
= random
.randint(0, num_ext
)
1805 extensions
= extensions
[:r
]
1807 # Based on the GLSL version and the set of extensions, pick the set of
1808 # possible data types.
1809 if glsl_version
< 400:
1810 types
= ALL130_TYPES
1812 types
= ALL400_TYPES
1814 if "GL_ARB_gpu_shader_fp64" in extensions
:
1815 types
.extend(DOUBLE_TYPES
)
1817 # Based on the GLSL version, pick a set of packing rules
1818 # FINISHME: Add support for std430_packing_rules() soon.
1819 packing
= random
.choice([std140_packing_rules(), shared_packing_rules()])
1821 # Based on the GLSL version and the set of available extensions, pick
1822 # some required combinations of data structures to include in the UBO.
1823 arrays_of_arrays
= (glsl_version
>= 430 or
1824 "GL_ARB_arrays_of_arrays" in extensions
)
1826 allow_row_major_structure
= glsl_version
>= 150
1830 x
= [random
.choice(["array", "struct"])]
1833 # If arrays-of-arrays are not supported, don't allow "array" to be
1834 # picked twice in a row.
1836 if x
[-1] == "array" and not arrays_of_arrays
:
1839 x
.append(random
.choice(["array", "struct"]))
1841 if "struct" in x
and allow_row_major_structure
:
1842 ordering
= random
.choice([None,
1853 requirements
.append(x
)
1855 if glsl_version
< 140:
1856 extensions
.append("GL_ARB_uniform_buffer_object")
1858 # Generate the test!
1859 fields
, required_layouts
= generate_ubo(requirements
, types
)
1861 layouts
= generate_layouts(
1864 allow_row_major_structure
)
1866 blocks
= generate_block_list(
1872 print(emit_shader_test(