[Piglit] [PATCH 4/4] Add constant array size builtin tests.
Paul Berry
stereotype441 at gmail.com
Mon Aug 8 14:15:07 PDT 2011
This patch adds a new set of generated tests which parallels those
introduced by commt cccc419e (Add comprehensive tests of builtin
functions with uniform input.)
The new tests validate the correctness of built-in functions when used
to compute array sizes. The tests work by creating arrays whose size
is 1 if the result of applying the built-in function is within
tolerance of the correct answer, and -1 if not. Since negative array
sizes are prohibited, any improperly computed values will generate a
compile error.
Though it may seem absurd to compute an array size based on the result
of calling a built-in function, it's worth testing for two reasons:
(1) It is explicitly allowed in GLSL versions since 1.20.
(2) Since array sizes need to be computed at compile time, this is a
convenient way of testing that the GLSL compiler properly implements
constant folding of built-in functions. Indeed, the original
motivation for these tests was to validate bug fixes to Mesa's
constant folding logic.
Here is an example of one of the generated tests (this test is
exp2-vec2.vert):
/* [config]
* expect_result: pass
* glsl_version: 1.20
* [end config]
*
* Check that the following test vectors are constant folded correctly:
* exp2(vec2(-2.0, -0.66666669)) => vec2(0.25, 0.62996054)
* exp2(vec2(0.66666669, 2.0)) => vec2(1.587401, 4.0)
*/
void main()
{
float[distance(exp2(vec2(-2.0, -0.66666669)), vec2(0.25, 0.62996054)) <= 6.7775386e-06 ? 1 : -1] array0;
float[distance(exp2(vec2(0.66666669, 2.0)), vec2(1.587401, 4.0)) <= 4.3034684e-05 ? 1 : -1] array1;
gl_Position = vec4(array0.length() + array1.length());
}
---
generated_tests/CMakeLists.txt | 7 +-
generated_tests/gen_constant_array_size_tests.py | 231 ++++++++++++++++++++++
tests/all.tests | 3 +
3 files changed, 240 insertions(+), 1 deletions(-)
create mode 100644 generated_tests/gen_constant_array_size_tests.py
diff --git a/generated_tests/CMakeLists.txt b/generated_tests/CMakeLists.txt
index 6772813..ac3cb95 100644
--- a/generated_tests/CMakeLists.txt
+++ b/generated_tests/CMakeLists.txt
@@ -41,8 +41,13 @@ piglit_make_generated_tests(
gen-builtin-uniform-tests
gen_builtin_uniform_tests.py
builtin_function.py)
+piglit_make_generated_tests(
+ gen-constant-array-size-tests
+ gen_constant_array_size_tests.py
+ builtin_function.py)
# Add a "gen-tests" target that can be used to generate all the
# tests without doing any other compilation.
add_custom_target(gen-tests ALL
- DEPENDS gen-builtin-uniform-tests)
+ DEPENDS gen-builtin-uniform-tests
+ gen-constant-array-size-tests)
diff --git a/generated_tests/gen_constant_array_size_tests.py b/generated_tests/gen_constant_array_size_tests.py
new file mode 100644
index 0000000..8258866
--- /dev/null
+++ b/generated_tests/gen_constant_array_size_tests.py
@@ -0,0 +1,231 @@
+# coding=utf-8
+#
+# Copyright © 2011 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 (including the next
+# paragraph) 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.
+
+# Generate a pair of glsl parser tests for every overloaded version of
+# every built-in function, which test that the built-in functions are
+# handled properly when applied to constant arguments inside an array
+# size declaration.
+#
+# In each pair of generated tests, one test exercises the built-in
+# function in vertex shaders, and the other exercises it in fragment
+# shaders.
+#
+# This program outputs, to stdout, the name of each file it generates.
+# With the optional argument --names-only, it only outputs the names
+# of the files; it doesn't generate them.
+
+from builtin_function import *
+import abc
+import optparse
+import os
+import os.path
+
+
+
+class ParserTest(object):
+ """Class used to build a test of a single built-in. This is an
+ abstract base class--derived types should override test_suffix()
+ and output_var().
+ """
+
+ def __init__(self, signature, test_vectors):
+ """Prepare to build a test for a single built-in. signature
+ is the signature of the built-in (a key from the
+ builtin_function.test_suite dict), and test_vectors is the
+ list of test vectors for testing the given builtin (the
+ corresponding value from the builtin_function.test_suite
+ dict).
+ """
+ self.__signature = signature
+ self.__test_vectors = test_vectors
+
+ def glsl_version(self):
+ if self.__signature.version_introduced < '1.20':
+ # Before version 1.20, built-in function invocations
+ # weren't allowed in constant expressions. So even if
+ # this built-in was introduced prior to 1.20, test it in
+ # version 1.20.
+ return '1.20'
+ else:
+ return self.__signature.version_introduced
+
+ def version_directive(self):
+ return '#version {0}\n'.format(self.glsl_version().replace('.', ''))
+
+ @abc.abstractmethod
+ def test_suffix(self):
+ """Return the suffix that should be used in the test file name
+ to identify the type of shader, e.g. "vert" for a vertex
+ shader test.
+ """
+
+ def make_condition(self, test_vector):
+ """Generate a GLSL constant expression that should evaluate to
+ true if the GLSL compiler's constant evaluation produces the
+ correct result for the given test vector, and false if not.
+ """
+ funcall = '{0}({1})'.format(
+ self.__signature.name, ', '.join(
+ glsl_constant(x) for x in test_vector.arguments))
+ if self.__signature.rettype.base_type == glsl_float:
+ # Test floating-point values within tolerance
+ if self.__signature.name == 'distance':
+ # Don't use the distance() function to test itself.
+ return '{0} <= {1} && {1} <= {2}'.format(
+ test_vector.result - test_vector.tolerance,
+ funcall,
+ test_vector.result + test_vector.tolerance)
+ elif self.__signature.rettype.is_matrix:
+ # We can't apply distance() to matrices. So apply it
+ # to each column and root-sum-square the results. It
+ # is safe to use pow() here because its behavior is
+ # verified in the pow() tests.
+ terms = []
+ for col in xrange(self.__signature.rettype.num_cols):
+ terms.append('pow(distance({0}[{1}], {2}), 2)'.format(
+ funcall, col,
+ glsl_constant(test_vector.result[:,col])))
+ rss_distance = ' + '.join(terms)
+ sq_tolerance = test_vector.tolerance * test_vector.tolerance
+ return '{0} <= {1}'.format(
+ rss_distance, glsl_constant(sq_tolerance))
+ else:
+ return 'distance({0}, {1}) <= {2}'.format(
+ funcall, glsl_constant(test_vector.result),
+ glsl_constant(test_vector.tolerance))
+ else:
+ # Test non-floating point values exactly
+ assert not self.__signature.rettype.is_matrix
+ if self.__signature.name == 'equal':
+ # Don't use the equal() function to test itself.
+ assert self.__signature.rettype.is_vector
+ terms = []
+ for row in xrange(self.__signature.rettype.num_rows):
+ terms.append('{0}[{1}] == {2}'.format(
+ funcall, row,
+ glsl_constant(test_vector.result[row])))
+ return ' && '.join(terms)
+ elif self.__signature.rettype.is_vector:
+ return 'all(equal({0}, {1}))'.format(
+ funcall, glsl_constant(test_vector.result))
+ else:
+ return '{0} == {1}'.format(
+ funcall, glsl_constant(test_vector.result))
+
+ def make_shader(self):
+ """Generate the shader code necessary to test the built-in."""
+ shader = self.version_directive()
+ shader += '\n'
+ shader += 'void main()\n'
+ shader += '{\n'
+ lengths = []
+ for i, test_vector in enumerate(self.__test_vectors):
+ shader += ' float[{0} ? 1 : -1] array{1};\n'.format(
+ self.make_condition(test_vector), i)
+ lengths.append('array{0}.length()'.format(i))
+ shader += ' {0} = vec4({1});\n'.format(
+ self.output_var(), ' + '.join(lengths))
+ shader += '}\n'
+ return shader
+
+ def filename(self):
+ argtype_names = '-'.join(
+ str(argtype) for argtype in self.__signature.argtypes)
+ return os.path.join(
+ 'spec', 'glsl-{0}'.format(self.glsl_version()),
+ 'compiler', 'built-in-functions',
+ '{0}-{1}.{2}'.format(
+ self.__signature.name, argtype_names, self.test_suffix()))
+
+ def generate_parser_test(self):
+ """Generate the test and write it to the output file."""
+ parser_test = '/* [config]\n'
+ parser_test += ' * expect_result: pass\n'
+ parser_test += ' * glsl_version: {0}\n'.format(self.glsl_version())
+ parser_test += ' * [end config]\n'
+ parser_test += ' *\n'
+ parser_test += ' * Check that the following test vectors are constant folded correctly:\n'
+ for test_vector in self.__test_vectors:
+ parser_test += ' * {0}({1}) => {2}\n'.format(
+ self.__signature.name,
+ ', '.join(glsl_constant(arg) for arg in test_vector.arguments),
+ glsl_constant(test_vector.result))
+ parser_test += ' */\n'
+ parser_test += self.make_shader()
+ filename = self.filename()
+ dirname = os.path.dirname(filename)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ with open(filename, 'w') as f:
+ f.write(parser_test)
+
+
+
+class VertexParserTest(ParserTest):
+ """Derived class for tests that exercise the built-in in a vertex
+ shader.
+ """
+ def test_suffix(self):
+ return 'vert'
+
+ def output_var(self):
+ return 'gl_Position'
+
+
+
+class FragmentParserTest(ParserTest):
+ """Derived class for tests that exercise the built-in in a fagment
+ shader.
+ """
+ def test_suffix(self):
+ return 'frag'
+
+ def output_var(self):
+ return 'gl_FragColor'
+
+
+
+def all_tests():
+ for signature, test_vectors in test_suite.items():
+ yield VertexParserTest(signature, test_vectors)
+ yield FragmentParserTest(signature, test_vectors)
+
+
+
+def main():
+ desc = 'Generate shader tests that test built-in functions using constant array sizes'
+ usage = 'usage: %prog [-h] [--names-only]'
+ parser = optparse.OptionParser(description=desc, usage=usage)
+ parser.add_option(
+ '--names-only', dest='names_only', action='store_true',
+ help="Don't output files, just generate a list of filenames to stdout")
+ options, args = parser.parse_args()
+ for test in all_tests():
+ if not options.names_only:
+ test.generate_parser_test()
+ print test.filename()
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/all.tests b/tests/all.tests
index 62deb87..3c2b237 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -745,6 +745,9 @@ spec['glsl-1.20'] = Group()
import_glsl_parser_tests(spec['glsl-1.20'],
os.path.join(os.path.dirname(__file__), 'spec', 'glsl-1.20'),
['preprocessor', 'compiler'])
+import_glsl_parser_tests(spec['glsl-1.20'],
+ os.path.join(generatedTestDir, 'spec', 'glsl-1.20'),
+ ['compiler'])
spec['glsl-1.20']['execution'] = Group()
add_shader_test_dir(spec['glsl-1.20']['execution'],
os.path.join(os.path.dirname(__file__), 'spec', 'glsl-1.20', 'execution'),
--
1.7.6
More information about the Piglit
mailing list