3 # Copyright © 2011 Intel Corporation
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the "Software"),
7 # to deal in the Software without restriction, including without limitation
8 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 # and/or sell copies of the Software, and to permit persons to whom the
10 # Software is furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice (including the next
13 # paragraph) shall be included in all copies or substantial portions of the
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
19 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 # DEALINGS IN THE SOFTWARE.
24 # Generate a pair of glsl parser tests for every overloaded version of
25 # every built-in function, which test that the built-in functions are
26 # handled properly when applied to constant arguments inside an array
29 # In each pair of generated tests, one test exercises the built-in
30 # function in vertex shaders, and the other exercises it in fragment
33 # This program outputs, to stdout, the name of each file it generates.
34 # With the optional argument --names-only, it only outputs the names
35 # of the files; it doesn't generate them.
37 from __future__
import print_function
, division
, absolute_import
38 from builtin_function
import *
44 from six
.moves
import range
46 from modules
import utils
48 class ParserTest(object):
49 """Class used to build a test of a single built-in. This is an
50 abstract base class--derived types should override test_suffix(),
51 output_var(), and other functions if necessary.
54 def __init__(self
, signature
, test_vectors
):
55 """Prepare to build a test for a single built-in. signature
56 is the signature of the built-in (a key from the
57 builtin_function.test_suite dict), and test_vectors is the
58 list of test vectors for testing the given builtin (the
59 corresponding value from the builtin_function.test_suite
62 self
.__signature
= signature
63 self
.__test
_vectors
= test_vectors
65 def glsl_version(self
):
66 if self
.__signature
.version_introduced
< 120:
67 # Before version 1.20, built-in function invocations
68 # weren't allowed in constant expressions. So even if
69 # this built-in was introduced prior to 1.20, test it in
73 return self
.__signature
.version_introduced
75 def version_directive(self
):
76 return '#version {0}\n'.format(self
.glsl_version())
78 def additional_declarations(self
):
79 """Return a string containing any additional declarations that
80 should be placed after the version directive. Returns the
81 empty string by default.
85 def additional_extensions(self
):
86 """Return a list (or other iterable) containing any additional
87 extension requirements that the test has. Returns the empty
91 if self
.__signature
.extension
:
92 ret
.append(self
.__signature
.extension
)
97 def test_suffix(self
):
98 """Return the suffix that should be used in the test file name
99 to identify the type of shader, e.g. "vert" for a vertex
103 def make_condition(self
, test_vector
):
104 """Generate a GLSL constant expression that should evaluate to
105 true if the GLSL compiler's constant evaluation produces the
106 correct result for the given test vector, and false if not.
108 invocation
= self
.__signature
.template
.format(
109 *[glsl_constant(x
) for x
in test_vector
.arguments
])
110 if self
.__signature
.rettype
.base_type
== glsl_float
:
111 # Test floating-point values within tolerance
112 if self
.__signature
.name
== 'distance':
113 # Don't use the distance() function to test itself.
114 return '{0} <= {1} && {1} <= {2}'.format(
115 test_vector
.result
- test_vector
.tolerance
,
117 test_vector
.result
+ test_vector
.tolerance
)
118 elif self
.__signature
.rettype
.is_matrix
:
119 # We can't apply distance() to matrices. So apply it
120 # to each column and root-sum-square the results. It
121 # is safe to use pow() here because its behavior is
122 # verified in the pow() tests.
124 for col
in range(self
.__signature
.rettype
.num_cols
):
125 terms
.append('pow(distance({0}[{1}], {2}), 2)'.format(
127 glsl_constant(test_vector
.result
[:, col
])))
128 rss_distance
= ' + '.join(terms
)
129 sq_tolerance
= test_vector
.tolerance
* test_vector
.tolerance
130 return '{0} <= {1}'.format(
131 rss_distance
, glsl_constant(sq_tolerance
))
133 return 'distance({0}, {1}) <= {2}'.format(
134 invocation
, glsl_constant(test_vector
.result
),
135 glsl_constant(test_vector
.tolerance
))
137 # Test non-floating point values exactly
138 assert not self
.__signature
.rettype
.is_matrix
139 if self
.__signature
.name
== 'equal':
140 # Don't use the equal() function to test itself.
141 assert self
.__signature
.rettype
.is_vector
143 for row
in range(self
.__signature
.rettype
.num_rows
):
144 terms
.append('{0}[{1}] == {2}'.format(
146 glsl_constant(test_vector
.result
[row
])))
147 return ' && '.join(terms
)
148 elif self
.__signature
.rettype
.is_vector
:
149 return 'all(equal({0}, {1}))'.format(
150 invocation
, glsl_constant(test_vector
.result
))
152 return '{0} == {1}'.format(
153 invocation
, glsl_constant(test_vector
.result
))
155 def make_shader(self
):
156 """Generate the shader code necessary to test the built-in."""
157 shader
= self
.version_directive()
158 if self
.__signature
.extension
:
159 shader
+= '#extension GL_{0} : require\n'.format(self
.__signature
.extension
)
160 shader
+= self
.additional_declarations()
162 shader
+= 'void main()\n'
165 for i
, test_vector
in enumerate(self
.__test
_vectors
):
166 shader
+= ' float[{0} ? 1 : -1] array{1};\n'.format(
167 self
.make_condition(test_vector
), i
)
168 lengths
.append('array{0}.length()'.format(i
))
169 shader
+= ' {0} = vec4({1});\n'.format(
170 self
.output_var(), ' + '.join(lengths
))
175 argtype_names
= '-'.join(
176 str(argtype
) for argtype
in self
.__signature
.argtypes
)
177 if self
.__signature
.extension
:
178 subdir
= self
.__signature
.extension
.lower()
180 subdir
= 'glsl-{0:1.2f}'.format(float(self
.glsl_version()) / 100)
182 'spec', subdir
, 'compiler', 'built-in-functions',
183 '{0}-{1}.{2}'.format(
184 self
.__signature
.name
, argtype_names
, self
.test_suffix()))
186 def generate_parser_test(self
):
187 """Generate the test and write it to the output file."""
188 parser_test
= '/* [config]\n'
189 parser_test
+= ' * expect_result: pass\n'
190 parser_test
+= ' * glsl_version: {0:1.2f}\n'.format(
191 float(self
.glsl_version()) / 100)
192 req_extensions
= list(self
.additional_extensions())
194 parser_test
+= ' * require_extensions: {}\n'.format(
195 ' '.join('GL_{}'.format(r
) for r
in req_extensions
))
196 parser_test
+= ' * [end config]\n'
197 parser_test
+= ' *\n'
198 parser_test
+= ' * Check that the following test vectors are constant'\
199 ' folded correctly:\n'
200 for test_vector
in self
.__test
_vectors
:
201 parser_test
+= ' * {0} => {1}\n'.format(
202 self
.__signature
.template
.format(
203 *[glsl_constant(arg
) for arg
in test_vector
.arguments
]),
204 glsl_constant(test_vector
.result
))
205 parser_test
+= ' */\n'
206 parser_test
+= self
.make_shader()
207 filename
= self
.filename()
208 dirname
= os
.path
.dirname(filename
)
209 utils
.safe_makedirs(dirname
)
210 with
open(filename
, 'w') as f
:
214 class VertexParserTest(ParserTest
):
215 """Derived class for tests that exercise the built-in in a vertex
218 def test_suffix(self
):
221 def output_var(self
):
225 class GeometryParserTest(ParserTest
):
226 """Derived class for tests that exercise the built-in in a geometry
229 def glsl_version(self
):
230 return max(150, ParserTest
.glsl_version(self
))
232 def test_suffix(self
):
235 def output_var(self
):
239 class FragmentParserTest(ParserTest
):
240 """Derived class for tests that exercise the built-in in a fagment
243 def test_suffix(self
):
246 def output_var(self
):
247 return 'gl_FragColor'
251 for signature
, test_vectors
in sorted(test_suite
.items()):
252 # Assignment operators other than = cannot be used in the constant
254 if not signature
.name
.startswith('op-assign'):
255 yield VertexParserTest(signature
, test_vectors
)
256 yield GeometryParserTest(signature
, test_vectors
)
257 yield FragmentParserTest(signature
, test_vectors
)
261 desc
= 'Generate shader tests that test built-in functions using constant'\
263 usage
= 'usage: %prog [-h] [--names-only]'
264 parser
= optparse
.OptionParser(description
=desc
, usage
=usage
)
265 parser
.add_option('--names-only',
268 help="Don't output files, just generate a list of"
269 "filenames to stdout")
270 options
, args
= parser
.parse_args()
272 for test
in all_tests():
273 if not options
.names_only
:
274 test
.generate_parser_test()
275 print(test
.filename())
278 if __name__
== '__main__':