draw-prim-rate: test triangle strips with primitive restart but no restarts
[piglit.git] / generated_tests / gen_builtin_uniform_tests_fp64.py
blob25c52321c0841d47705e19c136de0ac698f0fa8f
1 # coding=utf-8
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
14 # 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
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 set of shader_runner tests for every overloaded version
25 # of every built-in function, based on the test vectors computed by
26 # builtin_function.py.
28 # In each set of generated tests, one test exercises the built-in
29 # function in each type of shader (vertex, geometry, and fragment).
30 # In all cases, the inputs to the built-in function come from
31 # uniforms, so that the effectiveness of the test won't be
32 # circumvented by constant folding in the GLSL compiler.
34 # The tests operate by invoking the built-in function in the
35 # appropriate shader, applying a scale and offset so that the expected
36 # values are in the range [0.25, 0.75], and then outputting the result
37 # as a solid rgba color, which is then checked using shader_runner's
38 # "probe rgba" command.
40 # For built-in functions whose result type is a matrix, the test
41 # checks one column at a time.
43 # This program outputs, to stdout, the name of each file it generates.
44 # With the optional argument --names-only, it only outputs the names
45 # of the files; it doesn't generate them.
47 from builtin_function_fp64 import *
48 import abc
49 import numpy
50 import optparse
51 import os
52 import os.path
54 from modules import utils
57 def compute_offset_and_scale(test_vectors):
58 """Compute scale and offset values such that for each result in
59 test_vectors, (result - offset) * scale is in the range [0.25,
60 0.75], and scale is less than or equal to 1.0. These values are
61 used to transform the test vectors so that their outputs can be
62 stored in gl_FragColor without overflow.
63 """
64 low = min(numpy.min(tv.result) for tv in test_vectors)
65 hi = max(numpy.max(tv.result) for tv in test_vectors)
66 span = hi - low
67 center = (hi + low)/2.0
68 span *= 2.0
69 if span < 1.0:
70 span = 1.0
71 offset = center - span/2.0
72 scale = 1.0/span
73 return offset, scale
76 def shader_runner_format(values):
77 """Format the given values for use in a shader_runner "uniform" or
78 "probe rgba" command. Bools are converted to 0's and 1's, and
79 values are separated by spaces.
80 """
81 transformed_values = []
82 for value in values:
83 if isinstance(value, (bool, np.bool_)):
84 transformed_values.append(int(value))
85 else:
86 transformed_values.append(value)
87 return ' '.join(repr(x) for x in transformed_values)
90 def shader_runner_type(glsl_type):
91 """Return the appropriate type name necessary for binding a
92 uniform of the given type using shader_runner's "uniform" command.
93 Boolean values and vectors are converted to ints, and square
94 matrices are written in "matNxN" form.
95 """
96 if glsl_type.base_type == glsl_bool:
97 if glsl_type.is_scalar:
98 return 'int'
99 else:
100 return 'ivec{0}'.format(glsl_type.num_rows)
101 if glsl_type.is_matrix:
102 return 'dmat{0}x{1}'.format(glsl_type.num_cols, glsl_type.num_rows)
103 else:
104 return str(glsl_type)
107 class Comparator(object):
108 """Base class which abstracts how we compare expected and actual
109 values.
111 __metaclass__ = abc.ABCMeta
113 def make_additional_declarations(self):
114 """Return additional declarations, if any, that are needed in
115 the shader program.
117 return ''
119 @abc.abstractmethod
120 def make_result_handler(self, invocation, output_var):
121 """Return the shader code that is needed to produce the result
122 and store it in output_var.
124 invocation is the GLSL code to compute the output of the
125 built-in function.
128 @abc.abstractmethod
129 def make_result_test(self, test_num, test_vector):
130 """Return the shader_runner test code that is needed to test a
131 single test vector.
134 def testname_suffix(self):
135 """Return a string to be used as a suffix on the test name to
136 distinguish it from tests using other comparators."""
137 return ''
140 class BoolComparator(Comparator):
141 """Comparator that tests functions returning bools and bvecs by
142 converting them to floats.
144 This comparator causes code to be generated in the following form:
146 rettype result = func(args);
147 output_var = vec4(result, 0.0, ...);
149 def __init__(self, signature):
150 assert not signature.rettype.is_matrix
151 self.__signature = signature
152 self.__padding = 4 - signature.rettype.num_rows
154 def make_result_handler(self, invocation, output_var):
155 statements = ' {0} result = {1};\n'.format(
156 self.__signature.rettype, invocation)
157 statements += ' {0} = vec4(result{1});\n'.format(
158 output_var, ', 0.0' * self.__padding)
159 return statements
161 def convert_to_float(self, value):
162 """Convert the given vector or scalar value to a list of
163 floats representing the expected color produced by the test.
165 value = value*1.0 # convert bools to floats
166 value = column_major_values(value)
167 value += [0.0] * self.__padding
168 return value
170 def make_result_test(self, test_num, test_vector, draw):
171 test = draw
172 test += 'probe rgba {0} 0 {1}\n'.format(
173 test_num,
174 shader_runner_format(self.convert_to_float(test_vector.result)))
175 return test
178 class BoolIfComparator(Comparator):
179 """Comparator that tests functions returning bools by evaluating
180 them inside an if statement.
182 This comparator causes code to be generated in the following form:
184 if (func(args))
185 output_var = vec4(1.0, 1.0, 0.0, 1.0);
186 else
187 output_var = vecp(0.0, 0.0, 1.0, 1.0);
189 def __init__(self, signature):
190 assert signature.rettype == glsl_bool
191 self.__padding = 4 - signature.rettype.num_rows
193 def make_result_handler(self, invocation, output_var):
194 statements = ' if({0})\n'.format(invocation)
195 statements += ' {0} = vec4(1.0, 1.0, 0.0, 1.0);\n'.format(
196 output_var)
197 statements += ' else\n'
198 statements += ' {0} = vec4(0.0, 0.0, 1.0, 1.0);\n'.format(
199 output_var)
200 return statements
202 def convert_to_float(self, value):
203 """Convert the given vector or scalar value to a list of
204 floats representing the expected color produced by the test.
206 if value:
207 return [1.0, 1.0, 0.0, 1.0]
208 else:
209 return [0.0, 0.0, 1.0, 1.0]
211 def make_result_test(self, test_num, test_vector, draw):
212 test = draw
213 test += 'probe rgba {0} 0 {1}\n'.format(
214 test_num,
215 shader_runner_format(self.convert_to_float(test_vector.result)))
216 return test
218 def testname_suffix(self):
219 return '-using-if'
222 class IntComparator(Comparator):
223 """Comparator that tests functions returning ints or ivecs using a
224 strict equality test.
226 This comparator causes code to be generated in the following form:
228 rettype result = func(args);
229 output_var = result == expected ? vec4(0.0, 1.0, 0.0, 1.0)
230 : vec4(1.0, 0.0, 0.0, 1.0);
232 def __init__(self, signature):
233 self.__signature = signature
235 def make_additional_declarations(self):
236 return 'uniform {0} expected;\n'.format(self.__signature.rettype)
238 def make_result_handler(self, invocation, output_var):
239 statements = ' {0} result = {1};\n'.format(
240 self.__signature.rettype, invocation)
241 statements += ' {v} = {cond} ? {green} : {red};\n'.format(
242 v=output_var, cond='result == expected',
243 green='vec4(0.0, 1.0, 0.0, 1.0)',
244 red='vec4(1.0, 0.0, 0.0, 1.0)')
245 return statements
247 def make_result_test(self, test_num, test_vector, draw):
248 test = 'uniform {0} expected {1}\n'.format(
249 shader_runner_type(self.__signature.rettype),
250 shader_runner_format(column_major_values(test_vector.result)))
251 test += draw
252 test += 'probe rgba {0} 0 0.0 1.0 0.0 1.0\n'.format(test_num)
253 return test
256 class FloatComparator(Comparator):
257 """Comparator that tests functions returning floats or vecs using a
258 strict equality test.
260 This comparator causes code to be generated in the following form:
262 rettype result = func(args);
263 output_var = distance(result, expected) <= tolerance
264 ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);
266 def __init__(self, signature):
267 self.__signature = signature
269 def make_additional_declarations(self):
270 decls = 'uniform double tolerance;\n'
271 decls += 'uniform {0} expected;\n'.format(self.__signature.rettype)
272 return decls
274 def make_indexers(self):
275 """Build a list of strings which index into every possible
276 value of the result. For example, if the result is a vec2,
277 then build the indexers ['[0]', '[1]'].
279 if self.__signature.rettype.num_cols == 1:
280 col_indexers = ['']
281 else:
282 col_indexers = ['[{0}]'.format(i)
283 for i in range(self.__signature.rettype.num_cols)]
284 if self.__signature.rettype.num_rows == 1:
285 row_indexers = ['']
286 else:
287 row_indexers = ['[{0}]'.format(i)
288 for i in range(self.__signature.rettype.num_rows)]
289 return [col_indexer + row_indexer
290 for col_indexer in col_indexers
291 for row_indexer in row_indexers]
293 def make_result_handler(self, invocation, output_var):
294 statements = ' {0} result = {1};\n'.format(
295 self.__signature.rettype, invocation)
296 # Can't use distance when testing itself, or when the rettype
297 # is a matrix.
298 if self.__signature.name == 'distance' or \
299 self.__signature.rettype.is_matrix:
300 statements += ' {0} residual = result - expected;\n'.format(
301 self.__signature.rettype)
302 statements += ' double error_sq = {0};\n'.format(
303 ' + '.join(
304 'residual{0} * residual{0}'.format(indexer)
305 for indexer in self.make_indexers()))
306 condition = 'error_sq <= tolerance * tolerance'
307 else:
308 condition = 'distance(result, expected) <= tolerance'
309 statements += ' {v} = {cond} ? {green} : {red};\n'.format(
310 v=output_var, cond=condition, green='vec4(0.0, 1.0, 0.0, 1.0)',
311 red='vec4(1.0, 0.0, 0.0, 1.0)')
312 return statements
314 def make_result_test(self, test_num, test_vector, draw):
315 test = 'uniform {0} expected {1}\n'.format(
316 shader_runner_type(self.__signature.rettype),
317 shader_runner_format(column_major_values(test_vector.result)))
318 test += 'uniform double tolerance {0}\n'.format(
319 shader_runner_format([test_vector.tolerance]))
320 test += draw
321 test += 'probe rgba {0} 0 0.0 1.0 0.0 1.0\n'.format(test_num)
322 return test
325 class ShaderTest(object):
326 """Class used to build a test of a single built-in. This is an
327 abstract base class--derived types should override test_prefix(),
328 make_vertex_shader(), make_fragment_shader(), and other functions
329 if necessary.
331 __metaclass__ = abc.ABCMeta
333 def __init__(self, signature, test_vectors, use_if):
334 """Prepare to build a test for a single built-in. signature
335 is the signature of the built-in (a key from the
336 builtin_function.test_suite dict), and test_vectors is the
337 list of test vectors for testing the given builtin (the
338 corresponding value from the builtin_function.test_suite
339 dict).
341 If use_if is True, then the generated test checks the result
342 by using it in an if statement--this only works for builtins
343 returning bool.
345 self._signature = signature
346 self._test_vectors = test_vectors
347 if use_if:
348 self._comparator = BoolIfComparator(signature)
349 elif signature.rettype.base_type == glsl_bool:
350 self._comparator = BoolComparator(signature)
351 elif signature.rettype.base_type == glsl_double:
352 self._comparator = FloatComparator(signature)
353 else:
354 raise Exception('Unexpected rettype {0}'.format(signature.rettype))
356 def glsl_version(self):
357 return self._signature.version_introduced
359 def draw_command(self):
360 if self.glsl_version() >= 140:
361 return 'draw arrays GL_TRIANGLE_FAN 0 4\n'
362 else:
363 return 'draw rect -1 -1 2 2\n'
365 def make_additional_requirements(self):
366 """Return a string that should be included in the test's
367 [require] section.
369 if self._signature.extension:
370 return 'GL_{0}\n'.format(self._signature.extension)
371 return ""
373 @abc.abstractmethod
374 def test_prefix(self):
375 """Return the prefix that should be used in the test file name
376 to identify the type of test, e.g. "vs" for a vertex shader
377 test.
380 @abc.abstractmethod
381 def make_vertex_shader(self):
382 """Return the vertex shader for this test."""
384 def make_geometry_shader(self):
385 """Return the geometry shader for this test (or None if this
386 test doesn't require a geometry shader). No need to
387 reimplement this function in classes that don't use geometry
388 shaders.
390 return None
392 def make_geometry_layout(self):
393 """Return the geometry layout for this test (or None if this
394 test doesn't require a geometry layout section). No need to
395 reimplement this function in classes that don't use geometry
396 shaders.
398 return None
400 @abc.abstractmethod
401 def make_fragment_shader(self):
402 """Return the fragment shader for this test."""
404 def make_test_shader(self, additional_declarations, prefix_statements,
405 output_var, suffix_statements):
406 """Generate the shader code necessary to test the built-in.
407 additional_declarations is a string containing any
408 declarations that need to be before the main() function of the
409 shader. prefix_statements is a string containing any
410 additional statements than need to be inside the main()
411 function of the shader, before the built-in function is
412 called. output_var is the variable that the result of the
413 built-in function should be assigned to, after conversion to a
414 vec4. suffix_statements is a string containing any additional
415 statements that need to be inside the main() function of the
416 shader, after the built-in function is called.
418 shader = ''
419 if self._signature.extension:
420 shader += '#extension GL_{0} : require\n'.format(self._signature.extension)
421 shader += additional_declarations
422 for i in range(len(self._signature.argtypes)):
423 shader += 'uniform {0} arg{1};\n'.format(
424 self._signature.argtypes[i], i)
425 shader += self._comparator.make_additional_declarations()
426 shader += '\n'
427 shader += 'void main()\n'
428 shader += '{\n'
429 shader += prefix_statements
430 invocation = self._signature.template.format(
431 *['arg{0}'.format(i)
432 for i in range(len(self._signature.argtypes))])
433 shader += self._comparator.make_result_handler(invocation, output_var)
434 shader += suffix_statements
435 shader += '}\n'
436 return shader
438 def make_test(self):
439 """Make the complete shader_runner test file, and return it as
440 a string.
442 test = ''
443 for test_num, test_vector in enumerate(self._test_vectors):
444 for i in range(len(test_vector.arguments)):
445 test += 'uniform {0} arg{1} {2}\n'.format(
446 shader_runner_type(self._signature.argtypes[i]),
447 i, shader_runner_format(
448 column_major_values(test_vector.arguments[i])))
449 # Note: shader_runner uses a 250x250 window so we must
450 # ensure that test_num <= 250.
451 test += self._comparator.make_result_test(
452 test_num % 250, test_vector, self.draw_command())
453 return test
455 def make_vbo_data(self):
456 # Starting with GLSL 1.40/GL 3.1, we need to use VBOs and
457 # vertex shader input bindings for our vertex data instead of
458 # the piglit drawing utilities and gl_Vertex.
459 if self.glsl_version() < 140:
460 return ""
461 vbo = '[vertex data]\n'
462 vbo += 'vertex/float/2\n'
463 vbo += '-1.0 -1.0\n'
464 vbo += ' 1.0 -1.0\n'
465 vbo += ' 1.0 1.0\n'
466 vbo += '-1.0 1.0\n'
467 vbo += '\n'
468 return vbo
470 def filename(self):
471 argtype_names = '-'.join(
472 str(argtype) for argtype in self._signature.argtypes)
473 if self._signature.extension:
474 subdir = self._signature.extension.lower()
475 else:
476 subdir = 'glsl-{0:1.2f}'.format(float(self.glsl_version()) / 100)
477 return os.path.join(
478 'spec', subdir, 'execution', 'built-in-functions',
479 '{0}-{1}-{2}{3}.shader_test'.format(
480 self.test_prefix(), self._signature.name, argtype_names,
481 self._comparator.testname_suffix()))
483 def generate_shader_test(self):
484 """Generate the test and write it to the output file."""
485 shader_test = '[require]\n'
486 shader_test += 'GLSL >= {0:1.2f}\n'.format(
487 float(self.glsl_version()) / 100)
488 shader_test += self.make_additional_requirements()
489 shader_test += '\n'
490 shader_test += '[vertex shader]\n'
491 shader_test += self.make_vertex_shader()
492 shader_test += '\n'
493 gs = self.make_geometry_shader()
494 if gs:
495 shader_test += '[geometry shader]\n'
496 shader_test += gs
497 shader_test += '\n'
498 gl = self.make_geometry_layout()
499 if gl:
500 shader_test += '[geometry layout]\n'
501 shader_test += gl
502 shader_test += '\n'
503 shader_test += '[fragment shader]\n'
504 shader_test += self.make_fragment_shader()
505 shader_test += '\n'
506 shader_test += self.make_vbo_data()
507 shader_test += '[test]\n'
508 shader_test += 'clear color 0.0 0.0 1.0 0.0\n'
509 shader_test += 'clear\n'
510 shader_test += self.make_test()
511 filename = self.filename()
512 dirname = os.path.dirname(filename)
513 utils.safe_makedirs(dirname)
514 with open(filename, 'w') as f:
515 f.write(shader_test)
518 class VertexShaderTest(ShaderTest):
519 """Derived class for tests that exercise the built-in in a vertex
520 shader.
522 def test_prefix(self):
523 return 'vs'
525 def make_vertex_shader(self):
526 if self.glsl_version() >= 140:
527 return self.make_test_shader(
528 'in vec4 vertex;\n' +
529 'out vec4 color;\n',
530 ' gl_Position = vertex;\n',
531 'color', '')
532 else:
533 return self.make_test_shader(
534 'varying vec4 color;\n',
535 ' gl_Position = gl_Vertex;\n',
536 'color', '')
538 def make_fragment_shader(self):
539 shader = '''varying vec4 color;
541 void main()
543 gl_FragColor = color;
546 return shader
549 class GeometryShaderTest(ShaderTest):
550 """Derived class for tests that exercise the built-in in a
551 geometry shader.
553 def test_prefix(self):
554 return 'gs'
556 def glsl_version(self):
557 return max(150, ShaderTest.glsl_version(self))
559 def make_vertex_shader(self):
560 shader = ''
561 shader += "in vec4 vertex;\n"
562 shader += "out vec4 vertex_to_gs;\n"
564 shader += "void main()\n"
565 shader += "{\n"
566 shader += " vertex_to_gs = vertex;\n"
567 shader += "}\n"
569 return shader
571 def make_geometry_shader(self):
572 additional_declarations = ''
573 additional_declarations += 'layout(triangles) in;\n'
574 additional_declarations \
575 += 'layout(triangle_strip, max_vertices = 3) out;\n'
576 additional_declarations += 'in vec4 vertex_to_gs[3];\n'
577 additional_declarations += 'out vec4 color;\n'
578 return self.make_test_shader(
579 additional_declarations,
580 ' vec4 tmp_color;\n',
581 'tmp_color',
582 ' for (int i = 0; i < 3; i++) {\n'
583 ' gl_Position = vertex_to_gs[i];\n'
584 ' color = tmp_color;\n'
585 ' EmitVertex();\n'
586 ' }\n')
588 def make_fragment_shader(self):
589 shader = '''varying vec4 color;
591 void main()
593 gl_FragColor = color;
596 return shader
599 class FragmentShaderTest(ShaderTest):
600 """Derived class for tests that exercise the built-in in a
601 fragment shader.
603 def test_prefix(self):
604 return 'fs'
606 def make_vertex_shader(self):
607 shader = ""
608 if self.glsl_version() >= 140:
609 shader += "in vec4 vertex;\n"
611 shader += "void main()\n"
612 shader += "{\n"
613 if self.glsl_version() >= 140:
614 shader += " gl_Position = vertex;\n"
615 else:
616 shader += " gl_Position = gl_Vertex;\n"
617 shader += "}\n"
619 return shader
621 def make_fragment_shader(self):
622 return self.make_test_shader('', '', 'gl_FragColor', '')
625 def all_tests():
626 for use_if in [False, True]:
627 for signature, test_vectors in sorted(test_suite.items()):
628 if use_if and signature.rettype != glsl_bool:
629 continue
630 yield VertexShaderTest(signature, test_vectors, use_if)
631 yield GeometryShaderTest(signature, test_vectors, use_if)
632 yield FragmentShaderTest(signature, test_vectors, use_if)
635 def main():
636 desc = 'Generate shader tests that test built-in functions using uniforms'
637 usage = 'usage: %prog [-h] [--names-only]'
638 parser = optparse.OptionParser(description=desc, usage=usage)
639 parser.add_option(
640 '--names-only',
641 dest='names_only',
642 action='store_true',
643 help="Don't output files, just generate a list of filenames to stdout")
644 options, args = parser.parse_args()
645 for test in all_tests():
646 if not options.names_only:
647 test.generate_shader_test()
648 print(test.filename())
651 if __name__ == '__main__':
652 main()