3 # Copyright © 2014 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.
25 """Generate a set of shader_runner tests for overloaded versions of every
26 built-in function specified in arb_shader_precision, based on the test
27 vectors computed by builtin_function.py, and structured according to the mako
28 templates in templates/gen_shader_precision_tests.
30 The vertex, geometry, and fragment shader types are exercised by each test.
31 In all cases, the inputs to the built-in functions come from uniforms, so
32 that the effectiveness of the test won't be circumvented by constant folding
35 The tests operate by invoking the built-in function in the appropriate
36 shader, calculating any deviance from the expected value (in ulps), comparing
37 the deviance to a supplied tolerance (according to those specified in
38 arb_shader_precision), and then outputting the pass/fail result as a solid
39 rgba color which is then checked using shader_runner's "probe rgba" command.
41 For built-in functions whose result type is multi-valued (vec or mat), the
42 tests calculate the error in ulps for each element separately, but compare
43 only the largest error value to the tolerance. This accounts for cases where
44 error varies among the elements of a vec or mat result.
46 This program updates the tolerance values in the test vectors with tolerances
47 sourced from the GLSL spec (v4.5) and adds a few new tests as well.
49 This program outputs, to stdout, the name of each file it generates.
52 from __future__
import print_function
, division
, absolute_import
53 from builtin_function
import *
59 from six
.moves
import range
61 from templates
import template_file
65 tolerances
= {'pow': 16.0,
76 shader_precision_spec_fns
= ('op-add', 'op-assign-add',
77 'op-sub', 'op-assign-sub',
78 'op-mult', 'op-assign-mult',
79 'op-div', 'op-assign-div',
85 'sqrt', 'inversesqrt')
87 def _is_sequence(arg
):
88 return isinstance(arg
, (collections
.Sequence
, numpy
.ndarray
))
91 s
= struct
.pack('>f', f
)
92 return struct
.unpack('>L', s
)[0]
94 # The precision for log and log2 from the GLSL spec:
95 # 3 ULP outside the range [0.5, 2.0].
96 # Absolute error < 2^-21 inside the range [0.5, 2.0].
97 def _log_tol(compcnt
, args
, i
, j
):
98 arg
= args
[j
][i
] if compcnt
> 1 else args
[0]
99 if arg
< 0.5 or arg
> 2.0:
101 elif arg
>= 0.5 and arg
< 1.0:
102 # in the range [0.5, 1.0), one ulp is 5.960464478e-08,
106 # in the range [1.0, 2.0), one ulp is 1.192092896e-07,
110 # The precision for exp and exp2 from the GLSL spec:
112 def _exp_tol(compcnt
, args
, i
, j
):
113 arg
= args
[j
][i
] if compcnt
> 1 else args
[0]
114 return 3.0 + 2.0 * abs(arg
)
116 # The precision for division from the GLSL spec:
117 # 2.5 ULP for b in the range [2^-126, 2^126].
118 def _div_tol(compcnt
, args
, i
, j
):
119 divisor
= args
[1][i
] if _is_sequence(args
[1]) else args
[1]
120 assert (divisor
>= pow(2,-126) and divisor
<= pow(2,126)) or \
121 (divisor
<= -pow(2,-126) and divisor
>= -pow(2,126))
124 # The precision for pow from the GLSL spec:
125 # Inherited from exp2 (x * log2 (y)).
126 def _pow_tol(compcnt
, args
, i
, j
):
127 x
= args
[0][i
] if _is_sequence(args
[0]) else args
[0]
128 y
= args
[1][i
] if _is_sequence(args
[1]) else args
[1]
129 return(_exp_tol(0, [x
* _log_tol(0, [y
], 0, 0)], 0, 0))
132 _tol_fns
= { 'op-add': (lambda *args
: 0.0),
133 'op-assign-add': (lambda *args
: 0.0),
134 'op-sub': (lambda *args
: 0.0),
135 'op-assign-sub': (lambda *args
: 0.0),
136 'op-mult': (lambda *args
: 0.0),
137 'op-assign-mult': (lambda *args
: 0.0),
138 'op-div': (lambda compcnt
, args
, i
, j
: _div_tol(compcnt
, args
, i
, j
)),
139 'op-assign-div': (lambda compcnt
, args
, i
, j
: _div_tol(compcnt
, args
, i
, j
)),
140 'degrees': (lambda *args
: 2.5),
141 'radians': (lambda *args
: 2.5),
142 'pow': (lambda compcnt
, args
, i
, j
: _pow_tol(compcnt
, args
, i
, j
)),
147 'sqrt': (lambda *args
: 2.5),
148 'inversesqrt': (lambda *args
: 2.0) }
151 def _gen_tolerance(name
, rettype
, args
):
153 compcnt
= rettype
.num_cols
* rettype
.num_rows
155 for j
in range(rettype
.num_cols
):
156 for i
in range(rettype
.num_rows
):
157 assert _tol_fns
[name
]
158 ret
.append(_tol_fns
[name
](compcnt
, args
, i
, j
))
161 def make_indexers(signature
):
162 """Build a list of strings which index into every possible
163 value of the result. For example, if the result is a vec2,
164 then build the indexers ['[0]', '[1]'].
166 if signature
.rettype
.num_cols
== 1:
169 col_indexers
= ['[{0}]'.format(i
) for i
in range(signature
.rettype
.num_cols
)]
170 if signature
.rettype
.num_rows
== 1:
173 row_indexers
= ['[{0}]'.format(i
) for i
in range(signature
.rettype
.num_rows
)]
174 return [col_indexer
+ row_indexer
175 for col_indexer
in col_indexers
for row_indexer
in row_indexers
]
177 def shader_runner_type(glsl_type
):
178 """Return the appropriate type name necessary for binding a
179 uniform of the given type using shader_runner's "uniform" command.
180 Boolean values and vectors are converted to ints, and square
181 matrices are written in "matNxN" form.
183 if glsl_type
.base_type
== glsl_bool
:
184 if glsl_type
.is_scalar
:
187 return 'ivec{0}'.format(glsl_type
.num_rows
)
188 elif glsl_type
.is_matrix
:
189 return 'mat{0}x{1}'.format(glsl_type
.num_cols
, glsl_type
.num_rows
)
191 return str(glsl_type
)
193 def shader_runner_format(values
):
194 """Format the given values for use in a shader_runner "uniform" or
195 "probe rgba" command. Sequences of values are separated by spaces.
198 if _is_sequence(values
):
201 assert isinstance(x
, (float, np
.float32
))
202 retval
+= ' {0:#08x}'.format(_floatToBits(x
))
204 assert isinstance(values
, (float, np
.float32
))
205 retval
= '{0:#08x}'.format(_floatToBits(values
))
209 def drop_signbit(xs
):
210 if (np
.isscalar(xs
)):
211 return xs
.dtype
.type(0.0) if xs
== 0.0 else xs
212 return np
.array([xs
.dtype
.type(0.0) if x
== 0.0 else x
for x
in xs
],
216 """ Main function """
218 for signature
, test_vectors
in six
.iteritems(test_suite
):
219 arg_float_check
= all(arg
.base_type
== glsl_float
for arg
in signature
.argtypes
)
220 arg_mat_check
= any(arg
.is_matrix
for arg
in signature
.argtypes
)
221 # Filter the test vectors down to only those which deal exclusively in
222 # non-matrix float types and are specified in the spec
223 if (signature
.rettype
.base_type
== glsl_float
and
225 signature
.name
in shader_precision_spec_fns
and
227 # replace the tolerances in each test_vector with
228 # our own tolerances specified in ulps
229 refined_test_vectors
= []
230 complex_tol_type
= signature
.rettype
231 for test_vector
in test_vectors
:
232 tolerance
= _gen_tolerance(signature
.name
, signature
.rettype
, test_vector
.arguments
)
233 result
= drop_signbit(test_vector
.result
)
234 refined_test_vectors
.append(TestVector(test_vector
.arguments
, result
, tolerance
))
235 # Then generate the shader_test scripts
236 for shader_stage
in ('vs', 'fs', 'gs'):
237 template
= template_file('gen_shader_precision_tests', '{0}.mako'.format(shader_stage
))
238 output_filename
= os
.path
.join( 'spec', 'arb_shader_precision',
239 '{0}-{1}-{2}.shader_test'.format(
240 shader_stage
, signature
.name
,
241 '-'.join(str(argtype
)
242 for argtype
in signature
.argtypes
)))
243 print(output_filename
)
244 dirname
= os
.path
.dirname(output_filename
)
245 if not os
.path
.exists(dirname
):
247 indexers
= make_indexers(signature
)
248 num_elements
= signature
.rettype
.num_cols
* signature
.rettype
.num_rows
249 invocation
= signature
.template
.format( *['arg{0}'.format(i
)
250 for i
in range(len(signature
.argtypes
))])
251 with
open(output_filename
, 'w') as f
:
252 f
.write(template
.render_unicode( signature
=signature
,
253 is_complex_tolerance
=_is_sequence(tolerance
),
254 complex_tol_type
=signature
.rettype
,
255 test_vectors
=refined_test_vectors
,
256 invocation
=invocation
,
257 num_elements
=num_elements
,
259 shader_runner_type
=shader_runner_type
,
260 shader_runner_format
=shader_runner_format
,
261 column_major_values
=column_major_values
))
263 if __name__
== "__main__":