[Piglit] [PATCH v3 2/3] arb_shader_precision: add framework for calculating tolerances for complex functions
Micah Fedke
micah.fedke at collabora.co.uk
Fri Feb 27 14:36:55 PST 2015
The bigfloat package is a necessary dependency for this filter, as it provides
both arbitrary-size floating point operations and it allows all floating point
values and operations in a code block to be forced into a particular precision,
leaving no question as to what precision was employed for an intermediate
calculation. This comes in handy when running the reference versions of the
complex built-ins. Performance impact is small (~6s for the entire
gen_shader_precision_tests.py script on IvyBridge i7 @ 2.9GHz) and is localized
to build-time.
---
CMakeLists.txt | 1 +
cmake/Modules/FindPythonBigfloat.cmake | 43 ++
generated_tests/gen_shader_precision_tests.py | 603 +++++++++++++++++++++++++-
3 files changed, 633 insertions(+), 14 deletions(-)
create mode 100644 cmake/Modules/FindPythonBigfloat.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1a81eed..b13acdb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -193,6 +193,7 @@ find_package(PythonInterp 2.7 REQUIRED)
find_package(PythonNumpy 1.6.2 REQUIRED)
find_package(PythonMako 0.7.3 REQUIRED)
find_package(PythonSix REQUIRED)
+find_package(PythonBigfloat 3.1.0 REQUIRED)
# Default to compiling with debug information (`gcc -g`):
if(NOT CMAKE_BUILD_TYPE)
diff --git a/cmake/Modules/FindPythonBigfloat.cmake b/cmake/Modules/FindPythonBigfloat.cmake
new file mode 100644
index 0000000..0b65b55
--- /dev/null
+++ b/cmake/Modules/FindPythonBigfloat.cmake
@@ -0,0 +1,43 @@
+# Copyright (C) 2015 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Find bigfloat
+execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} -c "import sys, bigfloat; sys.stdout.write(bigfloat.MPFR_VERSION_STRING)"
+ OUTPUT_VARIABLE _version
+ RESULT_VARIABLE _status
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+set(PythonBigfloat_VERSION_STRING ${_version})
+
+# A status returns 0 if everything is okay. And zero is false. To make
+# checking in the outer scope less surprising
+if (_status EQUAL 0)
+ set("PythonBigfloat_STATUS" "success")
+endif (_status EQUAL 0)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(
+ "PythonBigfloat"
+ REQUIRED_VARS "PythonBigfloat_STATUS"
+ VERSION_VAR "PythonBigfloat_VERSION_STRING"
+)
diff --git a/generated_tests/gen_shader_precision_tests.py b/generated_tests/gen_shader_precision_tests.py
index 6c011a3..3195f89 100644
--- a/generated_tests/gen_shader_precision_tests.py
+++ b/generated_tests/gen_shader_precision_tests.py
@@ -50,31 +50,606 @@ from __future__ import print_function, division, absolute_import
from builtin_function import *
import os
import numpy
+import struct
+import bigfloat
import six
from six.moves import range
from templates import template_file
-tolerances = {'pow': 16.0,
- 'exp': 3.0,
- 'exp2': 3.0,
- 'log': 3.0,
- 'log2': 3.0,
- 'sqrt': 3.0,
- 'inversesqrt': 2.0,
- 'op-div': 2.5,
- 'op-assign-div': 2.5,
- }
+
+allowed_error_scale = 4.0
trig_builtins = ('sin', 'cos', 'tan',
'asin', 'acos', 'atan',
'sinh', 'cosh', 'tanh',
'asinh', 'acosh', 'atanh')
+high_precision = bigfloat.quadruple_precision
+low_precision = bigfloat.single_precision
+
def _is_sequence(arg):
return isinstance(arg, (collections.Sequence, numpy.ndarray))
+def _ulpsize(f):
+ """ determine _ulpsize in the direction of nearest infinity
+ which gives the worst case scenario for edge cases
+ """
+ if f >= 0:
+ return bigfloat.next_up(f) - f
+ else:
+ return f - bigfloat.next_down(f)
+
+def _listify(arg):
+ """ if arg is a scalar, convert it to an array
+ if it's an array, return it as-is
+ """
+ return arg if _is_sequence(arg) else [arg]
+
+def _to_bigfloat(arg):
+ if _is_sequence(arg):
+ for ar in arg:
+ assert(isinstance(ar, numpy.float32) or isinstance(ar, bigfloat.BigFloat))
+
+ else:
+ assert(isinstance(arg, numpy.float32) or isinstance(arg, bigfloat.BigFloat))
+
+ if isinstance(arg, bigfloat.BigFloat) or \
+ (_is_sequence(arg) and isinstance(arg[0], bigfloat.BigFloat)):
+ return arg
+ else:
+ return list(bigfloat.BigFloat('%.8e' % x) for x in arg)
+
+def _mod_ref(args):
+ """Modulus reference function.
+ Returns x floor (x/y).
+
+ This is a scalar implementation of a vector-componentwise GLSL built-in -
+ arguments must be in scalar form (float). Vector arguments must be
+ partitioned by the caller.
+ Arguments are converted to BigFloat type.
+ Return value is a list of a single BigFloat value.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- x
+ args[1] -- y
+ """
+ bfargs = _to_bigfloat(args)
+ x = bfargs[0]
+ y = bfargs[1]
+ return [x - y * (bigfloat.floor(x / y))]
+
+def _smoothstep_ref(args):
+ """Smoothstep reference function.
+ Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and
+ performs smooth Hermite interpolation between 0 and 1
+ when edge0 < x < edge1. This is useful in cases where
+ you would want a threshold function with a smooth
+ transition. This is equivalent to:
+ t = clamp ((x edge0), 0, 1);
+ return t * t * (3 a) + y * a
+
+ This is a scalar implementation of a vector-componentwise GLSL built-in -
+ arguments must be in scalar form (float). Vector arguments must be
+ partitioned by the caller.
+ Arguments are converted to BigFloat type.
+ Return value is a list of a single BigFloat value.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- x
+ args[1] -- y
+ args[2] -- a
+ """
+ bfargs = _to_bigfloat(args)
+ x = bfargs[0]
+ y = bfargs[1]
+ a = bfargs[2]
+ return [x * (bigfloat.BigFloat(1.0) - a) + y * a]
+
+def _length_ref(args):
+ """Length reference function.
+ Returns the length of vector x, i.e.,
+ sqrt(x[0]^2 + x[1]^2 + ...)
+
+ This is a non-componentwise GLSL built-in -
+ arguments may be scalar (float) or vector form (list).
+ Arguments are converted to BigFloat type.
+ Return value is a list of a single BigFloat value.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- x
+ """
+ component_sum = bigfloat.BigFloat(0.0)
+ x = _to_bigfloat(args[0])
+ for arg in x:
+ component_sum += bigfloat.pow(arg, 2.0)
+ return [bigfloat.sqrt(component_sum)]
+
+def _distance_ref(args):
+ """Distance reference function.
+ Returns the distance between p0 and p1, i.e.,
+ length (p0 y[1] * x[2]
+ x[2] * y[0] y[0] * x[1]]
+
+ This is a non-componentwise GLSL built-in -
+ arguments are 3-element vectors.
+ Arguments are converted to BigFloat type.
+ Return value is a 3-element list of BigFloat values.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- x
+ args[1] -- y
+ """
+ x = _to_bigfloat(args[0])
+ y = _to_bigfloat(args[1])
+ ret = [x[1] * y[2] - y[1] * x[2], x[2] * y[0] - y[2] * x[0], x[0] * y[1] - y[0] * x[1]]
+ return ret
+
+def _normalize_ref(args):
+ """Normalize reference function.
+ Returns a vector in the same direction as x but with a
+ length of 1, ie. x/length(x).
+
+ This is a non-componentwise GLSL built-in -
+ arguments may be scalar (float) or vector form (list).
+ Arguments are converted to BigFloat type.
+ Return value is a list of BigFloat values equal in count to the number of
+ items in x.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- x
+ """
+ bfargs = _to_bigfloat(args[0])
+ normalized_components = []
+ comp_len = _length_ref(args)
+ for component in bfargs:
+ normalized_components.append(component / comp_len[0])
+ return normalized_components
+
+def _faceforward_ref(args):
+ """Faceforward reference function.
+ If dot(Nref, I) < 0 return N, otherwise return 2 N
+ N must already be normalized in order to achieve the
+ desired result.
+
+ This is a non-componentwise GLSL built-in -
+ arguments may be scalar (float) or vector form (list).
+ Arguments are converted to BigFloat type.
+ Return value is a list of BigFloat values equal in count to the number of
+ items in I.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- I
+ args[1] -- N
+ """
+ I = _to_bigfloat(args[0])
+ N = _to_bigfloat(args[1])
+ dotNI = _dot_ref([N,I])[0]
+ ret = []
+ for Ncomponent, Icomponent in zip(N,I):
+ ret.append(Icomponent - bigfloat.BigFloat(2.0) * dotNI * Ncomponent)
+ return ret
+
+def _refract_ref(args):
+ """Refract reference function.
+ For the incident vector I and surface normal N, and the
+ ratio of indices of refraction eta, return the refraction
+ vector. The result is computed by
+ k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
+ if (k < 0.0)
+ return (0.0)
+ else
+ return eta * I - (eta * dot(N, I) + sqrt(k)) * N
+ The input parameters for the incident vector I and the
+ surface normal N must already be normalized to get the
+ desired results.
+
+ This is a non-componentwise GLSL built-in -
+ arguments may be scalar (float) or vector form (list).
+ Arguments are converted to BigFloat type.
+ Return value is a list of BigFloat values equal in count to the number of
+ items in I.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- I
+ args[1] -- N
+ args[2] -- eta
+ """
+ I = _to_bigfloat(args[0])
+ N = _to_bigfloat(args[1])
+ eta = _to_bigfloat(args[2])[0]
+ ret = []
+ k = bigfloat.BigFloat(1.0) - eta * eta * \
+ (bigfloat.BigFloat(1.0) - _dot_ref([N,I])[0] * _dot_ref([N,I])[0])
+ if k < 0.0:
+ for i in range(len(I)):
+ ret.append(bigfloat.BigFloat(0.0))
+ else:
+ Ntemp = []
+ Itemp = []
+ for Ncomponent, Icomponent in zip(N,I):
+ Ntemp = (eta * _dot_ref([N,I])[0] + bigfloat.sqrt(k)) * Ncomponent
+ Itemp = eta * Icomponent
+ ret.append(Itemp - Ntemp)
+ return ret
+
+def _vec_times_mat_ref(args):
+ """Matrix Multiplication (Vector * Matrix) reference function.
+ vec3 v, u;
+ mat3 m;
+ u = v * m;
+ u.x = dot(v, m[0]); // m[0] is the left column of m
+ u.y = dot(v, m[1]); // dot(a,b) is the inner (dot) product of a and b
+ u.z = dot(v, m[2]);
+
+ This is a non-componentwise GLSL built-in -
+ arguments are one vector (list) and one matrix (list (rows) of lists (cols))
+ ex. arg[0] = [a, b, c]; arg[1] = [[d, e, f], [g, h, i], [j, k, l]]; m[0] == [d, g, j]
+ Arguments are converted to BigFloat type.
+ Return value is a list of BigFloat values equal in count to the number of
+ items in v.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- v
+ args[1] -- m
+ """
+ v = args[0]
+ m = args[1]
+ m_type = glsl_type_of(m)
+ num_cols = m_type.num_cols
+ num_rows = m_type.num_rows
+ components = num_cols
+ ret = []
+ for i in range(components):
+ m_col = []
+ for j in range(num_rows):
+ m_col.append(m[j][i])
+ ret.append(_dot_ref([v,m_col])[0])
+ return ret
+
+def _mat_times_vec_ref(args):
+ """Matrix Multiplication (Matrix * Vector) reference function.
+ u = m * v;
+ u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
+ u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
+ u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
+
+ This is a non-componentwise GLSL built-in -
+ arguments are one matrix (list (rows) of lists (cols)) and one vector (list)
+ ex. arg[0] = [[d, e, f], [g, h, i], [j, k, l]]; m[0] == [d, g, j]; arg[1] = [a, b, c];
+ Arguments are converted to BigFloat type.
+ Return value is a list of BigFloat values equal in count to the number of
+ items in v.
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- m
+ args[1] -- v
+ """
+ m = args[0]
+ v = args[1]
+ m_type = glsl_type_of(m)
+ num_rows = m_type.num_rows
+ components = num_rows
+ ret = []
+ for i in range(components):
+ m_col = m[i]
+ ret.append(_dot_ref([v,m_col])[0])
+ return ret
+
+def _mat_times_mat_ref(args):
+ """Matrix Multiplication (Matrix * Matrix) reference function.
+ mat3 m, n, r;
+ r = m * n;
+ r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
+ r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
+ r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
+
+ r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
+ r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
+ r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
+
+ r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
+ r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
+ r[2].z = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;
+
+ This is a non-componentwise GLSL built-in -
+ arguments are two matrices (list (rows) of lists (cols))
+ ex. arg[0] = [[d, e, f], [g, h, i], [j, k, l]]; m[0] == [d, g, j]
+ Arguments are converted to BigFloat type.
+ Return value is a flattened matrix in the form of a list of BigFloat values
+ equal in count to the number of rows in matrix m times the number of
+ columns in matrix n, ie. [[r0c0, r0c1, r0c2], [r1c0, r1c1, r1c2], ... ].
+
+ arguments:
+ args -- list of all arguments
+ args[0] -- m
+ args[1] -- n
+ """
+ m = args[0]
+ n = args[1]
+ m_type = glsl_type_of(m)
+ n_type = glsl_type_of(n)
+ ret = []
+ for i in range(m_type.num_rows):
+ for j in range(n_type.num_cols):
+ n_col = []
+ for k in range(n_type.num_rows):
+ n_col.append(n[k][j])
+ ret.append(_dot_ref([m[i],n_col])[0])
+ return ret
+
+def _capture_error(precise, imprecise):
+ """Perform the legwork of calculating the difference in error of the high
+ precision and low precision runs. Decide whether this difference in error
+ is within allowable tolerances. The range of allowable tolerances is
+ subjective, as ARB_shader_precision (and GLSL spec as of v4.5) gives no
+ direct guidance for complex functions. Toronto, et. al. use quadrupled
+ error as a limit in "Practically Accurate Floating-Point Math," Computing
+ Now, Oct. 2014. Also use the difference in error and the value of one ulp
+ at the output to calculate the tolerance range in ulps for use by the
+ shader test, should this vector pass the badlands check.
+ """
+
+ errors = []
+ badlands = []
+ component_tolerances = []
+ with high_precision:
+ error = bigfloat.abs(precise - imprecise)
+ errors.append(error)
+ with low_precision:
+ ulpsz = _ulpsize(imprecise)
+ with high_precision:
+ badlands.append(error > ulpsz * allowed_error_scale)
+ component_tolerances.append(bigfloat.round(error / ulpsz))
+ return errors, badlands, component_tolerances
+
+def _analyze_ref_fn(fn, args):
+ """Many functions contain ill-conditioned regions referred to as "badlands"
+ (see Toronto, et. al., "Practically Accurate Floating-Point Math,"
+ Computing Now, Oct. 2014). Within these regions errors in the inputs are
+ magnified significantly, making the function impossible to test with any
+ reasonable accuracy. A complex function that operates on floating point
+ numbers has the potential to generate such error propagation even if the
+ inputs are exact floating point numbers, since intermediate results can be
+ generated with error. In order to identify and avoid these areas, we run
+ the function once at a lower precision and once at a higher precision, and
+ compare the outputs. Propagating errors will be greater at lower precision
+ and less at higher precision for a given set of function inputs, allowing
+ us to identify the badlands of the function.
+ """
+
+ errors = []
+ badlands = []
+ component_tolerances = []
+ with high_precision:
+ precise = fn(args)
+ with low_precision:
+ imprecise = fn(args)
+ for i, arg in enumerate(imprecise):
+ cur_errors, cur_badlands, cur_component_tolerances = _capture_error(precise[i], arg)
+ errors.extend(cur_errors)
+ badlands.extend(cur_badlands)
+ component_tolerances.extend(cur_component_tolerances)
+ return errors, badlands, component_tolerances
+
+
+# These three dicts (simple_fns, complex_fns and mult_fns) divide the names of
+# the glsl built-in operations into three mutually exclusive groups. They do
+# not need to cover all built-ins however, as a default tolerance is assigned
+# for names not appearing in these dicts.
+#
+# simple_fns are accompanied by a single blanket tolerance value used for all
+# calculations (these come from the EXT).
+#
+# complex_fns are accompanied by a reference to a function that can be used to
+# calculate a list of non-uniform tolerances.
+#
+# the two mult_fns (mult and assign-mult) are special in that they can act like
+# a simple function when multiplying floats or vec members together in a
+# componentwise manner, or they can act like complex functions when matrices
+# are involved and they switch to matrix multiplication rules.
+simple_fns = {'op-div': 2.5,
+ 'op-assign-div': 2.5,
+ 'pow': 16.0,
+ 'exp': 3.0,
+ 'exp2': 3.0,
+ 'log': 3.0,
+ 'log2': 3.0,
+ 'sqrt': 3.0,
+ 'inversesqrt': 2.0}
+
+complex_fns = {'mod': _mod_ref,
+ 'smoothstep': _smoothstep_ref,
+ 'mix': _mix_ref,
+ 'length': _length_ref,
+ 'distance': _distance_ref,
+ 'dot': _dot_ref,
+ 'cross': _cross_ref,
+ 'normalize': _normalize_ref,
+ 'faceforward': _faceforward_ref,
+ 'reflect': _reflect_ref,
+ 'refract': _refract_ref}
+
+mult_fns = {'op-mult': 0.0,
+ 'op-assign-mult': 0.0}
+
+# the complex functions listed here operate in a componentwise manner - the rest do not
+componentwise = ('mod', 'mix', 'smoothstep' )
+
+def _gen_tolerance(name, rettype, args):
+ """Return the tolerance that should be allowed for a function for the
+ test vector passed in. Return -1 for any vectors that would push the
+ tolerance outside of acceptable bounds
+ """
+ if name in simple_fns:
+ return simple_fns[name]
+ elif name in complex_fns:
+ if name in componentwise:
+ # for componentwise functions, call the reference function
+ # for each component (scalar components multiplex here)
+ # eg. for fn(float a, vec2 b, vec2 c) call:
+ # _fn_ref(a, b[0], c[0])
+ # _fn_ref(a, b[1], c[1])
+ # and then capture the results in a list
+ errors = []
+ badlands = []
+ component_tolerances = []
+ for component in range(rettype.num_cols * rettype.num_rows):
+ current_args = []
+ for arg in args:
+ # sanitize args so that all analyze operations can be performed on a list
+ sanitized_arg = _listify(arg)
+ current_args.append(sanitized_arg[component % len(sanitized_arg)])
+ cur_errors, cur_badlands, cur_component_tolerances = _analyze_ref_fn(complex_fns[name], current_args)
+ errors.extend(cur_errors)
+ badlands.extend(cur_badlands)
+ component_tolerances.extend(cur_component_tolerances)
+ else:
+ # for non-componentwise functions, all args must be passed in in a single run
+ # sanitize args so that all analyze operations can be performed on a list
+ current_args = map(_listify, args)
+ errors, badlands, component_tolerances = _analyze_ref_fn(complex_fns[name], current_args)
+ # results are in a list, and may have one or more members
+ return -1.0 if True in badlands else map(float, component_tolerances)
+ elif name in mult_fns:
+ x_type = glsl_type_of(args[0])
+ y_type = glsl_type_of(args[1])
+ # if matrix types are involved, the multiplication will be matrix multiplication
+ # and we will have to pass the args through a reference function
+ if x_type.is_vector and y_type.is_matrix:
+ mult_func = _vec_times_mat_ref
+ elif x_type.is_matrix and y_type.is_vector:
+ mult_func = _mat_times_vec_ref
+ elif x_type.is_matrix and y_type.is_matrix:
+ mult_func = _mat_times_mat_ref
+ else:
+ # float*float, float*vec, vec*float or vec*vec are all done as
+ # normal multiplication and share a single tolerance
+ return mult_fns[name]
+ # sanitize args so that all analyze operations can be performed on a list
+ errors, badlands, component_tolerances = _analyze_ref_fn(mult_func, _listify(args))
+ # results are in a list, and may have one or more members
+ return -1.0 if True in badlands else map(float, component_tolerances)
+ else:
+ # default for any operations without specified tolerances
+ return 0.0
+
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,
@@ -116,7 +691,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)
@@ -126,14 +701,14 @@ def shader_runner_format(values):
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
+ 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))])
if (signature.rettype.base_type == glsl_float and
@@ -155,7 +730,7 @@ def main():
with open(output_filename, 'w') as f:
f.write(template.render_unicode( signature=signature,
test_vectors=test_vectors,
- tolerances=tolerances,
+ tolerances=simple_fns,
invocation=invocation,
num_elements=num_elements,
indexers=indexers,
--
2.2.2
More information about the Piglit
mailing list