gen_builtin_uniform_tests.py 31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# 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.

24
# Generate a set of shader_runner tests for every overloaded version
25 26 27
# of every built-in function, based on the test vectors computed by
# builtin_function.py.
#
28 29 30 31
# In each set of generated tests, one test exercises the built-in
# function in each type of shader (vertex, geometry, and fragment).
# In all cases, the inputs to the built-in function come from
# uniforms, so that the effectiveness of the test won't be
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
# circumvented by constant folding in the GLSL compiler.
#
# The tests operate by invoking the built-in function in the
# appropriate shader, applying a scale and offset so that the expected
# values are in the range [0.25, 0.75], and then outputting the result
# as a solid rgba color, which is then checked using shader_runner's
# "probe rgba" command.
#
# For built-in functions whose result type is a matrix, the test
# checks one column at a time.
#
# 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.

47
from __future__ import print_function, division, absolute_import
48 49 50 51 52 53
from builtin_function import *
import abc
import numpy
import optparse
import os
import os.path
54 55

from six.moves import range
56

57 58
from modules import utils

59

60 61 62 63 64 65 66 67 68 69 70 71 72
def compute_offset_and_scale(test_vectors):
    """Compute scale and offset values such that for each result in
    test_vectors, (result - offset) * scale is in the range [0.25,
    0.75], and scale is less than or equal to 1.0.  These values are
    used to transform the test vectors so that their outputs can be
    stored in gl_FragColor without overflow.
    """
    low = min(numpy.min(tv.result) for tv in test_vectors)
    hi = max(numpy.max(tv.result) for tv in test_vectors)
    span = hi - low
    center = (hi + low)/2.0
    span *= 2.0
    if span < 1.0:
73
        span = 1.0
74 75 76 77 78 79 80 81 82 83 84 85
    offset = center - span/2.0
    scale = 1.0/span
    return offset, scale


def shader_runner_format(values):
    """Format the given values for use in a shader_runner "uniform" or
    "probe rgba" command.  Bools are converted to 0's and 1's, and
    values are separated by spaces.
    """
    transformed_values = []
    for value in values:
86 87 88 89
        if isinstance(value, (bool, np.bool_)):
            transformed_values.append(int(value))
        else:
            transformed_values.append(value)
90
    return ' '.join(repr(x) for x in transformed_values)
91 92 93 94 95 96 97 98 99


def shader_runner_type(glsl_type):
    """Return the appropriate type name necessary for binding a
    uniform of the given type using shader_runner's "uniform" command.
    Boolean values and vectors are converted to ints, and square
    matrices are written in "matNxN" form.
    """
    if glsl_type.base_type == glsl_bool:
100 101 102 103
        if glsl_type.is_scalar:
            return 'int'
        else:
            return 'ivec{0}'.format(glsl_type.num_rows)
104
    elif glsl_type.is_matrix:
105
        return 'mat{0}x{1}'.format(glsl_type.num_cols, glsl_type.num_rows)
106
    else:
107
        return str(glsl_type)
108 109


110 111 112 113 114 115 116
class Comparator(object):
    """Base class which abstracts how we compare expected and actual
    values.
    """
    __metaclass__ = abc.ABCMeta

    def make_additional_declarations(self):
117 118 119 120
        """Return additional declarations, if any, that are needed in
        the shader program.
        """
        return ''
121 122

    @abc.abstractmethod
123
    def make_result_handler(self, invocation, output_var):
124 125
        """Return the shader code that is needed to produce the result
        and store it in output_var.
126

127 128 129
        invocation is the GLSL code to compute the output of the
        built-in function.
        """
130 131

    @abc.abstractmethod
132 133
    def draw_test(self, test_vector, draw_command):
        """Return the shader_runner test code that is needed to run a
134 135
        single test vector.
        """
136

137 138 139 140
    @abc.abstractmethod
    def result_vector(self, test_vector):
        """Return the expected result color as a list of floats."""

141
    def testname_suffix(self):
142 143 144
        """Return a string to be used as a suffix on the test name to
        distinguish it from tests using other comparators."""
        return ''
145 146 147


class BoolComparator(Comparator):
148 149 150 151 152 153 154 155
    """Comparator that tests functions returning bools and bvecs by
    converting them to floats.

    This comparator causes code to be generated in the following form:

        rettype result = func(args);
        output_var = vec4(result, 0.0, ...);
    """
156
    def __init__(self, signature):
157 158 159
        assert not signature.rettype.is_matrix
        self.__signature = signature
        self.__padding = 4 - signature.rettype.num_rows
160

161
    def make_result_handler(self, invocation, output_var):
162 163 164 165 166
        statements = '  {0} result = {1};\n'.format(
            self.__signature.rettype, invocation)
        statements += '  {0} = vec4(result{1});\n'.format(
            output_var, ', 0.0' * self.__padding)
        return statements
167 168

    def convert_to_float(self, value):
169 170 171 172 173 174 175
        """Convert the given vector or scalar value to a list of
        floats representing the expected color produced by the test.
        """
        value = value*1.0  # convert bools to floats
        value = column_major_values(value)
        value += [0.0] * self.__padding
        return value
176

177 178 179 180 181
    def draw_test(self, test_vector, draw_command):
        return draw_command

    def result_vector(self, test_vector):
        return self.convert_to_float(test_vector.result)
182 183


184 185 186 187 188 189 190 191 192 193 194 195
class BoolIfComparator(Comparator):
    """Comparator that tests functions returning bools by evaluating
    them inside an if statement.

    This comparator causes code to be generated in the following form:

        if (func(args))
          output_var = vec4(1.0, 1.0, 0.0, 1.0);
        else
          output_var = vecp(0.0, 0.0, 1.0, 1.0);
    """
    def __init__(self, signature):
196 197
        assert signature.rettype == glsl_bool
        self.__padding = 4 - signature.rettype.num_rows
198 199

    def make_result_handler(self, invocation, output_var):
200 201 202 203 204 205 206
        statements = '  if({0})\n'.format(invocation)
        statements += '    {0} = vec4(1.0, 1.0, 0.0, 1.0);\n'.format(
            output_var)
        statements += '  else\n'
        statements += '    {0} = vec4(0.0, 0.0, 1.0, 1.0);\n'.format(
            output_var)
        return statements
207 208

    def convert_to_float(self, value):
209 210 211 212 213 214 215
        """Convert the given vector or scalar value to a list of
        floats representing the expected color produced by the test.
        """
        if value:
            return [1.0, 1.0, 0.0, 1.0]
        else:
            return [0.0, 0.0, 1.0, 1.0]
216

217 218 219 220 221
    def draw_test(self, test_vector, draw_command):
        return draw_command

    def result_vector(self, test_vector):
        return self.convert_to_float(test_vector.result)
222 223

    def testname_suffix(self):
224
        return '-using-if'
225 226


227
class IntComparator(Comparator):
228 229 230 231 232 233 234 235 236
    """Comparator that tests functions returning ints or ivecs using a
    strict equality test.

    This comparator causes code to be generated in the following form:

        rettype result = func(args);
        output_var = result == expected ? vec4(0.0, 1.0, 0.0, 1.0)
                                        : vec4(1.0, 0.0, 0.0, 1.0);
    """
237
    def __init__(self, signature):
238
        self.__signature = signature
239 240

    def make_additional_declarations(self):
241
        return 'uniform {0} expected;\n'.format(self.__signature.rettype)
242

243
    def make_result_handler(self, invocation, output_var):
244 245 246 247 248 249 250
        statements = '  {0} result = {1};\n'.format(
            self.__signature.rettype, invocation)
        statements += '  {v} = {cond} ? {green} : {red};\n'.format(
            v=output_var, cond='result == expected',
            green='vec4(0.0, 1.0, 0.0, 1.0)',
            red='vec4(1.0, 0.0, 0.0, 1.0)')
        return statements
251

252
    def draw_test(self, test_vector, draw_command):
253 254 255
        test = 'uniform {0} expected {1}\n'.format(
            shader_runner_type(self.__signature.rettype),
            shader_runner_format(column_major_values(test_vector.result)))
256
        test += draw_command
257
        return test
258

259 260 261
    def result_vector(self, test_vector):
        return [0.0, 1.0, 0.0, 1.0]

262

263
class FloatComparator(Comparator):
264
    """Comparator that tests functions returning floats or vecs using a
265 266 267 268 269 270 271 272
    strict equality test.

    This comparator causes code to be generated in the following form:

        rettype result = func(args);
        output_var = distance(result, expected) <= tolerance
                     ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);
    """
273
    def __init__(self, signature):
274
        self.__signature = signature
275 276

    def make_additional_declarations(self):
277 278 279
        decls = 'uniform float tolerance;\n'
        decls += 'uniform {0} expected;\n'.format(self.__signature.rettype)
        return decls
280 281

    def make_indexers(self):
282 283 284 285 286 287 288 289
        """Build a list of strings which index into every possible
        value of the result.  For example, if the result is a vec2,
        then build the indexers ['[0]', '[1]'].
        """
        if self.__signature.rettype.num_cols == 1:
            col_indexers = ['']
        else:
            col_indexers = ['[{0}]'.format(i)
290
                            for i in range(self.__signature.rettype.num_cols)]
291 292 293 294
        if self.__signature.rettype.num_rows == 1:
            row_indexers = ['']
        else:
            row_indexers = ['[{0}]'.format(i)
295
                            for i in range(self.__signature.rettype.num_rows)]
296 297 298
        return [col_indexer + row_indexer
                for col_indexer in col_indexers
                for row_indexer in row_indexers]
299

300
    def make_result_handler(self, invocation, output_var):
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
        statements = '  {0} result = {1};\n'.format(
            self.__signature.rettype, invocation)
        # Can't use distance when testing itself, or when the rettype
        # is a matrix.
        if self.__signature.name == 'distance' or \
                self.__signature.rettype.is_matrix:
            statements += '  {0} residual = result - expected;\n'.format(
                self.__signature.rettype)
            statements += '  float error_sq = {0};\n'.format(
                ' + '.join(
                    'residual{0} * residual{0}'.format(indexer)
                    for indexer in self.make_indexers()))
            condition = 'error_sq <= tolerance * tolerance'
        else:
            condition = 'distance(result, expected) <= tolerance'
        statements += '  {v} = {cond} ? {green} : {red};\n'.format(
            v=output_var, cond=condition, green='vec4(0.0, 1.0, 0.0, 1.0)',
            red='vec4(1.0, 0.0, 0.0, 1.0)')
        return statements
320

321
    def draw_test(self, test_vector, draw_command):
322 323 324 325 326
        test = 'uniform {0} expected {1}\n'.format(
            shader_runner_type(self.__signature.rettype),
            shader_runner_format(column_major_values(test_vector.result)))
        test += 'uniform float tolerance {0}\n'.format(
            shader_runner_format([test_vector.tolerance]))
327
        test += draw_command
328
        return test
329

330 331 332
    def result_vector(self, test_vector):
        return [0.0, 1.0, 0.0, 1.0]

333

334 335 336
class ShaderTest(object):
    """Class used to build a test of a single built-in.  This is an
    abstract base class--derived types should override test_prefix(),
337 338
    make_vertex_shader(), make_fragment_shader(), and other functions
    if necessary.
339 340 341
    """
    __metaclass__ = abc.ABCMeta

342
    def __init__(self, signature, test_vectors, use_if):
343 344 345 346 347 348 349 350 351 352 353 354 355
        """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).

        If use_if is True, then the generated test checks the result
        by using it in an if statement--this only works for builtins
        returning bool.
        """
        self._signature = signature
        self._test_vectors = test_vectors
356 357 358 359 360 361 362 363 364 365 366 367 368 369

        # Size of the rectangles drawn by the test.
        self.rect_width = 4
        self.rect_height = 4
        # shader_runner currently defaults to a 250x250 window.  We
        # could reduce window size to cut test time, but there are
        # platform-dependent limits we haven't really characterized
        # (smaller on Linux than Windows, but still present in some
        # window managers).
        self.win_width = 250
        self.win_height = 250
        self.tests_per_row = (self.win_width // self.rect_width)
        self.test_rows = (self.win_height // self.rect_height)

370 371 372 373 374 375 376 377
        if use_if:
            self._comparator = BoolIfComparator(signature)
        elif signature.rettype.base_type == glsl_bool:
            self._comparator = BoolComparator(signature)
        elif signature.rettype.base_type == glsl_float:
            self._comparator = FloatComparator(signature)
        elif signature.rettype.base_type in (glsl_int, glsl_uint):
            self._comparator = IntComparator(signature)
378 379
        elif signature.rettype.base_type in (glsl_int64_t, glsl_uint64_t):
            self._comparator = IntComparator(signature)
380 381
        else:
            raise Exception('Unexpected rettype {0}'.format(signature.rettype))
382 383

    def glsl_version(self):
384
        return self._signature.version_introduced
385

386 387 388
    def draw_command(self, test_num):
        x = (test_num % self.tests_per_row) * self.rect_width
        y = (test_num // self.tests_per_row) * self.rect_height
389
        assert(y + self.rect_height <= self.win_height)
390 391 392
        return 'draw rect ortho {0} {1} {2} {3}\n'.format(x, y,
                                                          self.rect_width,
                                                          self.rect_height)
393

394
    def probe_command(self, test_num, probe_vector):
395 396 397 398 399 400
        return 'probe rect rgba ({0}, {1}, {2}, {3}) ({4}, {5}, {6}, {7})\n'.format(
            (test_num % self.tests_per_row) * self.rect_width,
            (test_num // self.tests_per_row) * self.rect_height,
            self.rect_width,
            self.rect_height,
            probe_vector[0], probe_vector[1], probe_vector[2], probe_vector[3])
401

402 403 404 405 406 407
    def extensions(self):
        ext = []
        if self._signature.extension:
            ext.append(self._signature.extension)
        return ext

408
    def make_additional_requirements(self):
409 410 411 412
        """Return a string that should be included in the test's
        [require] section.
        """
        return ''
413

414 415
    @abc.abstractmethod
    def test_prefix(self):
416 417 418 419
        """Return the prefix that should be used in the test file name
        to identify the type of test, e.g. "vs" for a vertex shader
        test.
        """
420 421

    def make_vertex_shader(self):
422 423 424 425 426 427
        """Return the vertex shader for this test (or None if this
        test doesn't require a vertex shader).  No need to
        reimplement this function in classes that don't use vertex
        shaders.
        """
        return None
428

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    def make_tess_ctrl_shader(self):
        """Return the tessellation control shader for this test
        (or None if this test doesn't require a geometry shader).
        No need to reimplement this function in classes that don't
        use geometry shaders.
        """
        return None

    def make_tess_eval_shader(self):
        """Return the tessellation evaluation shader for this test
        (or None if this test doesn't require a geometry shader).
        No need to reimplement this function in classes that don't
        use geometry shaders.
        """
        return None

445
    def make_geometry_shader(self):
446 447 448 449 450 451
        """Return the geometry shader for this test (or None if this
        test doesn't require a geometry shader).  No need to
        reimplement this function in classes that don't use geometry
        shaders.
        """
        return None
452 453

    def make_geometry_layout(self):
454 455 456 457 458 459
        """Return the geometry layout for this test (or None if this
        test doesn't require a geometry layout section).  No need to
        reimplement this function in classes that don't use geometry
        shaders.
        """
        return None
460

461
    def make_fragment_shader(self):
462 463 464 465 466 467
        """Return the fragment shader for this test (or None if this
        test doesn't require a fragment shader).  No need to
        reimplement this function in classes that don't use fragment
        shaders.
        """
        return None
468

469 470 471 472 473 474 475
    def make_compute_shader(self):
        """Return the compute shader for this test (or None if this test
        doesn't require a compute shader).  No need to reimplement
        this function in classes that don't use compute shaders.
        """
        return None

476 477 478 479 480 481 482
    def needs_probe_per_draw(self):
        """Returns whether the test needs the probe to be immediately after each

        draw call.
        """
        return False

483
    def make_test_shader(self, additional_declarations, prefix_statements,
484 485 486 487 488 489 490 491 492 493
                         output_var, suffix_statements):
        """Generate the shader code necessary to test the built-in.
        additional_declarations is a string containing any
        declarations that need to be before the main() function of the
        shader.  prefix_statements is a string containing any
        additional statements than need to be inside the main()
        function of the shader, before the built-in function is
        called.  output_var is the variable that the result of the
        built-in function should be assigned to, after conversion to a
        vec4.  suffix_statements is a string containing any additional
494
        statements that need to be inside the main() function of the
495 496
        shader, after the built-in function is called.
        """
497
        shader = ''
498 499
        for ext in self.extensions():
            shader += '#extension GL_{0} : require\n'.format(ext)
500
        shader += additional_declarations
501
        for i in range(len(self._signature.argtypes)):
502 503 504 505 506 507 508 509 510
            shader += 'uniform {0} arg{1};\n'.format(
                self._signature.argtypes[i], i)
        shader += self._comparator.make_additional_declarations()
        shader += '\n'
        shader += 'void main()\n'
        shader += '{\n'
        shader += prefix_statements
        invocation = self._signature.template.format(
            *['arg{0}'.format(i)
511
              for i in range(len(self._signature.argtypes))])
512 513 514 515
        shader += self._comparator.make_result_handler(invocation, output_var)
        shader += suffix_statements
        shader += '}\n'
        return shader
516

517 518 519 520 521
    def make_test_init(self):
        """Generate initialization for the test.
        """
        return ''

522
    def make_test(self):
523 524 525
        """Make the complete shader_runner test file, and return it as
        a string.
        """
526
        test = self.make_test_init()
527
        for test_num, test_vector in enumerate(self._test_vectors):
528
            for i in range(len(test_vector.arguments)):
529 530 531 532
                test += 'uniform {0} arg{1} {2}\n'.format(
                    shader_runner_type(self._signature.argtypes[i]),
                    i, shader_runner_format(
                        column_major_values(test_vector.arguments[i])))
533
            test += self._comparator.draw_test(test_vector,
534 535 536 537 538 539 540 541 542
                                               self.draw_command(test_num))
            if self.needs_probe_per_draw():
                result_color = self._comparator.result_vector(test_vector)
                test += self.probe_command(test_num, result_color)

        if not self.needs_probe_per_draw():
            for test_num, test_vector in enumerate(self._test_vectors):
                result_color = self._comparator.result_vector(test_vector)
                test += self.probe_command(test_num, result_color)
543
        return test
544 545

    def filename(self):
546 547
        argtype_names = '-'.join(
            str(argtype) for argtype in self._signature.argtypes)
548 549
        if self.extensions():
            subdir = self.extensions()[0].lower()
550 551
        else:
            subdir = 'glsl-{0:1.2f}'.format(float(self.glsl_version()) / 100)
552
        return os.path.join(
553
            'spec', subdir, 'execution', 'built-in-functions',
554 555 556
            '{0}-{1}-{2}{3}.shader_test'.format(
                self.test_prefix(), self._signature.name, argtype_names,
                self._comparator.testname_suffix()))
557 558

    def generate_shader_test(self):
559 560 561 562
        """Generate the test and write it to the output file."""
        shader_test = '[require]\n'
        shader_test += 'GLSL >= {0:1.2f}\n'.format(
            float(self.glsl_version()) / 100)
563 564
        for extension in self.extensions():
            shader_test += 'GL_{}\n'.format(extension)
565 566
        shader_test += self.make_additional_requirements()
        shader_test += '\n'
567 568 569 570 571
        vs = self.make_vertex_shader()
        if vs:
            shader_test += '[vertex shader]\n'
            shader_test += vs
            shader_test += '\n'
572 573 574 575 576 577 578 579 580 581
        tcs = self.make_tess_ctrl_shader()
        if tcs:
            shader_test += '[tessellation control shader]\n'
            shader_test += tcs
            shader_test += '\n'
        tes = self.make_tess_eval_shader()
        if tes:
            shader_test += '[tessellation evaluation shader]\n'
            shader_test += tes
            shader_test += '\n'
582 583 584 585 586 587 588 589 590 591
        gs = self.make_geometry_shader()
        if gs:
            shader_test += '[geometry shader]\n'
            shader_test += gs
            shader_test += '\n'
        gl = self.make_geometry_layout()
        if gl:
            shader_test += '[geometry layout]\n'
            shader_test += gl
            shader_test += '\n'
592 593 594 595 596
        fs = self.make_fragment_shader()
        if fs:
            shader_test += '[fragment shader]\n'
            shader_test += fs
            shader_test += '\n'
597 598 599 600 601
        cs = self.make_compute_shader()
        if cs:
            shader_test += '[compute shader]\n'
            shader_test += cs
            shader_test += '\n'
602
        shader_test += '[test]\n'
603 604
        shader_test += 'clear color 0.0 0.0 1.0 0.0\n'
        shader_test += 'clear\n'
605 606 607
        shader_test += self.make_test()
        filename = self.filename()
        dirname = os.path.dirname(filename)
608
        utils.safe_makedirs(dirname)
609 610
        with open(filename, 'w') as f:
            f.write(shader_test)
611 612 613 614 615 616 617


class VertexShaderTest(ShaderTest):
    """Derived class for tests that exercise the built-in in a vertex
    shader.
    """
    def test_prefix(self):
618
        return 'vs'
619 620

    def make_vertex_shader(self):
621
        if self.glsl_version() >= 140:
622
            return self.make_test_shader(
623
                'in vec4 piglit_vertex;\n' +
624
                'out vec4 color;\n',
625
                '  gl_Position = piglit_vertex;\n',
626
                'color', '')
627 628 629 630
        else:
            return self.make_test_shader(
                'varying vec4 color;\n',
                '  gl_Position = gl_Vertex;\n',
631 632 633
                'color', '')

    def make_fragment_shader(self):
634
        shader = '''varying vec4 color;
635 636 637 638 639 640

void main()
{
  gl_FragColor = color;
}
'''
641
        return shader
642 643


644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
class TessellationShaderTest(ShaderTest):
    """Abstract class for tests that exercise the built-in in
    tessellation shaders.
    """

    def glsl_version(self):
        return max(150, ShaderTest.glsl_version(self))

    def make_additional_requirements(self):
        return 'GL_ARB_tessellation_shader'

    def extensions(self):
        ext = []
        if self._signature.extension:
            ext.append(self._signature.extension)
        ext.append("ARB_tessellation_shader")
        return ext

    def draw_command(self, test_num):
        x = (test_num % self.tests_per_row) * self.rect_width
        y = (test_num // self.tests_per_row) * self.rect_height
665
        assert(y + self.rect_height <= self.win_height)
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
        return 'draw rect ortho patch {0} {1} {2} {3}\n'.format(x, y,
                                                                self.rect_width,
                                                                self.rect_height)

    def make_vertex_shader(self):
        shader = \
"""in vec4 piglit_vertex;
out vec4 vertex_to_tcs;

void main()
{
     vertex_to_tcs = piglit_vertex;
}
"""
        return shader

    def make_fragment_shader(self):
        shader = \
"""in vec4 color_to_fs;

void main()
{
  gl_FragColor = color_to_fs;
}
"""
        return shader


class TessCtrlShaderTest(TessellationShaderTest):
    """Derived class for tests that exercise the built-in in a
    tessellation control shader.
    """

    def test_prefix(self):
        return 'tcs'

    def make_tess_ctrl_shader(self):
        additional_declarations = \
"""layout(vertices = 4) out;
in vec4 vertex_to_tcs[];
out vec4 vertex_to_tes[];
patch out vec4 color_to_tes;
"""
        body = \
"""  vertex_to_tes[gl_InvocationID] = vertex_to_tcs[gl_InvocationID];
  color_to_tes = tmp_color;
  gl_TessLevelOuter = float[4](1.0, 1.0, 1.0, 1.0);
  gl_TessLevelInner = float[2](1.0, 1.0);
"""
        shader = self.make_test_shader(
            additional_declarations,
            '  vec4 tmp_color;\n',
            'tmp_color',
            body)
        return shader

    def make_tess_eval_shader(self):
        shader = \
"""#extension GL_ARB_tessellation_shader : require
layout(quads) in;

in vec4 vertex_to_tes[];
patch in vec4 color_to_tes;
out vec4 color_to_fs;
void main() {
  gl_Position = mix(mix(vertex_to_tes[0], vertex_to_tes[1], gl_TessCoord.x),
                    mix(vertex_to_tes[2], vertex_to_tes[3], gl_TessCoord.x),
                    gl_TessCoord.y);
  color_to_fs = color_to_tes;
}
"""
        return shader


740 741 742 743 744
class GeometryShaderTest(ShaderTest):
    """Derived class for tests that exercise the built-in in a
    geometry shader.
    """
    def test_prefix(self):
745
        return 'gs'
746

747 748
    def glsl_version(self):
        return max(150, ShaderTest.glsl_version(self))
749 750

    def make_vertex_shader(self):
751
        shader = ''
752
        shader += "in vec4 piglit_vertex;\n"
753
        shader += "out vec4 vertex_to_gs;\n"
754

755 756
        shader += "void main()\n"
        shader += "{\n"
757
        shader += "     vertex_to_gs = piglit_vertex;\n"
758 759
        shader += "}\n"

760
        return shader
761 762

    def make_geometry_shader(self):
763 764 765 766 767 768
        additional_declarations = ''
        additional_declarations += 'layout(triangles) in;\n'
        additional_declarations \
            += 'layout(triangle_strip, max_vertices = 3) out;\n'
        additional_declarations += 'in vec4 vertex_to_gs[3];\n'
        additional_declarations += 'out vec4 color;\n'
769 770 771 772
        return self.make_test_shader(
            additional_declarations,
            '  vec4 tmp_color;\n',
            'tmp_color',
773 774
            '  for (int i = 0; i < 3; i++) {\n'
            '    gl_Position = vertex_to_gs[i];\n'
775 776 777
            '    color = tmp_color;\n'
            '    EmitVertex();\n'
            '  }\n')
778

779
    def make_fragment_shader(self):
780
        shader = '''varying vec4 color;
781 782 783 784 785 786

void main()
{
  gl_FragColor = color;
}
'''
787
        return shader
788 789 790 791 792 793 794


class FragmentShaderTest(ShaderTest):
    """Derived class for tests that exercise the built-in in a
    fragment shader.
    """
    def test_prefix(self):
795
        return 'fs'
796 797

    def make_vertex_shader(self):
798
        shader = ""
799
        if self.glsl_version() >= 140:
800
            shader += "in vec4 piglit_vertex;\n"
801

802
        shader += "void main()\n"
803
        shader += "{\n"
804
        if self.glsl_version() >= 140:
805
            shader += "        gl_Position = piglit_vertex;\n"
806
        else:
807
            shader += "        gl_Position = gl_Vertex;\n"
808 809
        shader += "}\n"

810
        return shader
811 812

    def make_fragment_shader(self):
813
        return self.make_test_shader('', '', 'gl_FragColor', '')
814 815


816 817 818 819 820 821 822 823 824 825 826 827
class ComputeShaderTest(ShaderTest):
    """Derived class for tests that exercise the built-in in a
    compute shader.
    """
    def test_prefix(self):
        return 'cs'

    def glsl_version(self):
        return max(430, ShaderTest.glsl_version(self))

    def make_compute_shader(self):
        additional_declarations = 'writeonly uniform image2D tex;\n'
828 829 830
        num_tests = len(self._test_vectors)
        layout_tmpl = 'layout(local_size_x = {0}) in;\n'
        additional_declarations += layout_tmpl.format(num_tests)
831 832 833 834 835 836 837 838 839
        return self.make_test_shader(
            additional_declarations,
            '  vec4 tmp_color;\n',
            'tmp_color',
            '  ivec2 coord = ivec2(gl_GlobalInvocationID.xy);\n'
            '  imageStore(tex, coord, tmp_color);\n')

    def make_test_init(self):
        return '''uniform int tex 0
840
texture rgbw 0 ({0}, 1) GL_RGBA8
841
image texture 0 GL_RGBA8
842
fb tex 2d 0
843
'''.format(len(self._test_vectors))
844 845


846
    def draw_command(self, test_num):
847 848
        return 'compute 1 1 1\n'

849 850 851 852 853 854 855 856 857 858 859
    def probe_command(self, test_num, probe_vector):
        # Note: shader_runner uses a 250x250 window so we must
        # ensure that test_num <= 250.
        return 'probe rgb {0} 0 {1} {2} {3} {4}\n'.format(test_num % 250,
                                                          probe_vector[0],
                                                          probe_vector[1],
                                                          probe_vector[2],
                                                          probe_vector[3])
    def needs_probe_per_draw(self):
        return True

860
def all_tests():
861
    for use_if in [False, True]:
862 863 864 865
        for signature, test_vectors in sorted(test_suite.items()):
            if use_if and signature.rettype != glsl_bool:
                continue
            yield VertexShaderTest(signature, test_vectors, use_if)
866
            yield TessCtrlShaderTest(signature, test_vectors, use_if)
867 868
            yield GeometryShaderTest(signature, test_vectors, use_if)
            yield FragmentShaderTest(signature, test_vectors, use_if)
869
            yield ComputeShaderTest(signature, test_vectors, use_if)
870 871 872 873 874 875 876


def main():
    desc = 'Generate shader tests that test built-in functions using uniforms'
    usage = 'usage: %prog [-h] [--names-only]'
    parser = optparse.OptionParser(description=desc, usage=usage)
    parser.add_option(
877 878 879 880
        '--names-only',
        dest='names_only',
        action='store_true',
        help="Don't output files, just generate a list of filenames to stdout")
881 882
    options, args = parser.parse_args()
    for test in all_tests():
883 884
        if not options.names_only:
            test.generate_shader_test()
885
        print(test.filename())
886 887 888 889


if __name__ == '__main__':
    main()