glsl-1.10: test mesa bug conflict between globals
[piglit.git] / generated_tests / builtin_function_fp64.py
blobdf8e72760174861f159c4b48f4e0d2ddf946d08a
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 # This source file defines a set of test vectors that can be used to
25 # test GLSL's built-in functions and operators. It is intended to be
26 # used by Python code that generates Piglit tests.
28 # The key export is the dictionary test_suite. It contains an entry
29 # for each possible overload of every pure built-in function and
30 # operator. By iterating through this dictionary you can find a set
31 # of test vectors for testing nearly every built-in GLSL function.
33 # The following functions are not included, since they are not pure,
34 # so they can't be tested using simple vectors:
35 # - dFdx()
36 # - dFdy()
37 # - fwidth()
38 # - ftransform()
39 # - Increment and decrement operators
41 # The following functions are not included, since they need to be
42 # tested in specialized ways:
43 # - modf(): not tested because it has an out parameter
44 # - isnan() and isinf(): not tested because special effort is required
45 # to create values that cause these functions to return true.
47 # Also not tested are array subscripting, field/method selection,
48 # swizzling, the function call operator, assignment, and the sequence
49 # operator.
51 import collections
52 import itertools
53 import functools
55 import numpy as np
58 # Floating point types used by Python and numpy
59 DOUBLE_TYPES = (float, np.float64, np.float32)
61 class GlslBuiltinType(object):
62 """Class representing a GLSL built-in type."""
63 def __init__(self, name, base_type, num_cols, num_rows,
64 version_introduced):
65 self.__name = name
66 if base_type is not None:
67 self.__base_type = base_type
68 else:
69 self.__base_type = self
70 self.__num_cols = num_cols
71 self.__num_rows = num_rows
72 self.__version_introduced = version_introduced
74 @property
75 def name(self):
76 """The name of the type, as a string."""
77 return self.__name
79 @property
80 def base_type(self):
81 """For vectors and matrices, the type of data stored in each
82 element. For scalars, equal to self.
83 """
84 return self.__base_type
86 @property
87 def num_cols(self):
88 """For matrices, the number of columns. For vectors and
89 scalars, 1.
90 """
91 return self.__num_cols
93 @property
94 def num_rows(self):
95 """For vectors and matrices, the number of rows. For scalars,
97 """
98 return self.__num_rows
100 @property
101 def is_scalar(self):
102 return self.__num_cols == 1 and self.__num_rows == 1
104 @property
105 def is_vector(self):
106 return self.__num_cols == 1 and self.__num_rows != 1
108 @property
109 def is_matrix(self):
110 return self.__num_cols != 1
112 @property
113 def version_introduced(self):
114 """The earliest version of GLSL that this type appears in (as
115 a string, e.g. 110).
117 return self.__version_introduced
119 def __eq__(self, other):
120 if isinstance(other, GlslBuiltinType):
121 return self.name == other.name
123 return NotImplemented
125 def __lt__(self, other):
126 if isinstance(other, GlslBuiltinType):
127 return self.name < other.name
129 return NotImplemented
131 def __hash__(self):
132 """Hash the object.
134 This hash isn't super awesome, but it isn't prone to change since you
135 have to muck with private (__prefixed values) and some unlikely text in
136 addition.
139 return hash('__GLslBuiltinType_{}__'.format(self.name))
141 def __str__(self):
142 return self.__name
144 def __repr__(self):
145 return 'glsl_{0}'.format(self.__name)
148 # Concrete declarations of GlslBuiltinType
149 glsl_bool = GlslBuiltinType('bool', None, 1, 1, 110)
150 glsl_bvec2 = GlslBuiltinType('bvec2', glsl_bool, 1, 2, 110)
151 glsl_bvec3 = GlslBuiltinType('bvec3', glsl_bool, 1, 3, 110)
152 glsl_bvec4 = GlslBuiltinType('bvec4', glsl_bool, 1, 4, 110)
153 glsl_double = GlslBuiltinType('double', None, 1, 1, 400)
154 glsl_dvec2 = GlslBuiltinType('dvec2', glsl_double, 1, 2, 400)
155 glsl_dvec3 = GlslBuiltinType('dvec3', glsl_double, 1, 3, 400)
156 glsl_dvec4 = GlslBuiltinType('dvec4', glsl_double, 1, 4, 400)
157 glsl_dmat2 = GlslBuiltinType('dmat2', glsl_double, 2, 2, 400)
158 glsl_dmat3 = GlslBuiltinType('dmat3', glsl_double, 3, 3, 400)
159 glsl_dmat4 = GlslBuiltinType('dmat4', glsl_double, 4, 4, 400)
160 glsl_dmat2x2 = glsl_dmat2
161 glsl_dmat3x2 = GlslBuiltinType('dmat3x2', glsl_double, 3, 2, 400)
162 glsl_dmat4x2 = GlslBuiltinType('dmat4x2', glsl_double, 4, 2, 400)
163 glsl_dmat2x3 = GlslBuiltinType('dmat2x3', glsl_double, 2, 3, 400)
164 glsl_dmat3x3 = glsl_dmat3
165 glsl_dmat4x3 = GlslBuiltinType('dmat4x3', glsl_double, 4, 3, 400)
166 glsl_dmat2x4 = GlslBuiltinType('dmat2x4', glsl_double, 2, 4, 400)
167 glsl_dmat3x4 = GlslBuiltinType('dmat3x4', glsl_double, 3, 4, 400)
168 glsl_dmat4x4 = glsl_dmat4
171 # Named tuple representing the signature of a single overload of a
172 # built-in GLSL function or operator:
173 # - name is a name suitable for use in test filenames. For functions,
174 # this is the name of the function. For operators, it is a short
175 # description of the operator, beginning with "op", e.g. "op-plus".
176 # - template is a Python format string that can be used to construct
177 # GLSL code that invokes the function or operator.
178 # - version_introduced earliest version of GLSL the test applies to
179 # (as a string, e.g. 110).
180 # - rettype is the return type of the function or operator (as a
181 # GlslBuiltinType).
182 # - argtypes is a tuple containing the types of each parameter (as
183 # GlslBuiltinTypes).
185 # For example, the function
187 # vec3 step(float edge, vec3 x)
189 # has a signature of
191 # Signature(name='step', template='step({0}, {1})',
192 # version_introduced=110, rettype='vec3',
193 # argtypes=('float', 'vec3'))
194 Signature = collections.namedtuple(
195 'Signature',
196 ('name', 'template', 'version_introduced', 'extension', 'rettype', 'argtypes'))
199 # Named tuple representing a single piece of test data for testing a
200 # built-in GLSL function:
201 # - arguments is a tuple containing the arguments to apply to the
202 # function. Each argument is of a type native to numpy (e.g.
203 # numpy.float32 or numpy.ndarray)
204 # - result is the value the function is expected to return. It is
205 # also of a type native to numpy.
206 # - tolerance is a float64 representing how much deviation from the
207 # result we expect, considering the floating point precision
208 # requirements of GLSL and OpenGL. The value may be zero for test
209 # vectors involving booleans and integers. If result is a vector or
210 # matrix, tolerance should be interpreted as the maximum permissible
211 # RMS error (as would be computed by the distance() function).
212 TestVector = collections.namedtuple(
213 'TestVector', ('arguments', 'result', 'tolerance'))
216 def glsl_type_of(value):
217 """Return the GLSL type corresponding to the given native numpy
218 value, as a GlslBuiltinType.
221 if isinstance(value, DOUBLE_TYPES):
222 return glsl_double
223 elif isinstance(value, (bool, np.bool_)):
224 return glsl_bool
225 else:
226 if len(value.shape) == 1:
227 # Vector
228 vector_length = value.shape[0]
229 assert 2 <= vector_length <= 4
230 if value.dtype in DOUBLE_TYPES:
231 return (glsl_dvec2, glsl_dvec3, glsl_dvec4)[vector_length - 2]
232 elif value.dtype == bool:
233 return (glsl_bvec2, glsl_bvec3, glsl_bvec4)[vector_length - 2]
234 else:
235 raise Exception(
236 'Unexpected vector base type {0}'.format(value.dtype))
237 else:
238 # Matrix
239 assert value.dtype in DOUBLE_TYPES
240 assert len(value.shape) == 2
241 matrix_rows = value.shape[0]
242 assert 2 <= matrix_rows <= 4
243 matrix_columns = value.shape[1]
244 assert 2 <= matrix_columns <= 4
245 matrix_types = ((glsl_dmat2x2, glsl_dmat2x3, glsl_dmat2x4),
246 (glsl_dmat3x2, glsl_dmat3x3, glsl_dmat3x4),
247 (glsl_dmat4x2, glsl_dmat4x3, glsl_dmat4x4))
248 return matrix_types[matrix_columns - 2][matrix_rows - 2]
251 def column_major_values(value):
252 """Given a native numpy value, return a list of the scalar values
253 comprising it, in column-major order."""
254 if isinstance(value, np.ndarray):
255 return list(np.reshape(value, newshape=-1, order='F'))
256 else:
257 return [value]
260 def glsl_constant(value):
261 """Given a native numpy value, return GLSL code that constructs
262 it."""
263 column_major = np.reshape(np.array(value), newshape=-1, order='F')
264 if column_major.dtype == bool:
265 values = ['true' if x else 'false' for x in column_major]
266 else:
267 values = ['{0}lf'.format(repr(x)) for x in column_major]
268 if len(column_major) == 1:
269 return values[0]
270 else:
271 return '{0}({1})'.format(glsl_type_of(value), ', '.join(values))
274 # Dictionary containing the test vectors. Each entry in the
275 # dictionary represents a single overload of a single built-in
276 # function. Its key is a Signature tuple, and its value is a list of
277 # TestVector tuples.
279 # Note: the dictionary is initialized to {} here, but it is filled
280 # with test vectors by code later in this file.
281 test_suite = {}
284 # Implementation
285 # ==============
287 # The functions below shouldn't be necessary to call from outside this
288 # file. They exist solely to populate test_suite with test vectors.
290 # Functions that simulate GLSL built-in functions (in the cases where
291 # the GLSL built-in functions have no python or numpy equivalent, or
292 # in cases where there is a behavioral difference). These functions
293 # return None if the behavior of the GLSL built-in is undefined for
294 # the given set of inputs.
295 def _multiply(x, y):
296 x_type = glsl_type_of(x)
297 y_type = glsl_type_of(y)
299 if x_type.is_vector and y_type.is_vector:
300 # vector * vector is done componentwise.
301 return x * y
302 else:
303 # All other cases are standard linear algebraic
304 # multiplication, which numpy calls "dot".
305 return np.dot(x, y)
308 def _divide(x, y):
309 if any(y_element == 0 for y_element in column_major_values(y)):
310 # Division by zero is undefined.
311 return None
312 return x / y
315 def _modulus(x, y):
316 if any(x_element < 0 for x_element in column_major_values(x)):
317 # Modulus operation with a negative first operand is
318 # undefined.
319 return None
320 if any(y_element <= 0 for y_element in column_major_values(y)):
321 # Modulus operation with a negative or zero second operand is
322 # undefined.
323 return None
324 return x % y
327 def _lshift(x, y):
328 if not all(0 <= y_element < 32 for y_element in column_major_values(y)):
329 # Shifts by less than 0 or more than the number of bits in the
330 # type being shifted are undefined.
331 return None
332 # When the arguments to << don't have the same signedness, numpy
333 # likes to promote them to int64. To avoid this, convert y to be
334 # the same type as x.
335 y_orig = y
336 result = x << y
338 # Shifting should always produce a result with the same base type
339 # as the left argument.
340 assert glsl_type_of(result).base_type == glsl_type_of(x).base_type
342 return result
345 def _rshift(x, y):
346 if not all(0 <= y_element < 32 for y_element in column_major_values(y)):
347 # Shifts by less than 0 or more than the number of bits in the
348 # type being shifted are undefined.
349 return None
350 # When the arguments to >> don't have the same signedness, numpy
351 # likes to promote them to int64. To avoid this, convert y to be
352 # the same type as x.
353 y_orig = y
354 result = x >> y
356 # Shifting should always produce a result with the same base type
357 # as the left argument.
358 assert glsl_type_of(result).base_type == glsl_type_of(x).base_type
360 return result
363 def _equal(x, y):
364 return all(column_major_values(x == y))
367 def _not_equal(x, y):
368 return not _equal(x, y)
371 def _arctan2(y, x):
372 if x == y == 0.0:
373 return None
374 return np.arctan2(y, x)
377 def _pow(x, y):
378 if x < 0.0:
379 return None
380 if x == 0.0 and y <= 0.0:
381 return None
382 return np.power(x, y)
385 def _exp2(x):
386 # exp2() is not available in versions of numpy < 1.3.0 so we
387 # emulate it with power().
388 return np.power(2, x)
391 def _trunc(x):
392 # trunc() rounds toward zero. It is not available in version
393 # 1.2.1 of numpy so we emulate it with floor(), sign(), and abs().
394 return np.sign(x) * np.floor(np.abs(x))
397 def _clamp(x, minVal, maxVal):
398 if minVal > maxVal:
399 return None
400 return min(max(x, minVal), maxVal)
403 # Inefficient, but obvious
404 def _mid3(x, y, z):
405 return np.sort([x, y, z])[1]
407 def _smoothstep(edge0, edge1, x):
408 if edge0 >= edge1:
409 return None
410 t = _clamp((x-edge0)/(edge1-edge0), 0.0, 1.0)
411 return t*t*(3.0-2.0*t)
414 def _normalize(x):
415 return x/np.linalg.norm(x)
418 def _faceforward(N, I, Nref):
419 if np.dot(Nref, I) < 0.0:
420 return N
421 else:
422 return -N
425 def _reflect(I, N):
426 return I-2*np.dot(N, I)*N
429 def _refract(I, N, eta):
430 k = 1.0-eta*eta*(1.0-np.dot(N, I)*np.dot(N, I))
431 if k < 0.0:
432 return I*0.0
433 else:
434 return eta*I-(eta*np.dot(N, I)+np.sqrt(k))*N
437 def _argument_types_match(arguments, argument_indices_to_match):
438 """Return True if all of the arguments indexed by
439 argument_indices_to_match have the same GLSL type.
441 types = [glsl_type_of(arguments[i]) for i in argument_indices_to_match]
442 return all(x == types[0] for x in types)
445 def _strict_tolerance(arguments, result):
446 """Compute tolerance using a strict interpretation of the GLSL and
447 OpenGL standards.
449 From the GLSL 1.20 spec (4.1.4 "Floats"):
451 "As an input value to one of the processing units, a
452 floating-point variable is expected to match the IEEE single
453 precision floating-point definition for precision and dynamic
454 range. It is not required that the precision of internal
455 processing match the IEEE floating-point specification for
456 floating-point operations, but the guidelines for precision
457 established by the OpenGL 1.4 specification must be met."
459 From the OpenGL 1.4 spec (2.1.1 "Floating-Point Computation"):
461 "We require simply that numbers' floating-point parts contain
462 enough bits ... so that individual results of floating-point
463 operations are accurate to about 1 part in 10^5."
465 A harsh interpretation of the above is that (a) no precision is
466 lost in moving numbers into or out of the GPU, and (b) any
467 built-in function constitutes a single operation, so therefore the
468 error in applying any built-in function should be off by no more
469 than 1e-5 times its theoretically correct value.
471 This is not the only possible interpretation, however. Certain
472 built-in functions, such as the cross product, are computed by a
473 formula consisting of many elementary multiplications and
474 additions, in which a large amount of cancellation sometimes
475 occurs. It's possible that these rules are meant to apply to
476 those elementary multiplications and additions, and not the full
477 built-in function. Other built-in functions, such as the trig
478 functions, are typically implemented by a series approximation, in
479 which 1 part in 10^5 accuracy seems like overkill. See below for
480 the tolerance computation we use on these other functions.
482 return 1e-5 * np.linalg.norm(result)
485 def _trig_tolerance(arguments, result):
486 """Compute a more lenient tolerance bound for trig functions.
488 The GLSL and OpenGL specs don't provide any guidance as to the
489 required accuracy of trig functions (other than the "1 part in
490 10^5" general accuracy requirement, which seems like overkill for
491 trig functions.
493 So the tolerance here is rather arbitrarily chosen to be either 1
494 part in 10^3 or 10^-4, whichever is larger.
496 return max(1e-4, 1e-3 * np.linalg.norm(result))
499 def _cross_product_tolerance(arguments, result):
500 """Compute a more lenient tolerance bound for cross product.
502 Since the computation of a cross product may involve a large
503 amount of cancellation, an error tolerance of 1 part in 10^5
504 (referred to the magnitude of the result vector) is overly tight.
506 So instead we allow the error to be 1 part in 10^5 referred to the
507 product of the magnitudes of the arguments.
509 assert len(arguments) == 2
510 return 1e-5 * np.linalg.norm(arguments[0]) * np.linalg.norm(arguments[1])
513 def _simulate_function(test_inputs, python_equivalent, tolerance_function):
514 """Construct test vectors by simulating a GLSL function on a list
515 of possible inputs, and return a list of test vectors.
517 test_inputs is a list of possible input sequences, each of which
518 represents a set of arguments that should be applied to the
519 function.
521 python_equivalent is the function to simulate--it should return
522 None if the GLSL function returns undefined results for the given
523 set of inputs, otherwise it should return the expected result.
524 Input sequences for which python_equivalent returns None are
525 ignored.
527 tolerance_function is the function to call to compute the
528 tolerance. It should take the set of arguments and the expected
529 result as its parameters. It is only used for functions that
530 return floating point values.
532 test_vectors = []
533 for inputs in test_inputs:
534 expected_output = python_equivalent(*inputs)
535 if expected_output is not None:
536 tolerance = tolerance_function(inputs, expected_output)
537 test_vectors.append(TestVector(inputs, expected_output, tolerance))
538 return test_vectors
541 def _vectorize_test_vectors(test_vectors, scalar_arg_indices, vector_length):
542 """Build a new set of test vectors by combining elements of
543 test_vectors into vectors of length vector_length. For example,
544 vectorizing the test vectors
546 [TestVector((10, 20), 30, tolerance), TestVector((11, 20), 31, tolerance)]
548 into vectors of length 2 would produce the result:
550 [TestVector((vec2(10, 11), vec2(20, 20)), vec2(30, 31), new_tolerance)].
552 Tolerances are combined in root-sum-square fashion.
554 scalar_arg_indices is a sequence of argument indices which should
555 not be vectorized. So, if scalar_arg_indices is [1] in the above
556 example, the result would be:
558 [TestVector((vec2(10, 11), 20), vec2(30, 31), new_tolerance)].
560 def make_groups(test_vectors):
561 """Group test vectors according to the values passed to the
562 arguments that should not be vectorized.
564 groups = {}
565 for tv in test_vectors:
566 key = tuple(tv.arguments[i] for i in scalar_arg_indices)
567 if key not in groups:
568 groups[key] = []
569 groups[key].append(tv)
570 return groups
572 def partition_vectors(test_vectors, partition_size):
573 """Partition test_vectors into lists of length partition_size.
574 If partition_size does not evenly divide the number of test
575 vectors, wrap around as necessary to ensure that every input
576 test vector is included.
578 for i in range(0, len(test_vectors), partition_size):
579 partition = []
580 for j in range(partition_size):
581 partition.append(test_vectors[(i + j) % len(test_vectors)])
582 yield partition
584 def merge_vectors(test_vectors):
585 """Merge the given set of test vectors (whose arguments and
586 result are scalars) into a single test vector whose arguments
587 and result are vectors. For argument indices in
588 scalar_arg_indices, leave the argument as a scalar.
590 arity = len(test_vectors[0].arguments)
591 arguments = []
592 for j in range(arity):
593 if j in scalar_arg_indices:
594 arguments.append(test_vectors[0].arguments[j])
595 else:
596 arguments.append(
597 np.array([tv.arguments[j] for tv in test_vectors]))
598 result = np.array([tv.result for tv in test_vectors])
599 tolerance = np.linalg.norm([tv.tolerance for tv in test_vectors])
600 return TestVector(arguments, result, tolerance)
602 vectorized_test_vectors = []
603 groups = make_groups(test_vectors)
604 for key in sorted(groups.keys()):
605 test_vectors = groups[key]
606 vectorized_test_vectors.extend(
607 merge_vectors(partition)
608 for partition in partition_vectors(test_vectors, vector_length))
609 return vectorized_test_vectors
612 def _store_test_vector(test_suite_dict, name, glsl_version, extension, test_vector,
613 template=None):
614 """Store a test vector in the appropriate place in
615 test_suite_dict. The dictionary key (which is a Signature tuple)
616 is generated by consulting the argument and return types of the
617 test vector, and combining them with name and glsl_version.
619 glsl_version is adjusted if necessary to reflect when the argument
620 and return types were introduced into GLSL.
622 If template is supplied, it is used instead as the template for the
623 Signature objects generated.
625 if template is None:
626 arg_indices = range(len(test_vector.arguments))
627 template = '{0}({1})'.format(
628 name, ', '.join('{{{0}}}'.format(i) for i in arg_indices))
629 rettype = glsl_type_of(test_vector.result)
630 argtypes = tuple(glsl_type_of(arg) for arg in test_vector.arguments)
631 adjusted_glsl_version = glsl_version
633 signature = Signature(
634 name, template, adjusted_glsl_version, extension, rettype, argtypes)
635 if signature not in test_suite_dict:
636 test_suite_dict[signature] = []
637 test_suite_dict[signature].append(test_vector)
640 def _store_test_vectors(test_suite_dict, name, glsl_version, extension,
641 test_vectors, template=None):
642 """Store multiple test vectors in the appropriate places in
643 test_suite_dict.
645 If template is supplied, it is used instead as the template for the
646 Signature objects generated.
648 for test_vector in test_vectors:
649 _store_test_vector(test_suite_dict, name, glsl_version, extension,
650 test_vector, template=template)
653 def make_arguments(input_generators):
654 """Construct a list of tuples of input arguments to test.
656 input_generators is a list, the ith element of which is a sequence
657 of values that are suitable for use as the ith argument of the
658 function under test.
660 Output is a list, each element of which is a tuple of arguments to
661 be passed to the function under test. These values are produced
662 by taking the cartesian product of the input sequences.
665 return list(itertools.product(*input_generators))
668 def _make_componentwise_test_vectors(test_suite_dict):
669 """Add test vectors to test_suite_dict for GLSL built-in
670 functions that operate on vectors in componentwise fashion.
671 Examples include sin(), cos(), min(), max(), and clamp().
673 # Make sure atan(x) and atan(x,y) don't misbehave for very large
674 # or very small input values.
675 atan_inputs = [0.0]
676 for exponent in (-10, -1, 0, 1, 10):
677 atan_inputs.append(pow(10.0, exponent))
678 atan_inputs.append(-pow(10.0, exponent))
679 # Make a similar set of inputs for acosh(), except don't use any
680 # values < 1, since acosh() is only defined for x >= 1.
681 acosh_inputs = [1.0 + x for x in atan_inputs if x >= 0]
682 ints = [np.int32(x) for x in [-5, -2, -1, 0, 1, 2, 5]]
683 uints = [np.uint32(x) for x in [0, 1, 2, 5, 34]]
684 bools = [True, False]
686 def f(name, arity, python_equivalent,
687 alternate_scalar_arg_indices, test_inputs,
688 tolerance_function=_strict_tolerance):
690 """Create test vectors for the function with the given name
691 and arity, which was introduced in the given glsl_version.
693 python_equivalent is a Python function which operates on scalars,
694 and simulates the GLSL function. This function should return None
695 in any case where the output of the GLSL function is undefined.
697 If alternate_scalar_arg_indices is not None, also create test
698 vectors for an alternate vectorized version of the function,
699 in which some arguments are scalars.
700 alternate_scalar_arg_indices is a sequence of the indices of
701 the arguments which are scalars.
703 test_inputs is a list, the ith element of which is a list of
704 values that are suitable for use as the ith argument of the
705 function.
707 If tolerance_function is supplied, it is a function which
708 should be used to compute the tolerance for the test vectors.
709 Otherwise, _strict_tolerance is used.
711 scalar_test_vectors = _simulate_function(
712 make_arguments(test_inputs), python_equivalent, tolerance_function)
713 _store_test_vectors(
714 test_suite_dict, name, 400, None, scalar_test_vectors)
715 _store_test_vectors(
716 test_suite_dict, name, 150, "ARB_gpu_shader_fp64", scalar_test_vectors)
717 if alternate_scalar_arg_indices is None:
718 scalar_arg_indices_list = [()]
719 else:
720 scalar_arg_indices_list = [(), alternate_scalar_arg_indices]
721 for scalar_arg_indices in scalar_arg_indices_list:
722 for vector_length in (2, 3, 4):
723 _store_test_vectors(
724 test_suite_dict, name, 400, None,
725 _vectorize_test_vectors(
726 scalar_test_vectors, scalar_arg_indices,
727 vector_length))
728 _store_test_vectors(
729 test_suite_dict, name, 150, "ARB_gpu_shader_fp64",
730 _vectorize_test_vectors(
731 scalar_test_vectors, scalar_arg_indices,
732 vector_length))
734 f('sqrt', 1, np.sqrt, None, [np.linspace(0.0, 2.0, 4)])
735 f('inversesqrt', 1, lambda x: 1.0/np.sqrt(x), None,
736 [np.linspace(0.1, 2.0, 4)])
737 f('abs', 1, np.abs, None, [np.linspace(-1.5, 1.5, 5)])
738 f('sign', 1, np.sign, None, [np.linspace(-1.5, 1.5, 5)])
739 f('floor', 1, np.floor, None, [np.linspace(-2.0, 2.0, 4)])
741 # Note: with trunc we want to test values in which the floating
742 # point exponent is < 0, > 52 or in the middle. Hence, we append
743 # some numbers to cover all possible scenarios. In addition, we
744 # want to check bitsize barriers (> 32, > 64, etc.) in case the
745 # conversion is done with a cast to and from another int based
746 # type.
747 f('trunc', 1, _trunc, None,
748 [np.append(np.linspace(-2.0, 2.0, 8),
749 [0.0, 45027112.98414, -45027112.98414,
750 19584198736.47329, -19584198736.47329,
751 7.51654162319e17, -7.51654162319e17,
752 4.502711234095857e19, -4.5027112340958570e19,
753 8.654196813385455e39, -8.6541968133854550e39,
754 6.54965168513655e83, -6.54965168513655e83])])
756 # Note: the direction of rounding used by round() is not specified
757 # for half-integer values, so we test it over a range that doesn't
758 # include exact half-integer values. roundEven() is required to
759 # round half-integer values to the nearest even integer, so we
760 # test it over a range that does include exact half-integer
761 # values. In both cases, we can use numpy's round() function,
762 # because it rounds half-integer values to even, and all other
763 # values to nearest.
764 f('round', 1, np.round, None, [np.linspace(-2.0, 2.0, 8)])
765 f('roundEven', 1, np.round, None, [np.linspace(-2.0, 2.0, 25)])
767 f('ceil', 1, np.ceil, None, [np.linspace(-2.0, 2.0, 4)])
768 f('fract', 1, lambda x: x-np.floor(x), None,
769 [np.linspace(-2.0, 2.0, 4)])
770 f('mod', 2, lambda x, y: x-y*np.floor(x/y), [1],
771 [np.linspace(-1.9, 1.9, 4), np.linspace(-2.0, 2.0, 4)])
772 f('min', 2, min, [1],
773 [np.linspace(-2.0, 2.0, 4), np.linspace(-2.0, 2.0, 4)])
774 f('max', 2, max, [1],
775 [np.linspace(-2.0, 2.0, 4), np.linspace(-2.0, 2.0, 4)])
776 f('clamp', 3, _clamp, [1, 2], [np.linspace(-2.0, 2.0, 4),
777 np.linspace(-1.5, 1.5, 3), np.linspace(-1.5, 1.5, 3)])
778 f('mix', 3, lambda x, y, a: x*(1-a)+y*a, [2],
779 [np.linspace(-2.0, 2.0, 2), np.linspace(-3.0, 3.0, 2),
780 np.linspace(0.0, 1.0, 4)])
781 f('mix', 3, lambda x, y, a: y if a else x, None,
782 [np.linspace(-2.0, 2.0, 2), np.linspace(-3.0, 3.0, 2), bools])
783 f('step', 2, lambda edge, x: 0.0 if x < edge else 1.0, [0],
784 [np.linspace(-2.0, 2.0, 4), np.linspace(-2.0, 2.0, 4)])
785 f('smoothstep', 3, _smoothstep, [0, 1],
786 [np.linspace(-1.9, 1.9, 4), np.linspace(-1.9, 1.9, 4),
787 np.linspace(-2.0, 2.0, 4)])
789 _make_componentwise_test_vectors(test_suite)
792 def _make_vector_relational_test_vectors(test_suite_dict):
793 """Add test vectors to test_suite_dict for GLSL built-in functions
794 that operate on vectors of floats, ints, or bools, but not on
795 single floats, ints, or bools. Examples include lessThan(),
796 equal(), and not().
798 _default_inputs = {
799 'v': np.linspace(-1.5, 1.5, 4),
800 'b': np.array([False, True])
803 def f(name, arity, python_equivalent, arg_types,
804 tolerance_function=_strict_tolerance,
805 extension=None):
806 """Make test vectors for the function with the given name and
807 arity, which was introduced in the given glsl_version.
809 python_equivalent is a Python function which operates on scalars,
810 and simulates the GLSL function.
812 arg_types is a string containing 'v' if the function supports
813 standard "vec" inputs, 'i' if it supports "ivec" inputs, and 'b'
814 if it supports "bvec" inputs. The output type of the function is
815 assumed to be the same as its input type.
817 If tolerance_function is supplied, it is a function which
818 should be used to compute the tolerance for the test vectors.
819 Otherwise, _strict_tolerance is used.
821 for arg_type in arg_types:
822 test_inputs = [_default_inputs[arg_type]]*arity
823 scalar_test_vectors = _simulate_function(
824 make_arguments(test_inputs), python_equivalent,
825 tolerance_function)
826 for vector_length in (2, 3, 4):
827 _store_test_vectors(
828 test_suite_dict, name, 400, None,
829 _vectorize_test_vectors(
830 scalar_test_vectors, (), vector_length))
831 _store_test_vectors(
832 test_suite_dict, name, 150, "ARB_gpu_shader_fp64",
833 _vectorize_test_vectors(
834 scalar_test_vectors, (), vector_length))
836 f('lessThan', 2, lambda x, y: x < y, 'v')
837 f('lessThanEqual', 2, lambda x, y: x <= y, 'v')
838 f('greaterThan', 2, lambda x, y: x > y, 'v')
839 f('greaterThanEqual', 2, lambda x, y: x >= y, 'v')
840 f('equal', 2, lambda x, y: x == y, 'v')
841 f('notEqual', 2, lambda x, y: x != y, 'v')
843 _make_vector_relational_test_vectors(test_suite)
846 def _make_vector_or_matrix_test_vectors(test_suite_dict):
847 """Add test vectors to test_suite_dict for GLSL built-in functions
848 that operate on vectors/matrices as a whole. Examples include
849 length(), dot(), cross(), normalize(), and refract().
851 def match_args(*indices):
852 """Return a function that determines whether the type of the
853 arguments at the given indices match.
855 For example:
857 match(1, 3)
859 is equivalent to:
861 lambda a, b, c, d: glsl_type_of(b) == glsl_type_of(d)
863 return lambda *args: _argument_types_match(args, indices)
865 def match_simple_binop(x, y):
866 """Determine whether the type of the arguments is compatible
867 for a simple binary operator (such as '+').
869 Arguments are compatible if one is a scalar and the other is a
870 vector/matrix with the same base type, or if they are the same
871 type.
873 x_type = glsl_type_of(x)
874 y_type = glsl_type_of(y)
875 if x_type.base_type != y_type.base_type:
876 return False
877 if x_type.is_scalar or y_type.is_scalar:
878 return True
879 return x_type == y_type
881 def match_multiply(x, y):
882 """Determine whether the type of the arguments is compatible
883 for multiply.
885 Arguments are compatible if they are scalars, vectors, or
886 matrices with the same base type, and the vector/matrix sizes
887 are properly matched.
889 x_type = glsl_type_of(x)
890 y_type = glsl_type_of(y)
891 if x_type.base_type != y_type.base_type:
892 return False
893 if x_type.is_scalar or y_type.is_scalar:
894 return True
895 if x_type.is_vector and y_type.is_matrix:
896 # When multiplying vector * matrix, the vector is
897 # transposed to a row vector. So its row count must match
898 # the row count of the matrix.
899 return x_type.num_rows == y_type.num_rows
900 elif x_type.is_vector:
901 assert y_type.is_vector
902 # When multiplying vector * vector, the multiplication is
903 # done componentwise, so the types must match exactly.
904 return x_type == y_type
905 else:
906 assert x_type.is_matrix
907 # When multiplying matrix * matrix or matrix * vector, a
908 # standard linear algebraic multiply is used, so x's
909 # column count must match y's row count.
910 return x_type.num_cols == y_type.num_rows
912 def match_shift(x, y):
913 """Determine whether the type of the arguments is compatible
914 for shift operations.
916 Arguments are compatible if they are the same length or the
917 first one is a vector and the second is a scalar. Their base
918 types need not be the same, but they both must be integral.
920 x_type = glsl_type_of(x)
921 y_type = glsl_type_of(y)
922 if x_type.base_type not in (glsl_int, glsl_uint):
923 return False
924 if y_type.base_type not in (glsl_int, glsl_uint):
925 return False
926 if y_type.is_scalar:
927 return True
928 assert not x_type.is_matrix
929 assert not y_type.is_matrix
930 return x_type.num_rows == y_type.num_rows
932 nz_doubles = [ -1.333333333333333259, 0.85]
933 doubles = [0.0] + nz_doubles
934 dvecs = [
935 np.array([-0.10, -1.20]),
936 np.array([-0.42, 0.48]),
937 np.array([-1.333333333333333259, -0.85, -0.94]),
938 np.array([1.67, 0.66, 1.87]),
939 np.array([-1.65, 1.33, 1.93, 0.76]),
940 np.array([0.80, -0.15, -0.51, 0.0])
942 nz_doubles_dvecs = nz_doubles + dvecs
943 dvec3s = [
944 np.array([-0.03, -0.85, -0.94]),
945 np.array([ -1.333333333333333259, 0.66, 1.87]),
948 norm_doubles_dvecs = [_normalize(x) for x in nz_doubles_dvecs]
949 squaremats = [
950 np.array([[ 1.60, 0.76],
951 [ 1.53, -1.00]]), # mat2
952 np.array([[-0.13, -0.87],
953 [-1.40, 1.40]]), # mat2
954 np.array([[-1.11, 1.67, -0.41],
955 [ 0.13, 1.09, -0.02],
956 [ 0.56, 0.95, 0.24]]), # mat3
957 np.array([[-1.69, -0.46, -0.18],
958 [-1.09, 1.75, 2.00],
959 [-1.53, -0.70, -1.47]]), # mat3
960 np.array([[-1.00, -0.55, -1.08, 1.79],
961 [ 1.77, 0.62, 0.48, -1.35],
962 [ 0.09, -0.71, -1.39, -1.21],
963 [-0.91, -1.82, -1.43, 0.72]]), # mat4
964 np.array([[ 0.06, 1.31, 1.52, -1.96],
965 [ 1.60, -0.32, 0.51, -1.84],
966 [ 1.25, 0.45, 1.90, -0.72],
967 [-0.16, 0.45, -0.88, 0.39]]), # mat4
969 mats = squaremats + [
970 np.array([[ 0.09, 1.30, 1.25],
971 [-1.19, 0.08, 1.08]]), # mat3x2
972 np.array([[-0.36, -1.08, -0.60],
973 [-0.53, 0.88, -1.79]]), # mat3x2
974 np.array([[-0.46, 1.94],
975 [-0.45, -0.75],
976 [ 1.03, -0.50]]), # mat2x3
977 np.array([[ 1.38, -1.08],
978 [-1.27, 1.83],
979 [ 1.00, -0.74]]), # mat2x3
980 np.array([[ 1.81, -0.87, 0.81, 0.65],
981 [-1.16, -1.52, 0.25, -1.51]]), # mat4x2
982 np.array([[ 1.93, -1.63, 0.29, 1.60],
983 [ 0.49, 0.27, 0.14, 0.94]]), # mat4x2
984 np.array([[ 0.16, -1.69],
985 [-0.80, 0.59],
986 [-1.74, -1.43],
987 [-0.02, -1.21]]), # mat2x4
988 np.array([[-1.02, 0.74],
989 [-1.64, -0.13],
990 [-1.59, 0.47],
991 [ 0.30, 1.13]]), # mat2x4
992 np.array([[-0.27, -1.38, -1.41, -0.12],
993 [-0.17, -0.56, 1.47, 1.86],
994 [-1.85, -1.29, 1.77, 0.01]]), # mat4x3
995 np.array([[-0.47, -0.15, 1.97, -1.05],
996 [-0.20, 0.53, -1.82, -1.41],
997 [-1.39, -0.19, 1.62, 1.58]]), # mat4x3
998 np.array([[ 1.42, -0.86, 0.27],
999 [ 1.80, -1.74, 0.04],
1000 [-1.88, -0.37, 0.43],
1001 [ 1.37, 1.90, 0.71]]), # mat3x4
1002 np.array([[-1.72, 0.09, 0.45],
1003 [-0.31, -1.58, 1.92],
1004 [ 0.14, 0.18, -0.56],
1005 [ 0.40, -0.77, 1.76]]), # mat3x4
1008 dsquaredmats = [
1009 np.array([[ 1.60, 0.76],
1010 [ 1.53, -1.00]]), # mat2
1011 np.array([[-0.13, -0.87],
1012 [-1.40, 1.40]]), # mat2
1013 np.array([[-1.11, 1.67, -0.41],
1014 [ 0.13, 1.09, -0.02],
1015 [ 0.56, 0.95, 0.24]]), # mat3
1016 np.array([[-1.69, -0.46, -0.18],
1017 [-1.09, 1.75, 2.00],
1018 [-1.53, -0.70, -1.47]]), # mat3
1019 np.array([[-1.00, -0.55, -1.08, 1.79],
1020 [ 1.77, 0.62, 0.48, -1.35],
1021 [ 0.09, -0.71, -1.39, -1.21],
1022 [-0.91, -1.82, -1.43, 0.72]]), # mat4
1023 np.array([[ 0.06, 1.31, 1.52, -1.96],
1024 [ 1.60, -0.32, 0.51, -1.84],
1025 [ 1.25, 0.45, 1.90, -0.72],
1026 [-0.16, 0.45, -0.88, 0.39]]), # mat4
1028 dmats = dsquaredmats + [
1029 np.array([[ 0.09, 1.30, 1.25],
1030 [-1.19, 0.08, 1.08]]), # mat3x2
1031 np.array([[-0.36, -1.08, -0.60],
1032 [-0.53, 0.88, -1.79]]), # mat3x2
1033 np.array([[-0.46, 1.94],
1034 [-0.45, -0.75],
1035 [ 1.03, -0.50]]), # mat2x3
1036 np.array([[ 1.38, -1.08],
1037 [-1.27, 1.83],
1038 [ 1.00, -0.74]]), # mat2x3
1039 np.array([[ 1.81, -0.87, 0.81, 0.65],
1040 [-1.16, -1.52, 0.25, -1.51]]), # mat4x2
1041 np.array([[ 1.93, -1.63, 0.29, 1.60],
1042 [ 0.49, 0.27, 0.14, 0.94]]), # mat4x2
1043 np.array([[ 0.16, -1.69],
1044 [-0.80, 0.59],
1045 [-1.74, -1.43],
1046 [-0.02, -1.21]]), # mat2x4
1047 np.array([[-1.02, 0.74],
1048 [-1.64, -0.13],
1049 [-1.59, 0.47],
1050 [ 0.30, 1.13]]), # mat2x4
1051 np.array([[-0.27, -1.38, -1.41, -0.12],
1052 [-0.17, -0.56, 1.47, 1.86],
1053 [-1.85, -1.29, 1.77, 0.01]]), # mat4x3
1054 np.array([[-0.47, -0.15, 1.97, -1.05],
1055 [-0.20, 0.53, -1.82, -1.41],
1056 [-1.39, -0.19, 1.62, 1.58]]), # mat4x3
1057 np.array([[ 1.42, -0.86, 0.27],
1058 [ 1.80, -1.74, 0.04],
1059 [-1.88, -0.37, 0.43],
1060 [ 1.37, 1.90, 0.71]]), # mat3x4
1061 np.array([[-1.72, 0.09, 0.45],
1062 [-0.31, -1.58, 1.92],
1063 [ 0.14, 0.18, -0.56],
1064 [ 0.40, -0.77, 1.76]]), # mat3x4
1066 def f(name, arity, python_equivalent,
1067 filter, test_inputs, tolerance_function=_strict_tolerance,
1068 template=None):
1069 """Make test vectors for the function with the given name and
1070 arity, which was introduced in the given glsl_version.
1072 python_equivalent is a Python function which simulates the GLSL
1073 function. This function should return None in any case where the
1074 output of the GLSL function is undefined. However, it need not
1075 check that the lengths of the input vectors are all the same.
1077 If filter is not None, it will be called with each set of
1078 arguments, and test cases will only be generated if the filter
1079 returns True.
1081 test_inputs is a list, the ith element of which is a list of
1082 vectors and/or scalars that are suitable for use as the ith
1083 argument of the function.
1085 If tolerance_function is supplied, it is a function which
1086 should be used to compute the tolerance for the test vectors.
1087 Otherwise, _strict_tolerance is used.
1089 If template is supplied, it is used instead as the template for
1090 the Signature objects generated.
1092 test_inputs = make_arguments(test_inputs)
1094 if filter is not None:
1095 test_inputs = \
1096 [arguments for arguments in test_inputs if filter(*arguments)]
1097 _store_test_vectors(
1098 test_suite_dict, name, 400, None,
1099 _simulate_function(
1100 test_inputs, python_equivalent, tolerance_function),
1101 template=template)
1102 _store_test_vectors(
1103 test_suite_dict, name, 150, "ARB_gpu_shader_fp64",
1104 _simulate_function(
1105 test_inputs, python_equivalent, tolerance_function),
1106 template=template)
1108 f('op-add', 2, lambda x, y: x + y, match_simple_binop,
1109 [doubles+dvecs+dmats,
1110 doubles+dvecs+dmats],
1111 template='({0} + {1})')
1112 f('op-sub', 2, lambda x, y: x - y, match_simple_binop,
1113 [doubles+dvecs+dmats,
1114 doubles+dvecs+dmats],
1115 template='({0} - {1})')
1116 f('op-mult', 2, _multiply, match_multiply,
1117 [doubles+dvecs+dmats,
1118 doubles+dvecs+dmats],
1119 template='({0} * {1})')
1120 f('op-div', 2, _divide, match_simple_binop,
1121 [doubles+dvecs+dmats,
1122 doubles+dvecs+dmats],
1123 template='({0} / {1})')
1124 f('length', 1, np.linalg.norm, None, [doubles+dvecs])
1125 f('distance', 2, lambda x, y: np.linalg.norm(x-y), match_args(0, 1),
1126 [doubles+dvecs, doubles+dvecs])
1127 f('dot', 2, np.dot, match_args(0, 1), [doubles+dvecs, doubles+dvecs])
1128 f('cross', 2, np.cross, match_args(0, 1), [dvec3s, dvec3s],
1129 _cross_product_tolerance)
1130 f('normalize', 1, _normalize, None, [nz_doubles_dvecs])
1131 f('faceforward', 3, _faceforward, match_args(0, 1, 2),
1132 [doubles+dvecs, doubles+dvecs, doubles+dvecs])
1133 f('reflect', 2, _reflect, match_args(0, 1),
1134 [doubles+dvecs, norm_doubles_dvecs])
1135 f('refract', 3, _refract, match_args(0, 1),
1136 [norm_doubles_dvecs, norm_doubles_dvecs, [0.5, 2.0]])
1137 f('matrixCompMult', 2, lambda x, y: x*y, match_args(0, 1),
1138 [dmats, dmats])
1139 f('outerProduct', 2, np.outer, None, [dvecs, dvecs])
1140 f('transpose', 1, np.transpose, None, [dmats])
1142 f('inverse', 1, np.linalg.inv, None, [dsquaredmats])
1144 f('determinant', 1, np.linalg.det, None, [dsquaredmats])
1145 _make_vector_or_matrix_test_vectors(test_suite)
1148 def _check_signature_safety(test_suite_dict):
1149 """As a final safety check, verify that for each possible
1150 combination of name and argtypes, there is exactly one
1151 signature.
1153 name_argtype_combos = set()
1154 for signature in test_suite_dict:
1155 name_argtype_combo = (signature.name, signature.argtypes, signature.extension)
1156 if name_argtype_combo in name_argtype_combos:
1157 raise Exception(
1158 'Duplicate signature found for {0}'.format(name_argtype_combo))
1159 name_argtype_combos.add(name_argtype_combo)
1160 _check_signature_safety(test_suite)