[Piglit] [PATCH v5 2/3] arb_shader_precision: add framework for calculating tolerances for complex functions
Micah Fedke
micah.fedke at collabora.co.uk
Fri Apr 3 14:00:08 PDT 2015
- update mako templates to support vectors of tolerances
---
generated_tests/gen_shader_precision_tests.py | 138 +++++++++++++++++----
.../templates/gen_shader_precision_tests/fs.mako | 62 +++++----
.../templates/gen_shader_precision_tests/gs.mako | 55 ++++----
.../templates/gen_shader_precision_tests/vs.mako | 55 ++++----
4 files changed, 217 insertions(+), 93 deletions(-)
diff --git a/generated_tests/gen_shader_precision_tests.py b/generated_tests/gen_shader_precision_tests.py
index 6c011a3..bef1459 100644
--- a/generated_tests/gen_shader_precision_tests.py
+++ b/generated_tests/gen_shader_precision_tests.py
@@ -25,7 +25,7 @@
"""Generate a set of shader_runner tests for overloaded versions of every
built-in function specified in arb_shader_precision, based on the test
vectors computed by builtin_function.py, and structured according to the mako
- templates in shader_precision_templates/.
+ templates in templates/gen_shader_precision_tests.
The vertex, geometry, and fragment shader types are exercised by each test.
In all cases, the inputs to the built-in functions come from uniforms, so
@@ -43,20 +43,26 @@
only the largest error value to the tolerance. This accounts for cases where
error varies among the elements of a vec or mat result.
+ This program updates the tolerance values in the test vectors with tolerances
+ sourced from the GLSL spec (v4.5) and adds a few new tests as well.
+
This program outputs, to stdout, the name of each file it generates.
"""
from __future__ import print_function, division, absolute_import
from builtin_function import *
-import os
+import os
import numpy
+import struct
import six
from six.moves import range
from templates import template_file
-tolerances = {'pow': 16.0,
+
+
+tolerances = {'pow': 16.0,
'exp': 3.0,
'exp2': 3.0,
'log': 3.0,
@@ -67,14 +73,87 @@ tolerances = {'pow': 16.0,
'op-assign-div': 2.5,
}
-trig_builtins = ('sin', 'cos', 'tan',
- 'asin', 'acos', 'atan',
- 'sinh', 'cosh', 'tanh',
- 'asinh', 'acosh', 'atanh')
+shader_precision_spec_fns = ('op-add', 'op-assign-add',
+ 'op-sub', 'op-assign-sub',
+ 'op-mult', 'op-assign-mult',
+ 'op-div', 'op-assign-div',
+ 'degrees', 'radians',
+ 'pow',
+ 'exp',
+ 'exp2',
+ 'log', 'log2',
+ 'sqrt', 'inversesqrt')
def _is_sequence(arg):
return isinstance(arg, (collections.Sequence, numpy.ndarray))
+# The precision for log and log2 from the GLSL spec:
+# 3 ULP outside the range [0.5, 2.0].
+# Absolute error < 2^-21 inside the range [0.5, 2.0].
+def _log_tol(arg):
+ if arg < 0.5 or arg > 2.0:
+ return 3.0
+ elif arg >= 0.5 and arg < 1.0:
+ # in the range [0.5, 1.0), one ulp is 5.960464478e-08,
+ # so 2^-21 is 8 ulps
+ return 8.0
+ else:
+ # in the range [1.0, 2.0), one ulp is 1.192092896e-07,
+ # so 2^-21 is 4 ulps
+ return 4.0
+
+# The precision for exp and exp2 from the GLSL spec:
+# (3 + 2 * |x|) ULP.
+def _exp_tol(arg):
+ return 3.0 + 2.0 * abs(arg)
+
+def _gen_tolerance(name, rettype, args, expected):
+ ret = []
+ compcnt = rettype.num_cols * rettype.num_rows
+
+ for j in range(rettype.num_cols):
+ for i in range(rettype.num_rows):
+ # The precision for add, sub and mult from the GLSL spec:
+ # Correctly rounded.
+ if name in ('op-add', 'op-assign-add', 'op-sub', 'op-assign-sub', 'op-mult', 'op-assign-mult'):
+ ret.append(0.0)
+ # The precision for division from the GLSL spec:
+ # 2.5 ULP for b in the range [2^-126, 2^126].
+ elif name in ('op-div', 'op-assign-div'):
+ divisor = args[1][i] if _is_sequence(args[1]) else args[1]
+ if (divisor >= pow(2,-126) and divisor <= pow(2,126)) or \
+ (divisor <= -pow(2,-126) and divisor >= -pow(2,126)):
+ ret.append(2.5)
+ else:
+ return -1.0
+ # The precision for degrees and radians from the GLSL spec:
+ # Inherited from division
+ # denominator (pi or 180, respectively) is always within [2^-126, 2^126]
+ elif name in ('degrees', 'radians'):
+ ret.append(2.5)
+ # The precision for pow from the GLSL spec:
+ # Inherited from exp2 (x * log2 (y)).
+ elif name is 'pow':
+ x = args[0][i] if _is_sequence(args[0]) else args[0]
+ y = args[1][i] if _is_sequence(args[1]) else args[1]
+ ret.append(_exp_tol(x * _log_tol(y)))
+ elif name in ('exp', 'exp2'):
+ ret.append(_exp_tol(args[j][i] if compcnt > 1 else args[0]))
+ elif name in ('log', 'log2'):
+ ret.append(_log_tol(args[j][i] if compcnt > 1 else args[0]))
+ # The precision for sqrt from the GLSL spec:
+ # Inherited from 1.0 / inversesqrt().
+ elif name is 'sqrt':
+ ret.append(2.5)
+ # The precision for inversesqrt from the GLSL spec:
+ # 2 ULP.
+ elif name is 'inversesqrt':
+ ret.append(2.0)
+ else:
+ return -1.0
+
+ return ret
+
def make_indexers(signature):
"""Build a list of strings which index into every possible
value of the result. For example, if the result is a vec2,
@@ -88,7 +167,7 @@ def make_indexers(signature):
row_indexers = ['']
else:
row_indexers = ['[{0}]'.format(i) for i in range(signature.rettype.num_rows)]
- return [col_indexer + row_indexer
+ return [col_indexer + row_indexer
for col_indexer in col_indexers for row_indexer in row_indexers]
def shader_runner_type(glsl_type):
@@ -116,7 +195,7 @@ def shader_runner_format(values):
retval = ''
for x in values:
assert isinstance(x, (float, np.float32))
- retval+=' {0:1.8e}'.format(x)
+ retval += ' {0:1.8e}'.format(x)
else:
assert isinstance(values, (float, np.float32))
retval = '{0:1.8e}'.format(values)
@@ -128,34 +207,45 @@ def main():
""" Main function """
for signature, test_vectors in sorted(six.iteritems(test_suite)):
- arg_float_check = tuple(
- arg.base_type == glsl_float for arg in signature.argtypes)
- # Filter the test vectors down to only those which deal exclusively in float types
- #and are not trig functions or determinant()
- indexers = make_indexers(signature)
- num_elements = signature.rettype.num_cols*signature.rettype.num_rows
- invocation = signature.template.format( *['arg{0}'.format(i)
- for i in range(len(signature.argtypes))])
+ arg_float_check = tuple(arg.base_type == glsl_float for arg in signature.argtypes)
+ arg_mat_check = tuple(arg.is_matrix for arg in signature.argtypes)
+ # Filter the test vectors down to only those which deal exclusively in
+ # non-matrix float types and are specified in the spec
if (signature.rettype.base_type == glsl_float and
arg_float_check and
all(arg_float_check) and
- signature.name not in trig_builtins and
- signature.name != 'determinant'):
+ signature.name in shader_precision_spec_fns and
+ arg_mat_check and
+ (not True in arg_mat_check)):
+ # replace the tolerances in each test_vector with
+ # our own tolerances specified in ulps
+ refined_test_vectors = []
+ complex_tol_type = signature.rettype
+ for test_vector in test_vectors:
+ tolerance = _gen_tolerance(signature.name, signature.rettype, test_vector.arguments, test_vector.result)
+ if tolerance != -1.0:
+ refined_test_vectors.append(TestVector(test_vector.arguments, test_vector.result, tolerance))
+ # Then generate the shader_test scripts
for shader_stage in ('vs', 'fs', 'gs'):
template = template_file('gen_shader_precision_tests', '{0}.mako'.format(shader_stage))
output_filename = os.path.join( 'spec', 'arb_shader_precision',
'{0}-{1}-{2}.shader_test'.format(
- shader_stage, signature.name,
- '-'.join(str(argtype)
+ shader_stage, signature.name,
+ '-'.join(str(argtype)
for argtype in signature.argtypes)))
print(output_filename)
dirname = os.path.dirname(output_filename)
if not os.path.exists(dirname):
os.makedirs(dirname)
+ indexers = make_indexers(signature)
+ num_elements = signature.rettype.num_cols * signature.rettype.num_rows
+ invocation = signature.template.format( *['arg{0}'.format(i)
+ for i in range(len(signature.argtypes))])
with open(output_filename, 'w') as f:
- f.write(template.render_unicode( signature=signature,
- test_vectors=test_vectors,
- tolerances=tolerances,
+ f.write(template.render_unicode( signature=signature,
+ is_complex_tolerance=_is_sequence(tolerance),
+ complex_tol_type=signature.rettype,
+ test_vectors=refined_test_vectors,
invocation=invocation,
num_elements=num_elements,
indexers=indexers,
diff --git a/generated_tests/templates/gen_shader_precision_tests/fs.mako b/generated_tests/templates/gen_shader_precision_tests/fs.mako
index 19ec737..7e97b7e 100644
--- a/generated_tests/templates/gen_shader_precision_tests/fs.mako
+++ b/generated_tests/templates/gen_shader_precision_tests/fs.mako
@@ -4,12 +4,7 @@
[require]
GLSL >= 4.00
-[vertex shader]
-in vec4 piglit_vertex;
-void main()
-{
- gl_Position = piglit_vertex;
-}
+[vertex shader passthrough]
[fragment shader]
#extension GL_ARB_shader_precision : require
@@ -19,7 +14,11 @@ void main()
% for i, arg in enumerate(signature.argtypes):
uniform ${arg} arg${i};
% endfor
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${complex_tol_type} tolerance;
+% else:
uniform float tolerance;
+% endif
uniform ${signature.rettype} expected;
void main()
@@ -56,29 +55,34 @@ ${' || '.join('(resultbits[{0}]>>31 != expectedbits[{0}]>>31)'.format(i) for i i
${signature.rettype} ulps = ${signature.rettype}(\
${', '.join('abs(resultbits[{0}] - expectedbits[{0}])'.format(i) for i in range(0, num_elements))}\
);
- ##
- ## find the maximum error in ulps of all the calculations using a nested max() sort
- ##
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+ ## compare vecs directly, use a boolean version of the rettype
+ b${signature.rettype} calcerr = greaterThan(ulps, tolerance);
+ % else:
+ ##
+ ## find the maximum error in ulps of all the calculations using a nested max() sort
+ ##
float max_error = \
- ## start with the outermost max() if there are more than 2 elements
- ## (two element arrays, eg. vec2, are handled by the final max() below, only)
- % if num_elements > 2:
+ ## start with the outermost max() if there are more than 2 elements
+ ## (two element arrays, eg. vec2, are handled by the final max() below, only)
+ % if num_elements > 2:
max( \
- % endif
- ## cat each value to compare, with an additional nested max() up until the final two values
- % for i, indexer in enumerate(indexers[:len(indexers)-2]):
+ % endif
+ ## cat each value to compare, with an additional nested max() up until the final two values
+ % for i, indexer in enumerate(indexers[:len(indexers)-2]):
ulps${indexer}, \
- % if i != len(indexers)-3:
+ % if i != len(indexers)-3:
max(\
- % endif
- ## cat the final, deepest, max comparison
- % endfor
+ % endif
+ ## cat the final, deepest, max comparison
+ % endfor
max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
- ## fill in completing parens
- % for i in range(0, num_elements-2):
+ ## fill in completing parens
+ % for i in range(0, num_elements-2):
)\
- % endfor
+ % endfor
;
+ %endif
% else:
##
## if there is only a single result value generated, compare it directly
@@ -86,18 +90,18 @@ max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
int resultbits = floatBitsToInt(result);
int expectedbits = floatBitsToInt(expected);
bool signerr = resultbits>>31 != expectedbits>>31;
- % if signature.name != 'distance':
- float ulps = distance(resultbits, expectedbits);
- % else:
float ulps = abs(resultbits - expectedbits);
- % endif
% endif
##
## the test passes if there were no sign errors and the ulps are within tolerance
##
gl_FragColor = \
% if signature.rettype.is_matrix or signature.rettype.is_vector:
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+!signerr && !any(calcerr)\
+ % else:
!signerr && max_error <= tolerance\
+ % endif
% else:
!signerr && ulps <= tolerance\
% endif
@@ -117,8 +121,12 @@ piglit_vertex/float/2
uniform ${shader_runner_type(signature.argtypes[i])} arg${i} ${shader_runner_format( column_major_values(test_vector.arguments[i]))}
% endfor
uniform ${shader_runner_type(signature.rettype)} expected ${shader_runner_format(column_major_values(test_vector.result))}
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${shader_runner_type(complex_tol_type)} tolerance \
+% else:
uniform float tolerance \
-${tolerances.get(signature.name, 0.0)}
+% endif
+${shader_runner_format(test_vector.tolerance)}
draw arrays GL_TRIANGLE_FAN 0 4
## shader_runner uses a 250x250 window so we must ensure that test_num <= 250.
probe rgba ${test_num % 250} 0 0.0 1.0 0.0 1.0
diff --git a/generated_tests/templates/gen_shader_precision_tests/gs.mako b/generated_tests/templates/gen_shader_precision_tests/gs.mako
index de4a3c8..3a76e9b 100644
--- a/generated_tests/templates/gen_shader_precision_tests/gs.mako
+++ b/generated_tests/templates/gen_shader_precision_tests/gs.mako
@@ -24,7 +24,11 @@ flat out vec4 color;
% for i, arg in enumerate(signature.argtypes):
uniform ${arg} arg${i};
% endfor
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${complex_tol_type} tolerance;
+% else:
uniform float tolerance;
+% endif
uniform ${signature.rettype} expected;
void main()
@@ -62,29 +66,34 @@ ${' || '.join('(resultbits[{0}]>>31 != expectedbits[{0}]>>31)'.format(i) for i i
${signature.rettype} ulps = ${signature.rettype}(\
${', '.join('abs(resultbits[{0}] - expectedbits[{0}])'.format(i) for i in range(0, num_elements))}\
);
- ##
- ## find the maximum error in ulps of all the calculations using a nested max() sort
- ##
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+ ## compare vecs directly, use a boolean version of the rettype
+ b${signature.rettype} calcerr = greaterThan(ulps, tolerance);
+ % else:
+ ##
+ ## find the maximum error in ulps of all the calculations using a nested max() sort
+ ##
float max_error = \
- ## start with the outermost max() if there are more than 2 elements
- ## (two element arrays, eg. vec2, are handled by the final max() below, only)
- % if num_elements > 2:
+ ## start with the outermost max() if there are more than 2 elements
+ ## (two element arrays, eg. vec2, are handled by the final max() below, only)
+ % if num_elements > 2:
max( \
- % endif
- ## cat each value to compare, with an additional nested max() up until the final two values
- % for i, indexer in enumerate(indexers[:len(indexers)-2]):
+ % endif
+ ## cat each value to compare, with an additional nested max() up until the final two values
+ % for i, indexer in enumerate(indexers[:len(indexers)-2]):
ulps${indexer}, \
- % if i != len(indexers)-3:
+ % if i != len(indexers)-3:
max(\
- % endif
- ## cat the final, deepest, max comparison
- % endfor
+ % endif
+ ## cat the final, deepest, max comparison
+ % endfor
max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
- ## fill in completing parens
- % for i in range(0, num_elements-2):
+ ## fill in completing parens
+ % for i in range(0, num_elements-2):
)\
- % endfor
+ % endfor
;
+ %endif
% else:
##
## if there is only a single result value generated, compare it directly
@@ -92,18 +101,18 @@ max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
int resultbits = floatBitsToInt(result);
int expectedbits = floatBitsToInt(expected);
bool signerr = resultbits>>31 != expectedbits>>31;
- % if signature.name != 'distance':
- float ulps = distance(resultbits, expectedbits);
- % else:
float ulps = abs(resultbits - expectedbits);
- % endif
% endif
##
## the test passes if there were no sign errors and the ulps are within tolerance
##
tmp_color = \
% if signature.rettype.is_matrix or signature.rettype.is_vector:
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+!signerr && !any(calcerr)\
+ % else:
!signerr && max_error <= tolerance\
+ % endif
% else:
!signerr && ulps <= tolerance\
% endif
@@ -136,8 +145,12 @@ piglit_vertex/float/2
uniform ${shader_runner_type(signature.argtypes[i])} arg${i} ${shader_runner_format( column_major_values(test_vector.arguments[i]))}
% endfor
uniform ${shader_runner_type(signature.rettype)} expected ${shader_runner_format(column_major_values(test_vector.result))}
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${shader_runner_type(complex_tol_type)} tolerance \
+% else:
uniform float tolerance \
-${tolerances.get(signature.name, 0.0)}
+% endif
+${shader_runner_format(test_vector.tolerance)}
draw arrays GL_TRIANGLE_FAN 0 4
## shader_runner uses a 250x250 window so we must ensure that test_num <= 250.
probe rgba ${test_num % 250} 0 0.0 1.0 0.0 1.0
diff --git a/generated_tests/templates/gen_shader_precision_tests/vs.mako b/generated_tests/templates/gen_shader_precision_tests/vs.mako
index 8d8fad6..d64effd 100644
--- a/generated_tests/templates/gen_shader_precision_tests/vs.mako
+++ b/generated_tests/templates/gen_shader_precision_tests/vs.mako
@@ -14,7 +14,11 @@ flat out vec4 color;
% for i, arg in enumerate(signature.argtypes):
uniform ${arg} arg${i};
% endfor
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${complex_tol_type} tolerance;
+% else:
uniform float tolerance;
+% endif
uniform ${signature.rettype} expected;
void main()
@@ -52,29 +56,34 @@ ${' || '.join('(resultbits[{0}]>>31 != expectedbits[{0}]>>31)'.format(i) for i i
${signature.rettype} ulps = ${signature.rettype}(\
${', '.join('abs(resultbits[{0}] - expectedbits[{0}])'.format(i) for i in range(0, num_elements))}\
);
- ##
- ## find the maximum error in ulps of all the calculations using a nested max() sort
- ##
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+ ## compare vecs directly, use a boolean version of the rettype
+ b${signature.rettype} calcerr = greaterThan(ulps, tolerance);
+ % else:
+ ##
+ ## find the maximum error in ulps of all the calculations using a nested max() sort
+ ##
float max_error = \
- ## start with the outermost max() if there are more than 2 elements
- ## (two element arrays, eg. vec2, are handled by the final max() below, only)
- % if num_elements > 2:
+ ## start with the outermost max() if there are more than 2 elements
+ ## (two element arrays, eg. vec2, are handled by the final max() below, only)
+ % if num_elements > 2:
max( \
- % endif
- ## cat each value to compare, with an additional nested max() up until the final two values
- % for i, indexer in enumerate(indexers[:len(indexers)-2]):
+ % endif
+ ## cat each value to compare, with an additional nested max() up until the final two values
+ % for i, indexer in enumerate(indexers[:len(indexers)-2]):
ulps${indexer}, \
- % if i != len(indexers)-3:
+ % if i != len(indexers)-3:
max(\
- % endif
- ## cat the final, deepest, max comparison
- % endfor
+ % endif
+ ## cat the final, deepest, max comparison
+ % endfor
max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
- ## fill in completing parens
- % for i in range(0, num_elements-2):
+ ## fill in completing parens
+ % for i in range(0, num_elements-2):
)\
- % endfor
+ % endfor
;
+ %endif
% else:
##
## if there is only a single result value generated, compare it directly
@@ -82,18 +91,18 @@ max(ulps${indexers[len(indexers)-2]}, ulps${indexers[len(indexers)-1]})\
int resultbits = floatBitsToInt(result);
int expectedbits = floatBitsToInt(expected);
bool signerr = resultbits>>31 != expectedbits>>31;
- % if signature.name != 'distance':
- float ulps = distance(resultbits, expectedbits);
- % else:
float ulps = abs(resultbits - expectedbits);
- % endif
% endif
##
## the test passes if there were no sign errors and the ulps are within tolerance
##
color = \
% if signature.rettype.is_matrix or signature.rettype.is_vector:
+ % if is_complex_tolerance and complex_tol_type.name != 'float':
+!signerr && !any(calcerr)\
+ % else:
!signerr && max_error <= tolerance\
+ % endif
% else:
!signerr && ulps <= tolerance\
% endif
@@ -121,8 +130,12 @@ piglit_vertex/float/2
uniform ${shader_runner_type(signature.argtypes[i])} arg${i} ${shader_runner_format( column_major_values(test_vector.arguments[i]))}
% endfor
uniform ${shader_runner_type(signature.rettype)} expected ${shader_runner_format(column_major_values(test_vector.result))}
+% if is_complex_tolerance and complex_tol_type.name != 'float':
+uniform ${shader_runner_type(complex_tol_type)} tolerance \
+% else:
uniform float tolerance \
-${tolerances.get(signature.name, 0.0)}
+% endif
+${shader_runner_format(test_vector.tolerance)}
draw arrays GL_TRIANGLE_FAN 0 4
## shader_runner uses a 250x250 window so we must ensure that test_num <= 250.
probe rgba ${test_num % 250} 0 0.0 1.0 0.0 1.0
--
2.3.5
More information about the Piglit
mailing list