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
51 from __future__
import print_function
, division
, absolute_import
56 from six
.moves
import range
60 # Floating point types used by Python and numpy
61 DOUBLE_TYPES
= (float, np
.float64
, np
.float32
)
63 class GlslBuiltinType(object):
64 """Class representing a GLSL built-in type."""
65 def __init__(self
, name
, base_type
, num_cols
, num_rows
,
68 if base_type
is not None:
69 self
.__base
_type
= base_type
71 self
.__base
_type
= self
72 self
.__num
_cols
= num_cols
73 self
.__num
_rows
= num_rows
74 self
.__version
_introduced
= version_introduced
78 """The name of the type, as a string."""
83 """For vectors and matrices, the type of data stored in each
84 element. For scalars, equal to self.
86 return self
.__base
_type
90 """For matrices, the number of columns. For vectors and
93 return self
.__num
_cols
97 """For vectors and matrices, the number of rows. For scalars,
100 return self
.__num
_rows
104 return self
.__num
_cols
== 1 and self
.__num
_rows
== 1
108 return self
.__num
_cols
== 1 and self
.__num
_rows
!= 1
112 return self
.__num
_cols
!= 1
115 def version_introduced(self
):
116 """The earliest version of GLSL that this type appears in (as
119 return self
.__version
_introduced
121 def __eq__(self
, other
):
122 if isinstance(other
, GlslBuiltinType
):
123 return self
.name
== other
.name
125 return NotImplemented
127 def __lt__(self
, other
):
128 if isinstance(other
, GlslBuiltinType
):
129 return self
.name
< other
.name
131 return NotImplemented
136 This hash isn't super awesome, but it isn't prone to change since you
137 have to muck with private (__prefixed values) and some unlikely text in
141 return hash('__GLslBuiltinType_{}__'.format(self
.name
))
147 return 'glsl_{0}'.format(self
.__name
)
150 # Concrete declarations of GlslBuiltinType
151 glsl_bool
= GlslBuiltinType('bool', None, 1, 1, 110)
152 glsl_bvec2
= GlslBuiltinType('bvec2', glsl_bool
, 1, 2, 110)
153 glsl_bvec3
= GlslBuiltinType('bvec3', glsl_bool
, 1, 3, 110)
154 glsl_bvec4
= GlslBuiltinType('bvec4', glsl_bool
, 1, 4, 110)
155 glsl_double
= GlslBuiltinType('double', None, 1, 1, 400)
156 glsl_dvec2
= GlslBuiltinType('dvec2', glsl_double
, 1, 2, 400)
157 glsl_dvec3
= GlslBuiltinType('dvec3', glsl_double
, 1, 3, 400)
158 glsl_dvec4
= GlslBuiltinType('dvec4', glsl_double
, 1, 4, 400)
159 glsl_dmat2
= GlslBuiltinType('dmat2', glsl_double
, 2, 2, 400)
160 glsl_dmat3
= GlslBuiltinType('dmat3', glsl_double
, 3, 3, 400)
161 glsl_dmat4
= GlslBuiltinType('dmat4', glsl_double
, 4, 4, 400)
162 glsl_dmat2x2
= glsl_dmat2
163 glsl_dmat3x2
= GlslBuiltinType('dmat3x2', glsl_double
, 3, 2, 400)
164 glsl_dmat4x2
= GlslBuiltinType('dmat4x2', glsl_double
, 4, 2, 400)
165 glsl_dmat2x3
= GlslBuiltinType('dmat2x3', glsl_double
, 2, 3, 400)
166 glsl_dmat3x3
= glsl_dmat3
167 glsl_dmat4x3
= GlslBuiltinType('dmat4x3', glsl_double
, 4, 3, 400)
168 glsl_dmat2x4
= GlslBuiltinType('dmat2x4', glsl_double
, 2, 4, 400)
169 glsl_dmat3x4
= GlslBuiltinType('dmat3x4', glsl_double
, 3, 4, 400)
170 glsl_dmat4x4
= glsl_dmat4
173 # Named tuple representing the signature of a single overload of a
174 # built-in GLSL function or operator:
175 # - name is a name suitable for use in test filenames. For functions,
176 # this is the name of the function. For operators, it is a short
177 # description of the operator, beginning with "op", e.g. "op-plus".
178 # - template is a Python format string that can be used to construct
179 # GLSL code that invokes the function or operator.
180 # - version_introduced earliest version of GLSL the test applies to
181 # (as a string, e.g. 110).
182 # - rettype is the return type of the function or operator (as a
184 # - argtypes is a tuple containing the types of each parameter (as
187 # For example, the function
189 # vec3 step(float edge, vec3 x)
193 # Signature(name='step', template='step({0}, {1})',
194 # version_introduced=110, rettype='vec3',
195 # argtypes=('float', 'vec3'))
196 Signature
= collections
.namedtuple(
198 ('name', 'template', 'version_introduced', 'extension', 'rettype', 'argtypes'))
201 # Named tuple representing a single piece of test data for testing a
202 # built-in GLSL function:
203 # - arguments is a tuple containing the arguments to apply to the
204 # function. Each argument is of a type native to numpy (e.g.
205 # numpy.float32 or numpy.ndarray)
206 # - result is the value the function is expected to return. It is
207 # also of a type native to numpy.
208 # - tolerance is a float64 representing how much deviation from the
209 # result we expect, considering the floating point precision
210 # requirements of GLSL and OpenGL. The value may be zero for test
211 # vectors involving booleans and integers. If result is a vector or
212 # matrix, tolerance should be interpreted as the maximum permissible
213 # RMS error (as would be computed by the distance() function).
214 TestVector
= collections
.namedtuple(
215 'TestVector', ('arguments', 'result', 'tolerance'))
218 def glsl_type_of(value
):
219 """Return the GLSL type corresponding to the given native numpy
220 value, as a GlslBuiltinType.
223 if isinstance(value
, DOUBLE_TYPES
):
225 elif isinstance(value
, (bool, np
.bool_
)):
228 if len(value
.shape
) == 1:
230 vector_length
= value
.shape
[0]
231 assert 2 <= vector_length
<= 4
232 if value
.dtype
in DOUBLE_TYPES
:
233 return (glsl_dvec2
, glsl_dvec3
, glsl_dvec4
)[vector_length
- 2]
234 elif value
.dtype
== bool:
235 return (glsl_bvec2
, glsl_bvec3
, glsl_bvec4
)[vector_length
- 2]
238 'Unexpected vector base type {0}'.format(value
.dtype
))
241 assert value
.dtype
in DOUBLE_TYPES
242 assert len(value
.shape
) == 2
243 matrix_rows
= value
.shape
[0]
244 assert 2 <= matrix_rows
<= 4
245 matrix_columns
= value
.shape
[1]
246 assert 2 <= matrix_columns
<= 4
247 matrix_types
= ((glsl_dmat2x2
, glsl_dmat2x3
, glsl_dmat2x4
),
248 (glsl_dmat3x2
, glsl_dmat3x3
, glsl_dmat3x4
),
249 (glsl_dmat4x2
, glsl_dmat4x3
, glsl_dmat4x4
))
250 return matrix_types
[matrix_columns
- 2][matrix_rows
- 2]
253 def column_major_values(value
):
254 """Given a native numpy value, return a list of the scalar values
255 comprising it, in column-major order."""
256 if isinstance(value
, np
.ndarray
):
257 return list(np
.reshape(value
, -1, 'F'))
262 def glsl_constant(value
):
263 """Given a native numpy value, return GLSL code that constructs
265 column_major
= np
.reshape(np
.array(value
), -1, 'F')
266 if column_major
.dtype
== bool:
267 values
= ['true' if x
else 'false' for x
in column_major
]
269 values
= ['{0}lf'.format(repr(x
)) for x
in column_major
]
270 if len(column_major
) == 1:
273 return '{0}({1})'.format(glsl_type_of(value
), ', '.join(values
))
276 def round_to_32_bits(value
):
277 """If value is a floating point type, round it down to 32 bits.
278 Otherwise return it unchanged.
280 if isinstance(value
, float):
281 return np
.float32(value
)
282 elif isinstance(value
, np
.ndarray
) and value
.dtype
== np
.float64
:
283 return np
.array(value
, dtype
=np
.float32
)
288 def extend_to_64_bits(value
):
289 """If value is a floating point type, extend it to 64 bits.
290 Otherwise return it unchanged.
292 if isinstance(value
, np
.float32
):
293 return np
.float64(value
)
294 elif isinstance(value
, np
.ndarray
) and value
.dtype
== np
.float32
:
295 return np
.array(value
, dtype
=np
.float64
)
300 # Dictionary containing the test vectors. Each entry in the
301 # dictionary represents a single overload of a single built-in
302 # function. Its key is a Signature tuple, and its value is a list of
305 # Note: the dictionary is initialized to {} here, but it is filled
306 # with test vectors by code later in this file.
313 # The functions below shouldn't be necessary to call from outside this
314 # file. They exist solely to populate test_suite with test vectors.
316 # Functions that simulate GLSL built-in functions (in the cases where
317 # the GLSL built-in functions have no python or numpy equivalent, or
318 # in cases where there is a behavioral difference). These functions
319 # return None if the behavior of the GLSL built-in is undefined for
320 # the given set of inputs.
322 x_type
= glsl_type_of(x
)
323 y_type
= glsl_type_of(y
)
325 if x_type
.is_vector
and y_type
.is_vector
:
326 # vector * vector is done componentwise.
329 # All other cases are standard linear algebraic
330 # multiplication, which numpy calls "dot".
335 if any(y_element
== 0 for y_element
in column_major_values(y
)):
336 # Division by zero is undefined.
342 if any(x_element
< 0 for x_element
in column_major_values(x
)):
343 # Modulus operation with a negative first operand is
346 if any(y_element
<= 0 for y_element
in column_major_values(y
)):
347 # Modulus operation with a negative or zero second operand is
354 if not all(0 <= y_element
< 32 for y_element
in column_major_values(y
)):
355 # Shifts by less than 0 or more than the number of bits in the
356 # type being shifted are undefined.
358 # When the arguments to << don't have the same signedness, numpy
359 # likes to promote them to int64. To avoid this, convert y to be
360 # the same type as x.
364 # Shifting should always produce a result with the same base type
365 # as the left argument.
366 assert glsl_type_of(result
).base_type
== glsl_type_of(x
).base_type
372 if not all(0 <= y_element
< 32 for y_element
in column_major_values(y
)):
373 # Shifts by less than 0 or more than the number of bits in the
374 # type being shifted are undefined.
376 # When the arguments to >> don't have the same signedness, numpy
377 # likes to promote them to int64. To avoid this, convert y to be
378 # the same type as x.
382 # Shifting should always produce a result with the same base type
383 # as the left argument.
384 assert glsl_type_of(result
).base_type
== glsl_type_of(x
).base_type
390 return all(column_major_values(x
== y
))
393 def _not_equal(x
, y
):
394 return not _equal(x
, y
)
400 return np
.arctan2(y
, x
)
406 if x
== 0.0 and y
<= 0.0:
408 return np
.power(x
, y
)
412 # exp2() is not available in versions of numpy < 1.3.0 so we
413 # emulate it with power().
414 return np
.power(2, x
)
418 # trunc() rounds toward zero. It is not available in version
419 # 1.2.1 of numpy so we emulate it with floor(), sign(), and abs().
420 return np
.sign(x
) * np
.floor(np
.abs(x
))
423 def _clamp(x
, minVal
, maxVal
):
426 return min(max(x
, minVal
), maxVal
)
429 # Inefficient, but obvious
431 return np
.sort([x
, y
, z
])[1]
433 def _smoothstep(edge0
, edge1
, x
):
436 t
= _clamp((x
-edge0
)/(edge1
-edge0
), 0.0, 1.0)
437 return t
*t
*(3.0-2.0*t
)
441 return x
/np
.linalg
.norm(x
)
444 def _faceforward(N
, I
, Nref
):
445 if np
.dot(Nref
, I
) < 0.0:
452 return I
-2*np
.dot(N
, I
)*N
455 def _refract(I
, N
, eta
):
456 k
= 1.0-eta
*eta
*(1.0-np
.dot(N
, I
)*np
.dot(N
, I
))
460 return eta
*I
-(eta
*np
.dot(N
, I
)+np
.sqrt(k
))*N
463 def _argument_types_match(arguments
, argument_indices_to_match
):
464 """Return True if all of the arguments indexed by
465 argument_indices_to_match have the same GLSL type.
467 types
= [glsl_type_of(arguments
[i
]) for i
in argument_indices_to_match
]
468 return all(x
== types
[0] for x
in types
)
471 def _strict_tolerance(arguments
, result
):
472 """Compute tolerance using a strict interpretation of the GLSL and
475 From the GLSL 1.20 spec (4.1.4 "Floats"):
477 "As an input value to one of the processing units, a
478 floating-point variable is expected to match the IEEE single
479 precision floating-point definition for precision and dynamic
480 range. It is not required that the precision of internal
481 processing match the IEEE floating-point specification for
482 floating-point operations, but the guidelines for precision
483 established by the OpenGL 1.4 specification must be met."
485 From the OpenGL 1.4 spec (2.1.1 "Floating-Point Computation"):
487 "We require simply that numbers' floating-point parts contain
488 enough bits ... so that individual results of floating-point
489 operations are accurate to about 1 part in 10^5."
491 A harsh interpretation of the above is that (a) no precision is
492 lost in moving numbers into or out of the GPU, and (b) any
493 built-in function constitutes a single operation, so therefore the
494 error in applying any built-in function should be off by no more
495 than 1e-5 times its theoretically correct value.
497 This is not the only possible interpretation, however. Certain
498 built-in functions, such as the cross product, are computed by a
499 formula consisting of many elementary multiplications and
500 additions, in which a large amount of cancellation sometimes
501 occurs. It's possible that these rules are meant to apply to
502 those elementary multiplications and additions, and not the full
503 built-in function. Other built-in functions, such as the trig
504 functions, are typically implemented by a series approximation, in
505 which 1 part in 10^5 accuracy seems like overkill. See below for
506 the tolerance computation we use on these other functions.
508 return 1e-5 * np
.linalg
.norm(result
)
511 def _trig_tolerance(arguments
, result
):
512 """Compute a more lenient tolerance bound for trig functions.
514 The GLSL and OpenGL specs don't provide any guidance as to the
515 required accuracy of trig functions (other than the "1 part in
516 10^5" general accuracy requirement, which seems like overkill for
519 So the tolerance here is rather arbitrarily chosen to be either 1
520 part in 10^3 or 10^-4, whichever is larger.
522 return max(1e-4, 1e-3 * np
.linalg
.norm(result
))
525 def _cross_product_tolerance(arguments
, result
):
526 """Compute a more lenient tolerance bound for cross product.
528 Since the computation of a cross product may involve a large
529 amount of cancellation, an error tolerance of 1 part in 10^5
530 (referred to the magnitude of the result vector) is overly tight.
532 So instead we allow the error to be 1 part in 10^5 referred to the
533 product of the magnitudes of the arguments.
535 assert len(arguments
) == 2
536 return 1e-5 * np
.linalg
.norm(arguments
[0]) * np
.linalg
.norm(arguments
[1])
539 def _simulate_function(test_inputs
, python_equivalent
, tolerance_function
):
540 """Construct test vectors by simulating a GLSL function on a list
541 of possible inputs, and return a list of test vectors.
543 test_inputs is a list of possible input sequences, each of which
544 represents a set of arguments that should be applied to the
547 python_equivalent is the function to simulate--it should return
548 None if the GLSL function returns undefined results for the given
549 set of inputs, otherwise it should return the expected result.
550 Input sequences for which python_equivalent returns None are
553 The function is simulated using 64 bit floats for maximum possible
554 accuracy, but the output is rounded to 32 bits since that is the
555 data type that we expect to get back form OpenGL.
557 tolerance_function is the function to call to compute the
558 tolerance. It should take the set of arguments and the expected
559 result as its parameters. It is only used for functions that
560 return floating point values.
563 for inputs
in test_inputs
:
564 expected_output
= python_equivalent(*[extend_to_64_bits(x
) for x
in inputs
])
565 if expected_output
is not None:
566 tolerance
= np
.float64(
567 tolerance_function(inputs
, expected_output
))
568 test_vectors
.append(TestVector(inputs
, expected_output
, tolerance
))
572 def _vectorize_test_vectors(test_vectors
, scalar_arg_indices
, vector_length
):
573 """Build a new set of test vectors by combining elements of
574 test_vectors into vectors of length vector_length. For example,
575 vectorizing the test vectors
577 [TestVector((10, 20), 30, tolerance), TestVector((11, 20), 31, tolerance)]
579 into vectors of length 2 would produce the result:
581 [TestVector((vec2(10, 11), vec2(20, 20)), vec2(30, 31), new_tolerance)].
583 Tolerances are combined in root-sum-square fashion.
585 scalar_arg_indices is a sequence of argument indices which should
586 not be vectorized. So, if scalar_arg_indices is [1] in the above
587 example, the result would be:
589 [TestVector((vec2(10, 11), 20), vec2(30, 31), new_tolerance)].
591 def make_groups(test_vectors
):
592 """Group test vectors according to the values passed to the
593 arguments that should not be vectorized.
596 for tv
in test_vectors
:
597 key
= tuple(tv
.arguments
[i
] for i
in scalar_arg_indices
)
598 if key
not in groups
:
600 groups
[key
].append(tv
)
603 def partition_vectors(test_vectors
, partition_size
):
604 """Partition test_vectors into lists of length partition_size.
605 If partition_size does not evenly divide the number of test
606 vectors, wrap around as necessary to ensure that every input
607 test vector is included.
609 for i
in range(0, len(test_vectors
), partition_size
):
611 for j
in range(partition_size
):
612 partition
.append(test_vectors
[(i
+ j
) % len(test_vectors
)])
615 def merge_vectors(test_vectors
):
616 """Merge the given set of test vectors (whose arguments and
617 result are scalars) into a single test vector whose arguments
618 and result are vectors. For argument indices in
619 scalar_arg_indices, leave the argument as a scalar.
621 arity
= len(test_vectors
[0].arguments
)
623 for j
in range(arity
):
624 if j
in scalar_arg_indices
:
625 arguments
.append(test_vectors
[0].arguments
[j
])
628 np
.array([tv
.arguments
[j
] for tv
in test_vectors
]))
629 result
= np
.array([tv
.result
for tv
in test_vectors
])
630 tolerance
= np
.float64(
631 np
.linalg
.norm([tv
.tolerance
for tv
in test_vectors
]))
632 return TestVector(arguments
, result
, tolerance
)
633 vectorized_test_vectors
= []
634 groups
= make_groups(test_vectors
)
635 for key
in sorted(groups
.keys()):
636 test_vectors
= groups
[key
]
637 vectorized_test_vectors
.extend(
638 merge_vectors(partition
)
639 for partition
in partition_vectors(test_vectors
, vector_length
))
640 return vectorized_test_vectors
643 def _store_test_vector(test_suite_dict
, name
, glsl_version
, extension
, test_vector
,
645 """Store a test vector in the appropriate place in
646 test_suite_dict. The dictionary key (which is a Signature tuple)
647 is generated by consulting the argument and return types of the
648 test vector, and combining them with name and glsl_version.
650 glsl_version is adjusted if necessary to reflect when the argument
651 and return types were introduced into GLSL.
653 If template is supplied, it is used insted as the template for the
654 Signature objects generated.
657 arg_indices
= range(len(test_vector
.arguments
))
658 template
= '{0}({1})'.format(
659 name
, ', '.join('{{{0}}}'.format(i
) for i
in arg_indices
))
660 rettype
= glsl_type_of(test_vector
.result
)
661 argtypes
= tuple(glsl_type_of(arg
) for arg
in test_vector
.arguments
)
662 adjusted_glsl_version
= glsl_version
664 signature
= Signature(
665 name
, template
, adjusted_glsl_version
, extension
, rettype
, argtypes
)
666 if signature
not in test_suite_dict
:
667 test_suite_dict
[signature
] = []
668 test_suite_dict
[signature
].append(test_vector
)
671 def _store_test_vectors(test_suite_dict
, name
, glsl_version
, extension
,
672 test_vectors
, template
=None):
673 """Store multiple test vectors in the appropriate places in
676 If template is supplied, it is used insted as the template for the
677 Signature objects generated.
679 for test_vector
in test_vectors
:
680 _store_test_vector(test_suite_dict
, name
, glsl_version
, extension
,
681 test_vector
, template
=template
)
684 def make_arguments(input_generators
):
685 """Construct a list of tuples of input arguments to test.
687 input_generators is a list, the ith element of which is a sequence
688 of values that are suitable for use as the ith argument of the
691 Output is a list, each element of which is a tuple of arguments to
692 be passed to the function under test. These values are produced
693 by taking the cartesian product of the input sequences.
696 return list(itertools
.product(*input_generators
))
699 def _make_componentwise_test_vectors(test_suite_dict
):
700 """Add test vectors to test_suite_dict for GLSL built-in
701 functions that operate on vectors in componentwise fashion.
702 Examples include sin(), cos(), min(), max(), and clamp().
704 # Make sure atan(x) and atan(x,y) don't misbehave for very large
705 # or very small input values.
707 for exponent
in (-10, -1, 0, 1, 10):
708 atan_inputs
.append(pow(10.0, exponent
))
709 atan_inputs
.append(-pow(10.0, exponent
))
710 # Make a similar set of inputs for acosh(), except don't use any
711 # values < 1, since acosh() is only defined for x >= 1.
712 acosh_inputs
= [1.0 + x
for x
in atan_inputs
if x
>= 0]
713 ints
= [np
.int32(x
) for x
in [-5, -2, -1, 0, 1, 2, 5]]
714 uints
= [np
.uint32(x
) for x
in [0, 1, 2, 5, 34]]
715 bools
= [True, False]
717 def f(name
, arity
, python_equivalent
,
718 alternate_scalar_arg_indices
, test_inputs
,
719 tolerance_function
=_strict_tolerance
):
721 """Create test vectors for the function with the given name
722 and arity, which was introduced in the given glsl_version.
724 python_equivalent is a Python function which operates on scalars,
725 and simulates the GLSL function. This function should return None
726 in any case where the output of the GLSL function is undefined.
728 If alternate_scalar_arg_indices is not None, also create test
729 vectors for an alternate vectorized version of the function,
730 in which some arguments are scalars.
731 alternate_scalar_arg_indices is a sequence of the indices of
732 the arguments which are scalars.
734 test_inputs is a list, the ith element of which is a list of
735 values that are suitable for use as the ith argument of the
738 If tolerance_function is supplied, it is a function which
739 should be used to compute the tolerance for the test vectors.
740 Otherwise, _strict_tolerance is used.
742 scalar_test_vectors
= _simulate_function(
743 make_arguments(test_inputs
), python_equivalent
, tolerance_function
)
745 test_suite_dict
, name
, 400, None, scalar_test_vectors
)
747 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64", scalar_test_vectors
)
748 if alternate_scalar_arg_indices
is None:
749 scalar_arg_indices_list
= [()]
751 scalar_arg_indices_list
= [(), alternate_scalar_arg_indices
]
752 for scalar_arg_indices
in scalar_arg_indices_list
:
753 for vector_length
in (2, 3, 4):
755 test_suite_dict
, name
, 400, None,
756 _vectorize_test_vectors(
757 scalar_test_vectors
, scalar_arg_indices
,
760 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64",
761 _vectorize_test_vectors(
762 scalar_test_vectors
, scalar_arg_indices
,
765 f('sqrt', 1, np
.sqrt
, None, [np
.linspace(0.0, 2.0, 4)])
766 f('inversesqrt', 1, lambda x
: 1.0/np
.sqrt(x
), None,
767 [np
.linspace(0.1, 2.0, 4)])
768 f('abs', 1, np
.abs, None, [np
.linspace(-1.5, 1.5, 5)])
769 f('sign', 1, np
.sign
, None, [np
.linspace(-1.5, 1.5, 5)])
770 f('floor', 1, np
.floor
, None, [np
.linspace(-2.0, 2.0, 4)])
771 f('trunc', 1, _trunc
, None, [np
.linspace(-2.0, 2.0, 8)])
773 # Note: the direction of rounding used by round() is not specified
774 # for half-integer values, so we test it over a range that doesn't
775 # include exact half-integer values. roundEven() is required to
776 # round half-integer values to the nearest even integer, so we
777 # test it over a range that does include exact half-integer
778 # values. In both cases, we can use numpy's round() function,
779 # because it rounds half-integer values to even, and all other
781 f('round', 1, np
.round, None, [np
.linspace(-2.0, 2.0, 8)])
782 f('roundEven', 1, np
.round, None, [np
.linspace(-2.0, 2.0, 25)])
784 f('ceil', 1, np
.ceil
, None, [np
.linspace(-2.0, 2.0, 4)])
785 f('fract', 1, lambda x
: x
-np
.floor(x
), None,
786 [np
.linspace(-2.0, 2.0, 4)])
787 f('mod', 2, lambda x
, y
: x
-y
*np
.floor(x
/y
), [1],
788 [np
.linspace(-1.9, 1.9, 4), np
.linspace(-2.0, 2.0, 4)])
789 f('min', 2, min, [1],
790 [np
.linspace(-2.0, 2.0, 4), np
.linspace(-2.0, 2.0, 4)])
791 f('max', 2, max, [1],
792 [np
.linspace(-2.0, 2.0, 4), np
.linspace(-2.0, 2.0, 4)])
793 f('clamp', 3, _clamp
, [1, 2], [np
.linspace(-2.0, 2.0, 4),
794 np
.linspace(-1.5, 1.5, 3), np
.linspace(-1.5, 1.5, 3)])
795 f('mix', 3, lambda x
, y
, a
: x
*(1-a
)+y
*a
, [2],
796 [np
.linspace(-2.0, 2.0, 2), np
.linspace(-3.0, 3.0, 2),
797 np
.linspace(0.0, 1.0, 4)])
798 f('mix', 3, lambda x
, y
, a
: y
if a
else x
, None,
799 [np
.linspace(-2.0, 2.0, 2), np
.linspace(-3.0, 3.0, 2), bools
])
800 f('step', 2, lambda edge
, x
: 0.0 if x
< edge
else 1.0, [0],
801 [np
.linspace(-2.0, 2.0, 4), np
.linspace(-2.0, 2.0, 4)])
802 f('smoothstep', 3, _smoothstep
, [0, 1],
803 [np
.linspace(-1.9, 1.9, 4), np
.linspace(-1.9, 1.9, 4),
804 np
.linspace(-2.0, 2.0, 4)])
806 _make_componentwise_test_vectors(test_suite
)
809 def _make_vector_relational_test_vectors(test_suite_dict
):
810 """Add test vectors to test_suite_dict for GLSL built-in functions
811 that operate on vectors of floats, ints, or bools, but not on
812 single floats, ints, or bools. Examples include lessThan(),
816 'v': np
.linspace(-1.5, 1.5, 4),
817 'b': np
.array([False, True])
820 def f(name
, arity
, python_equivalent
, arg_types
,
821 tolerance_function
=_strict_tolerance
,
823 """Make test vectors for the function with the given name and
824 arity, which was introduced in the given glsl_version.
826 python_equivalent is a Python function which operates on scalars,
827 and simulates the GLSL function.
829 arg_types is a string containing 'v' if the function supports
830 standard "vec" inputs, 'i' if it supports "ivec" inputs, and 'b'
831 if it supports "bvec" inputs. The output type of the function is
832 assumed to be the same as its input type.
834 If tolerance_function is supplied, it is a function which
835 should be used to compute the tolerance for the test vectors.
836 Otherwise, _strict_tolerance is used.
838 for arg_type
in arg_types
:
839 test_inputs
= [_default_inputs
[arg_type
]]*arity
840 scalar_test_vectors
= _simulate_function(
841 make_arguments(test_inputs
), python_equivalent
,
843 for vector_length
in (2, 3, 4):
845 test_suite_dict
, name
, 400, None,
846 _vectorize_test_vectors(
847 scalar_test_vectors
, (), vector_length
))
849 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64",
850 _vectorize_test_vectors(
851 scalar_test_vectors
, (), vector_length
))
853 f('lessThan', 2, lambda x
, y
: x
< y
, 'v')
854 f('lessThanEqual', 2, lambda x
, y
: x
<= y
, 'v')
855 f('greaterThan', 2, lambda x
, y
: x
> y
, 'v')
856 f('greaterThanEqual', 2, lambda x
, y
: x
>= y
, 'v')
857 f('equal', 2, lambda x
, y
: x
== y
, 'v')
858 f('notEqual', 2, lambda x
, y
: x
!= y
, 'v')
860 _make_vector_relational_test_vectors(test_suite
)
863 def _make_vector_or_matrix_test_vectors(test_suite_dict
):
864 """Add test vectors to test_suite_dict for GLSL built-in functions
865 that operate on vectors/matrices as a whole. Examples include
866 length(), dot(), cross(), normalize(), and refract().
868 def match_args(*indices
):
869 """Return a function that determines whether the type of the
870 arguments at the given indices match.
878 lambda a, b, c, d: glsl_type_of(b) == glsl_type_of(d)
880 return lambda *args
: _argument_types_match(args
, indices
)
882 def match_simple_binop(x
, y
):
883 """Detemine whether the type of the arguments is compatible
884 for a simple binary operator (such as '+').
886 Arguments are compatible if one is a scalar and the other is a
887 vector/matrix with the same base type, or if they are the same
890 x_type
= glsl_type_of(x
)
891 y_type
= glsl_type_of(y
)
892 if x_type
.base_type
!= y_type
.base_type
:
894 if x_type
.is_scalar
or y_type
.is_scalar
:
896 return x_type
== y_type
898 def match_multiply(x
, y
):
899 """Determine whether the type of the arguments is compatible
902 Arguments are compatible if they are scalars, vectors, or
903 matrices with the same base type, and the vector/matrix sizes
904 are properly matched.
906 x_type
= glsl_type_of(x
)
907 y_type
= glsl_type_of(y
)
908 if x_type
.base_type
!= y_type
.base_type
:
910 if x_type
.is_scalar
or y_type
.is_scalar
:
912 if x_type
.is_vector
and y_type
.is_matrix
:
913 # When multiplying vector * matrix, the vector is
914 # transposed to a row vector. So its row count must match
915 # the row count of the matrix.
916 return x_type
.num_rows
== y_type
.num_rows
917 elif x_type
.is_vector
:
918 assert y_type
.is_vector
919 # When multiplying vector * vector, the multiplication is
920 # done componentwise, so the types must match exactly.
921 return x_type
== y_type
923 assert x_type
.is_matrix
924 # When multiplying matrix * matrix or matrix * vector, a
925 # standard linear algebraic multiply is used, so x's
926 # column count must match y's row count.
927 return x_type
.num_cols
== y_type
.num_rows
929 def match_shift(x
, y
):
930 """Determine whether the type of the arguments is compatible
931 for shift operations.
933 Arguments are compatible if they are the same length or the
934 first one is a vector and the second is a scalar. Their base
935 types need not be the same, but they both must be integral.
937 x_type
= glsl_type_of(x
)
938 y_type
= glsl_type_of(y
)
939 if x_type
.base_type
not in (glsl_int
, glsl_uint
):
941 if y_type
.base_type
not in (glsl_int
, glsl_uint
):
945 assert not x_type
.is_matrix
946 assert not y_type
.is_matrix
947 return x_type
.num_rows
== y_type
.num_rows
949 nz_doubles
= [ -1.333333333333333259, 0.85]
950 doubles
= [0.0] + nz_doubles
952 np
.array([-0.10, -1.20]),
953 np
.array([-0.42, 0.48]),
954 np
.array([-1.333333333333333259, -0.85, -0.94]),
955 np
.array([1.67, 0.66, 1.87]),
956 np
.array([-1.65, 1.33, 1.93, 0.76]),
957 np
.array([0.80, -0.15, -0.51, 0.0])
959 nz_doubles_dvecs
= nz_doubles
+ dvecs
961 np
.array([-0.03, -0.85, -0.94]),
962 np
.array([ -1.333333333333333259, 0.66, 1.87]),
965 norm_doubles_dvecs
= [_normalize(x
) for x
in nz_doubles_dvecs
]
967 np
.array([[ 1.60, 0.76],
968 [ 1.53, -1.00]]), # mat2
969 np
.array([[-0.13, -0.87],
970 [-1.40, 1.40]]), # mat2
971 np
.array([[-1.11, 1.67, -0.41],
972 [ 0.13, 1.09, -0.02],
973 [ 0.56, 0.95, 0.24]]), # mat3
974 np
.array([[-1.69, -0.46, -0.18],
976 [-1.53, -0.70, -1.47]]), # mat3
977 np
.array([[-1.00, -0.55, -1.08, 1.79],
978 [ 1.77, 0.62, 0.48, -1.35],
979 [ 0.09, -0.71, -1.39, -1.21],
980 [-0.91, -1.82, -1.43, 0.72]]), # mat4
981 np
.array([[ 0.06, 1.31, 1.52, -1.96],
982 [ 1.60, -0.32, 0.51, -1.84],
983 [ 1.25, 0.45, 1.90, -0.72],
984 [-0.16, 0.45, -0.88, 0.39]]), # mat4
986 mats
= squaremats
+ [
987 np
.array([[ 0.09, 1.30, 1.25],
988 [-1.19, 0.08, 1.08]]), # mat3x2
989 np
.array([[-0.36, -1.08, -0.60],
990 [-0.53, 0.88, -1.79]]), # mat3x2
991 np
.array([[-0.46, 1.94],
993 [ 1.03, -0.50]]), # mat2x3
994 np
.array([[ 1.38, -1.08],
996 [ 1.00, -0.74]]), # mat2x3
997 np
.array([[ 1.81, -0.87, 0.81, 0.65],
998 [-1.16, -1.52, 0.25, -1.51]]), # mat4x2
999 np
.array([[ 1.93, -1.63, 0.29, 1.60],
1000 [ 0.49, 0.27, 0.14, 0.94]]), # mat4x2
1001 np
.array([[ 0.16, -1.69],
1004 [-0.02, -1.21]]), # mat2x4
1005 np
.array([[-1.02, 0.74],
1008 [ 0.30, 1.13]]), # mat2x4
1009 np
.array([[-0.27, -1.38, -1.41, -0.12],
1010 [-0.17, -0.56, 1.47, 1.86],
1011 [-1.85, -1.29, 1.77, 0.01]]), # mat4x3
1012 np
.array([[-0.47, -0.15, 1.97, -1.05],
1013 [-0.20, 0.53, -1.82, -1.41],
1014 [-1.39, -0.19, 1.62, 1.58]]), # mat4x3
1015 np
.array([[ 1.42, -0.86, 0.27],
1016 [ 1.80, -1.74, 0.04],
1017 [-1.88, -0.37, 0.43],
1018 [ 1.37, 1.90, 0.71]]), # mat3x4
1019 np
.array([[-1.72, 0.09, 0.45],
1020 [-0.31, -1.58, 1.92],
1021 [ 0.14, 0.18, -0.56],
1022 [ 0.40, -0.77, 1.76]]), # mat3x4
1026 np
.array([[ 1.60, 0.76],
1027 [ 1.53, -1.00]]), # mat2
1028 np
.array([[-0.13, -0.87],
1029 [-1.40, 1.40]]), # mat2
1030 np
.array([[-1.11, 1.67, -0.41],
1031 [ 0.13, 1.09, -0.02],
1032 [ 0.56, 0.95, 0.24]]), # mat3
1033 np
.array([[-1.69, -0.46, -0.18],
1034 [-1.09, 1.75, 2.00],
1035 [-1.53, -0.70, -1.47]]), # mat3
1036 np
.array([[-1.00, -0.55, -1.08, 1.79],
1037 [ 1.77, 0.62, 0.48, -1.35],
1038 [ 0.09, -0.71, -1.39, -1.21],
1039 [-0.91, -1.82, -1.43, 0.72]]), # mat4
1040 np
.array([[ 0.06, 1.31, 1.52, -1.96],
1041 [ 1.60, -0.32, 0.51, -1.84],
1042 [ 1.25, 0.45, 1.90, -0.72],
1043 [-0.16, 0.45, -0.88, 0.39]]), # mat4
1045 dmats
= dsquaredmats
+ [
1046 np
.array([[ 0.09, 1.30, 1.25],
1047 [-1.19, 0.08, 1.08]]), # mat3x2
1048 np
.array([[-0.36, -1.08, -0.60],
1049 [-0.53, 0.88, -1.79]]), # mat3x2
1050 np
.array([[-0.46, 1.94],
1052 [ 1.03, -0.50]]), # mat2x3
1053 np
.array([[ 1.38, -1.08],
1055 [ 1.00, -0.74]]), # mat2x3
1056 np
.array([[ 1.81, -0.87, 0.81, 0.65],
1057 [-1.16, -1.52, 0.25, -1.51]]), # mat4x2
1058 np
.array([[ 1.93, -1.63, 0.29, 1.60],
1059 [ 0.49, 0.27, 0.14, 0.94]]), # mat4x2
1060 np
.array([[ 0.16, -1.69],
1063 [-0.02, -1.21]]), # mat2x4
1064 np
.array([[-1.02, 0.74],
1067 [ 0.30, 1.13]]), # mat2x4
1068 np
.array([[-0.27, -1.38, -1.41, -0.12],
1069 [-0.17, -0.56, 1.47, 1.86],
1070 [-1.85, -1.29, 1.77, 0.01]]), # mat4x3
1071 np
.array([[-0.47, -0.15, 1.97, -1.05],
1072 [-0.20, 0.53, -1.82, -1.41],
1073 [-1.39, -0.19, 1.62, 1.58]]), # mat4x3
1074 np
.array([[ 1.42, -0.86, 0.27],
1075 [ 1.80, -1.74, 0.04],
1076 [-1.88, -0.37, 0.43],
1077 [ 1.37, 1.90, 0.71]]), # mat3x4
1078 np
.array([[-1.72, 0.09, 0.45],
1079 [-0.31, -1.58, 1.92],
1080 [ 0.14, 0.18, -0.56],
1081 [ 0.40, -0.77, 1.76]]), # mat3x4
1083 def f(name
, arity
, python_equivalent
,
1084 filter, test_inputs
, tolerance_function
=_strict_tolerance
,
1086 """Make test vectors for the function with the given name and
1087 arity, which was introduced in the given glsl_version.
1089 python_equivalent is a Python function which simulates the GLSL
1090 function. This function should return None in any case where the
1091 output of the GLSL function is undefined. However, it need not
1092 check that the lengths of the input vectors are all the same.
1094 If filter is not None, it will be called with each set of
1095 arguments, and test cases will only be generated if the filter
1098 test_inputs is a list, the ith element of which is a list of
1099 vectors and/or scalars that are suitable for use as the ith
1100 argument of the function.
1102 If tolerance_function is supplied, it is a function which
1103 should be used to compute the tolerance for the test vectors.
1104 Otherwise, _strict_tolerance is used.
1106 If template is supplied, it is used insted as the template for
1107 the Signature objects generated.
1109 test_inputs
= make_arguments(test_inputs
)
1111 if filter is not None:
1113 [arguments
for arguments
in test_inputs
if filter(*arguments
)]
1114 _store_test_vectors(
1115 test_suite_dict
, name
, 400, None,
1117 test_inputs
, python_equivalent
, tolerance_function
),
1119 _store_test_vectors(
1120 test_suite_dict
, name
, 150, "ARB_gpu_shader_fp64",
1122 test_inputs
, python_equivalent
, tolerance_function
),
1125 f('op-add', 2, lambda x
, y
: x
+ y
, match_simple_binop
,
1126 [doubles
+dvecs
+dmats
,
1127 doubles
+dvecs
+dmats
],
1128 template
='({0} + {1})')
1129 f('op-sub', 2, lambda x
, y
: x
- y
, match_simple_binop
,
1130 [doubles
+dvecs
+dmats
,
1131 doubles
+dvecs
+dmats
],
1132 template
='({0} - {1})')
1133 f('op-mult', 2, _multiply
, match_multiply
,
1134 [doubles
+dvecs
+dmats
,
1135 doubles
+dvecs
+dmats
],
1136 template
='({0} * {1})')
1137 f('op-div', 2, _divide
, match_simple_binop
,
1138 [doubles
+dvecs
+dmats
,
1139 doubles
+dvecs
+dmats
],
1140 template
='({0} / {1})')
1141 f('length', 1, np
.linalg
.norm
, None, [doubles
+dvecs
])
1142 f('distance', 2, lambda x
, y
: np
.linalg
.norm(x
-y
), match_args(0, 1),
1143 [doubles
+dvecs
, doubles
+dvecs
])
1144 f('dot', 2, np
.dot
, match_args(0, 1), [doubles
+dvecs
, doubles
+dvecs
])
1145 f('cross', 2, np
.cross
, match_args(0, 1), [dvec3s
, dvec3s
],
1146 _cross_product_tolerance
)
1147 f('normalize', 1, _normalize
, None, [nz_doubles_dvecs
])
1148 f('faceforward', 3, _faceforward
, match_args(0, 1, 2),
1149 [doubles
+dvecs
, doubles
+dvecs
, doubles
+dvecs
])
1150 f('reflect', 2, _reflect
, match_args(0, 1),
1151 [doubles
+dvecs
, norm_doubles_dvecs
])
1152 f('refract', 3, _refract
, match_args(0, 1),
1153 [norm_doubles_dvecs
, norm_doubles_dvecs
, [0.5, 2.0]])
1154 f('matrixCompMult', 2, lambda x
, y
: x
*y
, match_args(0, 1),
1156 f('outerProduct', 2, np
.outer
, None, [dvecs
, dvecs
])
1157 f('transpose', 1, np
.transpose
, None, [dmats
])
1159 f('inverse', 1, np
.linalg
.inv
, None, [dsquaredmats
])
1161 f('determinant', 1, np
.linalg
.det
, None, [dsquaredmats
])
1162 _make_vector_or_matrix_test_vectors(test_suite
)
1165 def _check_signature_safety(test_suite_dict
):
1166 """As a final safety check, verify that for each possible
1167 combination of name and argtypes, there is exactly one
1170 name_argtype_combos
= set()
1171 for signature
in test_suite_dict
:
1172 name_argtype_combo
= (signature
.name
, signature
.argtypes
, signature
.extension
)
1173 if name_argtype_combo
in name_argtype_combos
:
1175 'Duplicate signature found for {0}'.format(name_argtype_combo
))
1176 name_argtype_combos
.add(name_argtype_combo
)
1177 _check_signature_safety(test_suite
)