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 # 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:
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
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
,
66 if base_type
is not None:
67 self
.__base
_type
= base_type
69 self
.__base
_type
= self
70 self
.__num
_cols
= num_cols
71 self
.__num
_rows
= num_rows
72 self
.__version
_introduced
= version_introduced
76 """The name of the type, as a string."""
81 """For vectors and matrices, the type of data stored in each
82 element. For scalars, equal to self.
84 return self
.__base
_type
88 """For matrices, the number of columns. For vectors and
91 return self
.__num
_cols
95 """For vectors and matrices, the number of rows. For scalars,
98 return self
.__num
_rows
102 return self
.__num
_cols
== 1 and self
.__num
_rows
== 1
106 return self
.__num
_cols
== 1 and self
.__num
_rows
!= 1
110 return self
.__num
_cols
!= 1
113 def version_introduced(self
):
114 """The earliest version of GLSL that this type appears in (as
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
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
139 return hash('__GLslBuiltinType_{}__'.format(self
.name
))
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
182 # - argtypes is a tuple containing the types of each parameter (as
185 # For example, the function
187 # vec3 step(float edge, vec3 x)
191 # Signature(name='step', template='step({0}, {1})',
192 # version_introduced=110, rettype='vec3',
193 # argtypes=('float', 'vec3'))
194 Signature
= collections
.namedtuple(
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
):
223 elif isinstance(value
, (bool, np
.bool_
)):
226 if len(value
.shape
) == 1:
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]
236 'Unexpected vector base type {0}'.format(value
.dtype
))
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
, -1, 'F'))
260 def glsl_constant(value
):
261 """Given a native numpy value, return GLSL code that constructs
263 column_major
= np
.reshape(np
.array(value
), -1, 'F')
264 if column_major
.dtype
== bool:
265 values
= ['true' if x
else 'false' for x
in column_major
]
267 values
= ['{0}lf'.format(repr(x
)) for x
in column_major
]
268 if len(column_major
) == 1:
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
279 # Note: the dictionary is initialized to {} here, but it is filled
280 # with test vectors by code later in this file.
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.
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.
303 # All other cases are standard linear algebraic
304 # multiplication, which numpy calls "dot".
309 if any(y_element
== 0 for y_element
in column_major_values(y
)):
310 # Division by zero is undefined.
316 if any(x_element
< 0 for x_element
in column_major_values(x
)):
317 # Modulus operation with a negative first operand is
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
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.
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.
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
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.
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.
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
364 return all(column_major_values(x
== y
))
367 def _not_equal(x
, y
):
368 return not _equal(x
, y
)
374 return np
.arctan2(y
, x
)
380 if x
== 0.0 and y
<= 0.0:
382 return np
.power(x
, y
)
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
)
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
):
400 return min(max(x
, minVal
), maxVal
)
403 # Inefficient, but obvious
405 return np
.sort([x
, y
, z
])[1]
407 def _smoothstep(edge0
, edge1
, x
):
410 t
= _clamp((x
-edge0
)/(edge1
-edge0
), 0.0, 1.0)
411 return t
*t
*(3.0-2.0*t
)
415 return x
/np
.linalg
.norm(x
)
418 def _faceforward(N
, I
, Nref
):
419 if np
.dot(Nref
, I
) < 0.0:
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
))
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
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
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
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
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.
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
))
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.
565 for tv
in test_vectors
:
566 key
= tuple(tv
.arguments
[i
] for i
in scalar_arg_indices
)
567 if key
not in groups
:
569 groups
[key
].append(tv
)
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
):
580 for j
in range(partition_size
):
581 partition
.append(test_vectors
[(i
+ j
) % len(test_vectors
)])
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
)
592 for j
in range(arity
):
593 if j
in scalar_arg_indices
:
594 arguments
.append(test_vectors
[0].arguments
[j
])
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
,
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 insted as the template for the
623 Signature objects generated.
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
645 If template is supplied, it is used insted 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
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.
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
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
)
714 test_suite_dict
, name
, 400, None, scalar_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
= [()]
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):
724 test_suite_dict
, name
, 400, None,
725 _vectorize_test_vectors(
726 scalar_test_vectors
, scalar_arg_indices
,
729 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64",
730 _vectorize_test_vectors(
731 scalar_test_vectors
, scalar_arg_indices
,
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
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
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(),
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
,
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
,
826 for vector_length
in (2, 3, 4):
828 test_suite_dict
, name
, 400, None,
829 _vectorize_test_vectors(
830 scalar_test_vectors
, (), vector_length
))
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.
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 """Detemine 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
873 x_type
= glsl_type_of(x
)
874 y_type
= glsl_type_of(y
)
875 if x_type
.base_type
!= y_type
.base_type
:
877 if x_type
.is_scalar
or y_type
.is_scalar
:
879 return x_type
== y_type
881 def match_multiply(x
, y
):
882 """Determine whether the type of the arguments is compatible
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
:
893 if x_type
.is_scalar
or y_type
.is_scalar
:
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
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
):
924 if y_type
.base_type
not in (glsl_int
, glsl_uint
):
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
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
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
]
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],
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],
976 [ 1.03, -0.50]]), # mat2x3
977 np
.array([[ 1.38, -1.08],
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],
987 [-0.02, -1.21]]), # mat2x4
988 np
.array([[-1.02, 0.74],
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
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],
1035 [ 1.03, -0.50]]), # mat2x3
1036 np
.array([[ 1.38, -1.08],
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],
1046 [-0.02, -1.21]]), # mat2x4
1047 np
.array([[-1.02, 0.74],
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
,
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
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 insted as the template for
1090 the Signature objects generated.
1092 test_inputs
= make_arguments(test_inputs
)
1094 if filter is not None:
1096 [arguments
for arguments
in test_inputs
if filter(*arguments
)]
1097 _store_test_vectors(
1098 test_suite_dict
, name
, 400, None,
1100 test_inputs
, python_equivalent
, tolerance_function
),
1102 _store_test_vectors(
1103 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64",
1105 test_inputs
, python_equivalent
, tolerance_function
),
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),
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
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
:
1158 'Duplicate signature found for {0}'.format(name_argtype_combo
))
1159 name_argtype_combos
.add(name_argtype_combo
)
1160 _check_signature_safety(test_suite
)