ext_gpu_shader4: add compiler tests for everything
[piglit.git] / generated_tests / gen_builtin_packing_tests.py
blob97d31f1f70cf5d5e9fe7fcb873e93d7fcb9f7030
1 # coding=utf-8
2 # Copyright (c) 2014 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 # SOFTWARE.
22 """This scripts generates tests for the GLSL packing functions, such as
23 packSnorm2x16.
25 In the test templates below, observe that the GLSL function's actual output is
26 compared against multiple expected outputs. Given an input and a
27 pack/unpackfunction, there exist multiple valid outputs because the GLSL specs
28 permit variation in the implementation of the function. The actual output is
29 dependent on the GLSL compiler's and hardware's choice of rounding mode (for
30 example, to even or to nearest) and handling of subnormal (also called
31 denormalized) floating point numbers.
33 """
35 from __future__ import print_function, division, absolute_import
36 import math
37 import optparse
38 import os
39 import sys
40 from collections import namedtuple
41 from math import copysign, fabs, fmod, frexp, isinf, isnan, modf
43 from numpy import int8, int16, uint8, uint16, uint32, float32
45 from templates import template_dir
46 from modules import utils
48 TEMPLATES = template_dir(os.path.basename(os.path.splitext(__file__)[0]))
50 # pylint: disable=bad-whitespace,line-too-long
51 TEMPLATE_TABLE = {
52 ("const", "p", "2x16"): TEMPLATES.get_template('const_pack.shader_test.mako'),
53 ("const", "p", "4x8"): TEMPLATES.get_template('const_pack.shader_test.mako'),
54 ("const", "u", "2x16"): TEMPLATES.get_template('const_unpack.shader_test.mako'),
55 ("const", "u", "4x8"): TEMPLATES.get_template('const_unpack.shader_test.mako'),
56 ("vs", "p", "2x16"): TEMPLATES.get_template('vs_pack.shader_test.mako'),
57 ("vs", "p", "4x8"): TEMPLATES.get_template('vs_pack.shader_test.mako'),
58 ("vs", "u", "2x16"): TEMPLATES.get_template('vs_unpack.shader_test.mako'),
59 ("vs", "u", "4x8"): TEMPLATES.get_template('vs_unpack.shader_test.mako'),
60 ("fs", "p", "2x16"): TEMPLATES.get_template('fs_pack.shader_test.mako'),
61 ("fs", "p", "4x8"): TEMPLATES.get_template('fs_pack.shader_test.mako'),
62 ("fs", "u", "2x16"): TEMPLATES.get_template('fs_unpack.shader_test.mako'),
63 ("fs", "u", "4x8"): TEMPLATES.get_template('fs_unpack.shader_test.mako'),
65 # pylint: enable=bad-whitespace,line-too-long
67 # TODO: all of the invalid names should be fixed (mostly one and two letter
68 # variable names), but there are lots of them, and they get in the way.
69 # TODO: Docstrings...
70 # pylint: disable=invalid-name,missing-docstring
72 class FuncOpts(object): # pylint: disable=too-few-public-methods
73 """Options that modify the evaluation of the GLSL pack/unpack functions.
75 Given an input and a pack/unpack function, there exist multiple valid
76 outputs because the GLSL specs permit variation in the implementation of
77 the function. The actual output is dependent on the GLSL compiler's and
78 hardware's choice of rounding mode (for example, to even or to nearest).
80 This class attempts to capture the permitted variation in rounding
81 behavior. To select a particular behavior, pass the appropriate enum to
82 the constructor, as described below.
84 Rounding mode
85 -------------
86 For some packing functions, the GLSL ES 3.00 specification's definition of
87 the function's behavior involves round(), whose behavior at
88 0.5 is an implementation detail. From section 8.3 of the spec:
89 The fraction 0.5 will round in a direction chosen by the
90 implementation, presumably the direction that is fastest.
92 The constructor parameter 'round_mode' selects the rounding behavior.
93 Valid values are:
94 - ROUND_TO_EVEN
95 - ROUND_TO_NEAREST
96 """
98 ROUND_TO_EVEN = 0
99 ROUND_TO_NEAREST = 1
101 def __init__(self, round_mode=ROUND_TO_EVEN):
102 if round_mode == FuncOpts.ROUND_TO_EVEN:
103 self.__round_func = round_to_even
104 elif round_mode == FuncOpts.ROUND_TO_NEAREST:
105 self.__round_func = round_to_nearest
106 else:
107 raise Exception('Must round to even or nearest.\n'
108 'round function: {}'.format(round_mode))
110 def round(self, x):
111 """Round a float according to the requested rounding mode."""
112 assert any(isinstance(x, T) for T in [float, float32])
114 # Drop the floating-point precision from 64 to 32 bits before
115 # rounding. The loss of precision may shift the float's fractional
116 # value to 0.5, which will affect the rounding.
117 x = float32(x)
118 return self.__round_func(x)
121 def clamp(x, min_, max_):
122 if x < min_:
123 return min_
124 elif x > max_:
125 return max_
126 else:
127 return x
130 def round_to_nearest(x):
131 # Get fractional and integral parts.
132 (f, i) = modf(x)
134 if fabs(f) < 0.5:
135 return i
136 else:
137 return i + copysign(1.0, x)
140 def round_to_even(x):
141 # Get fractional and integral parts.
142 (f, i) = modf(x)
144 if fabs(f) < 0.5:
145 return i
146 elif fabs(f) == 0.5:
147 return i + fmod(i, 2.0)
148 else:
149 return i + copysign(1.0, x)
152 def pack_2x16(pack_1x16_func, x, y, func_opts):
153 """Evaluate a GLSL pack2x16 function.
155 :param pack_1x16_func: the component-wise function of the GLSL pack2x16
156 function
157 :param x,y: each a float32
158 :return: a uint32
160 assert isinstance(x, float32)
161 assert isinstance(y, float32)
163 ux = pack_1x16_func(x, func_opts)
164 uy = pack_1x16_func(y, func_opts)
166 assert isinstance(ux, uint16)
167 assert isinstance(uy, uint16)
169 return uint32((uy << 16) | ux)
172 def pack_4x8(pack_1x8_func, x, y, z, w, func_opts):
173 # pylint: disable=too-many-arguments
174 """Evaluate a GLSL pack4x8 function.
176 :param pack_1x8_func: the component-wise function of the GLSL pack4x8
177 function
178 :param x,y,z,w: each a float32
179 :return: a uint32
181 assert isinstance(x, float32)
182 assert isinstance(y, float32)
183 assert isinstance(z, float32)
184 assert isinstance(w, float32)
186 ux = pack_1x8_func(x, func_opts)
187 uy = pack_1x8_func(y, func_opts)
188 uz = pack_1x8_func(z, func_opts)
189 uw = pack_1x8_func(w, func_opts)
191 assert isinstance(ux, uint8)
192 assert isinstance(uy, uint8)
193 assert isinstance(uz, uint8)
194 assert isinstance(uw, uint8)
196 return uint32((uw << 24) | (uz << 16) | (uy << 8) | ux)
199 def unpack_2x16(unpack_1x16_func, u, _):
200 """Evaluate a GLSL unpack2x16 function.
202 :param unpack_1x16_func: the component-wise function of the GLSL
203 unpack2x16 function
204 :param u: a uint32
205 :return: a 2-tuple of float32
207 assert isinstance(u, uint32)
209 ux = uint16(u & 0xffff)
210 uy = uint16(u >> 16)
212 x = unpack_1x16_func(ux)
213 y = unpack_1x16_func(uy)
215 assert isinstance(x, float32)
216 assert isinstance(y, float32)
218 return (x, y)
221 def unpack_4x8(unpack_1x8_func, u, _):
222 """Evaluate a GLSL unpack4x8 function.
224 :param unpack_1x8_func: the component-wise function of the GLSL
225 unpack4x8 function
226 :param u: a uint32
227 :return: a 4-tuple of float32
229 assert isinstance(u, uint32)
231 ux = uint8(u & 0xff)
232 uy = uint8((u >> 8) & 0xff)
233 uz = uint8((u >> 16) & 0xff)
234 uw = uint8((u >> 24) & 0xff)
236 x = unpack_1x8_func(ux)
237 y = unpack_1x8_func(uy)
238 z = unpack_1x8_func(uz)
239 w = unpack_1x8_func(uw)
241 assert isinstance(x, float32)
242 assert isinstance(y, float32)
243 assert isinstance(z, float32)
244 assert isinstance(w, float32)
246 return (x, y, z, w)
249 def pack_snorm_1x8(f32, func_opts):
250 """Component-wise function of packSnorm4x8."""
251 assert isinstance(f32, float32)
252 return uint8(int8(func_opts.round(clamp(f32, -1.0, +1.0) * 127.0)))
255 def pack_snorm_1x16(f32, func_opts):
256 """Component-wise function of packSnorm2x16."""
257 assert isinstance(f32, float32)
258 return uint16(int16(func_opts.round(clamp(f32, -1.0, +1.0) * 32767.0)))
261 def unpack_snorm_1x8(u8):
262 """Component-wise function of unpackSnorm4x8."""
263 assert isinstance(u8, uint8)
264 return float32(clamp(int8(u8) / 127.0, -1.0, +1.0))
267 def unpack_snorm_1x16(u16):
268 """Component-wise function of unpackSnorm2x16."""
269 assert isinstance(u16, uint16)
270 return float32(clamp(int16(u16) / 32767.0, -1.0, +1.0))
273 def pack_unorm_1x8(f32, func_opts):
274 """Component-wise function of packUnorm4x8."""
275 assert isinstance(f32, float32)
276 return uint8(func_opts.round(clamp(f32, 0.0, 1.0) * 255.0))
279 def pack_unorm_1x16(f32, func_opts):
280 """Component-wise function of packUnorm2x16."""
281 assert isinstance(f32, float32)
282 return uint16(func_opts.round(clamp(f32, 0.0, 1.0) * 65535.0))
285 def unpack_unorm_1x8(u8):
286 """Component-wise function of unpackUnorm4x8."""
287 assert isinstance(u8, uint8)
288 return float32(u8 / 255.0)
291 def unpack_unorm_1x16(u16):
292 """Component-wise function of unpackUnorm2x16."""
293 assert isinstance(u16, uint16)
294 return float32(u16 / 65535.0)
297 def pack_half_1x16(f32, func_opts):
298 """Component-wise function of packHalf2x16."""
299 assert isinstance(f32, float32)
301 # The bit layout of a float16 is:
303 # sign: 15
304 # exponent: 10:14
305 # mantissa: 0:9
307 # The sign, exponent, and mantissa determine its value by:
309 # if e = 0 and m = 0, then zero: (-1)^s * 0
310 # if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10
311 # if 0 < e < 31, then normal: (-1)^s * 2^(e - 15) * (1 + m / 2^10)
312 # if e = 31 and m = 0, then inf: (-1)^s * inf
313 # if e = 31 and m != 0, then nan
315 # where 0 <= m < 2^10.
317 # Some key boundary values of float16 are:
319 # min_normal16 = 2^(1 - 15) * (1 + 0 / 2^10)
320 # max_normal16 = 2^(30 - 15) * (1 + 1023 / 2^10)
322 # The maximum float16 step value is:
324 # max_step16 = 2^5
326 # Observe that each of the above boundary values lies in the range of
327 # normal float32 values. If we represent each of the above boundary values
328 # in the form returned by frexpf() for normal float32 values, 2^E
329 # * F where 0.5 <= F < 1, then:
331 # min_normal16 = 2^(-13) * 0.5
332 # max_normal16 = 2^16 * 0.99951171875
334 # The resultant float16's sign, exponent, and mantissa bits.
335 s = 0
336 e = 0
337 m = 0
339 # Calculate sign bit.
340 # Use copysign() to handle the case where x is -0.0.
341 if copysign(1.0, f32) < 0.0:
342 s = 1
344 # To reduce the number of cases in the if-tree below, decompose `abs(f32)`
345 # rather than `f32`.
346 (F, E) = frexp(fabs(f32))
348 # The output of frexp falls into three classes:
349 # - If f32 is NaN, then F is NaN .
350 # - If f32 is ±inf, then F is ±inf .
351 # - If f32 is ±0.0, then F is ±0.0 .
352 # - Otherwise, f32 = 2^E * F where 0.5 <= F < 1.0 .
354 # Since we decomposed `abs(f32)`, we only need be concerned with the
355 # positive cases.
356 if isnan(F):
357 # The resultant float16 is NaN.
358 e = 31
359 m = 1
360 elif isinf(F):
361 # The resultant float16 is infinite.
362 e = 31
363 m = 0
364 elif F == 0:
365 # f32 is zero, therefore the resultant float16 is zero.
366 e = 0
367 m = 0
368 elif E < -13:
369 # f32 lies in the range (0.0, min_normal16). Round f32 to a nearby
370 # float16 value. The resultant float16 will be either zero, subnormal,
371 # or normal.
372 e = 0
373 m = int(func_opts.round(2**(E + 24) * F))
374 elif E <= 16:
375 # f32 lies in the range [min_normal16, max_normal16 + max_step16).
376 # Round f32 to a nearby float16 value. The resultant float16 will be
377 # either normal or infinite.
378 e = int(E + 14)
379 m = int(func_opts.round(2**11 * F - 2**10))
380 else:
381 # f32 lies in the range [max_normal16 + max_step16, inf), which is
382 # outside the range of finite float16 values. The resultant float16 is
383 # infinite.
384 e = 31
385 m = 0
387 if m == 1024:
388 # f32 was rounded upwards into the range of the next exponent. This
389 # correctly handles the case where f32 should be rounded up to float16
390 # infinity.
391 e += 1
392 m = 0
394 assert s == 0 or s == 1
395 assert 0 <= e and e <= 31
396 assert 0 <= m and m <= 1023
398 return uint16((s << 15) | (e << 10) | m)
401 def unpack_half_1x16(u16):
402 """Component-wise function of unpackHalf2x16."""
403 assert isinstance(u16, uint16)
405 # The bit layout of a float16 is:
407 # sign: 15
408 # exponent: 10:14
409 # mantissa: 0:9
411 # The sign, exponent, and mantissa determine its value by:
413 # if e = 0 and m = 0, then zero: (-1)^s * 0
414 # if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10
415 # if 0 < e < 31, then normal: (-1)^s * 2^(e - 15) * (1 + m / 2^10)
416 # if e = 31 and m = 0, then inf: (-1)^s * inf
417 # if e = 31 and m != 0, then nan
419 # where 0 <= m < 2^10.
421 s = (u16 >> 15) & 0x1
422 e = (u16 >> 10) & 0x1f
423 m = u16 & 0x3ff
425 if s == 0:
426 sign = 1.0
427 else:
428 sign = -1.0
430 if e == 0:
431 return float32(sign * 2.0**(-14) * (m / 2.0**10))
432 elif 1 <= e and e <= 30:
433 return float32(sign * 2.0**(e - 15.0) * (1.0 + m / 2.0**10))
434 elif e == 31 and m == 0:
435 return float32(sign * float32("inf"))
436 elif e == 31 and m != 0:
437 return float32("NaN")
438 else:
439 raise Exception('invalid inputs')
441 # ----------------------------------------------------------------------------
442 # Inputs for GLSL functions
443 # ----------------------------------------------------------------------------
445 # This table maps GLSL pack/unpack function names to a sequence of inputs to
446 # the respective component-wise function. It contains four types of mappings:
447 # - name of a pack2x16 function to a sequence of float32
448 # - name of a pack4x8 function to a sequence of float32
449 # - name of a unpack2x16 function to a sequence of uint16
450 # - name of a unpack4x8 function to a sequence of uint8
451 full_input_table = dict()
453 # This table maps each GLSL pack/unpack function name to a subset of
454 # ``full_input_table[name]``.
456 # To sufficiently test some functions, we must test a fairly large set of
457 # component-wise inputs, so large that its cartesian product explodes. The
458 # test such functions, we test over the cartesian product of full_input_table
459 # and reduced_input_table. See make_inouts_for_pack_2x16.
461 reduced_input_table = dict()
464 def make_inputs_for_pack_snorm_2x16():
465 # The domain of packSnorm2x16 is [-inf, +inf]^2. The function clamps
466 # its input into the range [-1, +1]^2.
467 pos = (
468 0.0, # zero
469 0.1, # near zero
470 0.9, # slightly below the clamp boundary
471 1.0, # the clamp boundary
472 1.1, # slightly above the clamp boundary
473 float("+inf")
475 neg = tuple(reversed(tuple(-x for x in pos)))
476 return tuple(float32(x) for x in pos + neg)
478 full_input_table["packSnorm2x16"] = make_inputs_for_pack_snorm_2x16()
479 reduced_input_table["packSnorm2x16"] = None
481 full_input_table["packSnorm4x8"] = full_input_table["packSnorm2x16"]
483 # XXX: Perhaps there is a better choice of test inputs?
484 full_input_table["unpackSnorm2x16"] = tuple(uint16(u) for u in (
485 0, 1, 2, 3,
486 2**15 - 1,
487 2**15,
488 2**15 + 1,
489 2**16 - 1 # max uint16
492 # XXX: Perhaps there is a better choice of test inputs?
493 full_input_table["unpackSnorm4x8"] = tuple(uint8(u) for u in (
494 0, 1, 2, 3,
495 2**7 - 1,
496 2**7,
497 2**7 + 1,
498 2**8 - 1 # max uint8
501 full_input_table["packUnorm2x16"] = tuple(float32(x) for x in (
502 # The domain of packUnorm2x16 is [-inf, +inf]^2. The function clamps its
503 # input into the range [0, 1]^2.
505 "-inf",
506 -0.1, # slightly below the inner clamp boundary
507 -0.0, # infintesimally below the inner clamp boundary
508 +0.0, # the inner clamp boundary
509 +0.1, # slightly above the inner clamp boundary
510 +0.9, # slightly below the outer clamp boundary
511 +1.0, # the outer clamp boundary
512 +1.1, # slightly above the outer clamp boundary
513 "+inf"
516 reduced_input_table["packUnorm2x16"] = None
518 full_input_table["packUnorm4x8"] = full_input_table["packUnorm2x16"]
520 # XXX: Perhaps there is a better choice of test inputs?
521 full_input_table["unpackUnorm2x16"] = full_input_table["unpackSnorm2x16"]
522 full_input_table["unpackUnorm4x8"] = full_input_table["unpackSnorm4x8"]
525 def make_inputs_for_pack_half_2x16():
526 # The domain of packHalf2x16 is ([-inf, +inf] + {NaN})^2. The function
527 # does not clamp its input.
529 # We test both -0.0 and +0.0 in order to stress the implementation's
530 # handling of zero.
532 subnormal_min = 2.0**(-14) * (1.0 / 2.0**10)
533 normal_min = 2.0**(-14) * (1.0 + 0.0 / 2.0**10)
534 normal_max = 2.0**15 * (1.0 + 1023.0 / 2.0**10)
535 min_step = 2.0**(-24)
536 max_step = 2.0**5
538 pos = tuple(float32(x) for x in (
539 # Inputs that result in 0.0 .
540 0.0,
541 0.0 + 0.25 * min_step,
543 # A thorny input...
545 # if round_to_even:
546 # f16 := 0.0
547 # elif round_to_nearest:
548 # f16 := subnormal_min
550 0.0 + 0.50 * min_step,
552 # Inputs that result in a subnormal
553 # float16.
555 0.0 + 0.75 * min_step,
556 subnormal_min + 0.00 * min_step,
557 subnormal_min + 0.25 * min_step,
558 subnormal_min + 0.50 * min_step,
559 subnormal_min + 0.75 * min_step,
560 subnormal_min + 1.00 * min_step,
561 subnormal_min + 1.25 * min_step,
562 subnormal_min + 1.50 * min_step,
563 subnormal_min + 1.75 * min_step,
564 subnormal_min + 2.00 * min_step,
566 normal_min - 2.00 * min_step,
567 normal_min - 1.75 * min_step,
568 normal_min - 1.50 * min_step,
569 normal_min - 1.25 * min_step,
570 normal_min - 1.00 * min_step,
571 normal_min - 0.75 * min_step,
573 # Inputs that result in a normal float16.
575 normal_min - 0.50 * min_step,
576 normal_min - 0.25 * min_step,
577 normal_min + 0.00 * min_step,
578 normal_min + 0.25 * min_step,
579 normal_min + 0.50 * min_step,
580 normal_min + 0.75 * min_step,
581 normal_min + 1.00 * min_step,
582 normal_min + 1.25 * min_step,
583 normal_min + 1.50 * min_step,
584 normal_min + 1.75 * min_step,
585 normal_min + 2.00 * min_step,
587 2.0 * normal_min + 0.50 * min_step,
588 2.0 * normal_min + 0.75 * min_step,
589 2.0 * normal_min + 1.00 * min_step,
591 0.5,
592 1.0,
593 1.5,
595 normal_max - 2.00 * max_step,
596 normal_max - 1.75 * max_step,
597 normal_max - 1.50 * max_step,
598 normal_max - 1.25 * max_step,
599 normal_max - 1.00 * max_step,
600 normal_max - 0.75 * max_step,
601 normal_max - 0.50 * max_step,
602 normal_max - 0.25 * max_step,
603 normal_max + 0.00 * max_step,
604 normal_max + 0.25 * max_step,
606 # Inputs that result in infinity.
608 normal_max + 0.50 * max_step,
609 normal_max + 0.75 * max_step,
610 normal_max + 1.00 * max_step,
611 normal_max + 2.00 * max_step,
613 "+inf"))
615 neg = tuple(reversed([-x for x in pos]))
616 return neg + pos
618 full_input_table["packHalf2x16"] = make_inputs_for_pack_half_2x16()
620 reduced_input_table["packHalf2x16"] = tuple(float32(x) for x in (
621 "-inf",
622 -2.0,
623 -1.0,
624 -0.0,
625 +0.0,
626 +1.0,
627 +2.0,
628 "+inf"
632 def make_inputs_for_unpack_half_2x16():
633 # For each of the two classes of float16 values, subnormal and normalized,
634 # below are listed the exponent and mantissa of the class's boundary
635 # values and some values slightly inside the bounds.
636 # pylint: disable=bad-whitespace
637 bounds = (
638 (0, 0), # zero
639 (0, 1), # subnormal_min
640 (0, 2), # subnormal_min + min_step
641 (0, 1022), # subnormal_max - min_step
642 (0, 1023), # subnormal_max
643 (1, 0), # normal_min
644 (1, 1), # normal_min + min_step
645 (30, 1022), # normal_max - max_step
646 (30, 1023), # normal_max
647 (31, 0) # inf
649 # pylint: enable=bad-whitespace
651 def make_uint16(s, e, m):
652 return uint16((s << 15) | (e << 10) | m)
654 pos = tuple(make_uint16(0, e, m) for (e, m) in bounds)
655 neg = tuple(make_uint16(1, e, m) for (e, m) in reversed(bounds))
656 return neg + pos
658 full_input_table["unpackHalf2x16"] = make_inputs_for_unpack_half_2x16()
660 # ----------------------------------------------------------------------------
661 # Expected outputs for GLSL functions
662 # ----------------------------------------------------------------------------
664 # For a given input to a GLSL function, InOutTuple lists all valid outputs.
666 # There are multiple types of InOutTuple, described below. In each
667 # description, the numerical types actually refer to strings that represent
668 # a GLSL literal of that type.
670 # - That for a pack2x16 function: the input is a 2-tuple of float32 and each
671 # output is a uint32. For example, ``InOutTuple(input=("0.0", "0.0"),
672 # valid_outputs=("0u", "0u", "0u"))``.
674 # - That for a unpack2x16 function: the input is a uint32 and each output is
675 # a 2-tuple of float32. For example, ``InOutTuple(input="0x80000000u",
676 # valid_outputs=(("0.0", "-0.0"),))``.
678 InOutTuple = namedtuple("InOutTuple", ("input", "valid_outputs"))
681 def glsl_literal(x):
682 """Convert the given number to a string that represents a GLSL literal.
684 :param x: a uint32 or float32
686 if isinstance(x, uint32):
687 return "{0}u".format(uint32(x))
688 elif isinstance(x, float32):
689 if math.isnan(x):
690 # GLSL ES 3.00 and GLSL 4.10 do not require implementations to
691 # support NaN, so we do not test it.
692 raise Exception('NaN is not tested.')
693 elif math.isinf(x):
694 # GLSL ES 3.00 lacks a literal for infinity. However, ±1.0e256
695 # suffices because it lies sufficientlyoutside the range of finite
696 # float32 values.
698 # From page 31 of the GLSL ES 3.00 spec:
700 # If the value of the floating point number is too large (small)
701 # to be stored as a single precision value, it is converted to
702 # positive (negative) infinity.
704 return repr(copysign(1.0e256, x))
705 elif x == 0 and copysign(1.0, x) == -1.0:
706 # Workaround for numpy-1.7.0, in which repr(float32(-0.0)) does
707 # not return a float literal.
708 # See https://github.com/numpy/numpy/issues/2935 .
709 return "-0.0"
710 else:
711 return repr(x)
712 else:
713 raise Exception('Unsupported GLSL litteral')
716 def make_inouts_for_pack_2x16(pack_1x16_func,
717 all_float32_inputs,
718 reduced_inputs=None):
719 """Determine valid outputs for a given GLSL pack2x16 function.
721 If the reduced_float32_inputs parameter is None, then it is assumed to be
722 the same as all_float32_inputs.
724 The set of vec2 inputs constructed by this function is the union of
725 cartesian products:
726 (all_float32_inputs x reduced_inputs)
727 + (reduced_inputs x all_float32_inputs)
729 :param pack_1x16_func: the component-wise function of the pack2x16
730 function
731 :param float32_inputs: a sequence of inputs to pack_1x16_func
732 :return: a sequence of InOutTuple
734 inout_seq = []
736 func_opt_seq = (FuncOpts(FuncOpts.ROUND_TO_EVEN),
737 FuncOpts(FuncOpts.ROUND_TO_NEAREST))
739 if reduced_inputs is None:
740 reduced_inputs = all_float32_inputs
742 def add_vec2_input(x, y):
743 assert isinstance(x, float32)
744 assert isinstance(y, float32)
746 valid_outputs = []
747 for func_opts in func_opt_seq:
748 u32 = pack_2x16(pack_1x16_func, x, y, func_opts)
749 assert isinstance(u32, uint32)
750 valid_outputs.append(glsl_literal(u32))
752 inout_seq.append(
753 InOutTuple(input=(glsl_literal(x), glsl_literal(y)),
754 valid_outputs=valid_outputs))
756 for y in reduced_inputs:
757 for x in all_float32_inputs:
758 add_vec2_input(x, y)
759 add_vec2_input(y, x)
761 return inout_seq
764 def make_inouts_for_pack_4x8(pack_1x8_func, float32_inputs):
765 """Determine valid outputs for a given GLSL pack4x8 function.
767 :param pack_1x8_func: the component-wise function of the pack4x8
768 function
769 :param float32_inputs: a sequence of inputs to pack_1x8_func
770 :return: a sequence of InOutTuple
772 inout_seq = []
774 func_opt_seq = (FuncOpts(FuncOpts.ROUND_TO_EVEN),
775 FuncOpts(FuncOpts.ROUND_TO_NEAREST))
777 for y in float32_inputs:
778 for x in float32_inputs:
779 assert isinstance(x, float32)
781 valid_outputs_0 = []
782 valid_outputs_1 = []
783 for func_opts in func_opt_seq:
784 u32_0 = pack_4x8(pack_1x8_func, x, y, x, y, func_opts)
785 u32_1 = pack_4x8(pack_1x8_func, x, x, y, y, func_opts)
786 assert isinstance(u32_0, uint32)
787 assert isinstance(u32_1, uint32)
788 valid_outputs_0.append(glsl_literal(u32_0))
789 valid_outputs_1.append(glsl_literal(u32_1))
791 inout_seq.append(
792 InOutTuple(input=(glsl_literal(x), glsl_literal(y),
793 glsl_literal(x), glsl_literal(y)),
794 valid_outputs=valid_outputs_0))
795 inout_seq.append(
796 InOutTuple(input=(glsl_literal(x), glsl_literal(x),
797 glsl_literal(y), glsl_literal(y)),
798 valid_outputs=valid_outputs_1))
799 return inout_seq
802 def make_inouts_for_unpack_2x16(unpack_1x16_func, uint16_inputs):
803 """Determine expected outputs of a given GLSL unpack2x16 function.
805 :param unpack_1x16_func: the component-wise function of the unpack2x16
806 function
807 :param uint16_inputs: a sequence of inputs to unpack_1x16_func
808 :return: a sequence of InOutTuple
810 inout_seq = []
811 func_opts = FuncOpts()
813 for y in uint16_inputs:
814 for x in uint16_inputs:
815 assert isinstance(x, uint16)
816 u32 = uint32((y << 16) | x)
817 vec2 = unpack_2x16(unpack_1x16_func, u32, func_opts)
818 assert isinstance(vec2[0], float32)
819 assert isinstance(vec2[1], float32)
820 inout_seq.append(
821 InOutTuple(input=glsl_literal(u32),
822 valid_outputs=[(glsl_literal(vec2[0]),
823 glsl_literal(vec2[1]))]))
825 return inout_seq
828 def make_inouts_for_unpack_4x8(unpack_1x8_func, uint8_inputs):
829 """Determine expected outputs of a given GLSL unpack4x8 function.
831 :param unpack_1x8_func: the component-wise function of the unpack4x8
832 function
833 :param uint8_inputs: a sequence of inputs to unpack_1x8_func
834 :return: a sequence of InOutTuple
836 inout_seq = []
838 func_opts = FuncOpts()
840 for y in uint8_inputs:
841 for x in uint8_inputs:
842 assert isinstance(x, uint8)
843 u32_0 = uint32((y << 24) | (x << 16) | (y << 8) | x)
844 u32_1 = uint32((y << 24) | (y << 16) | (x << 8) | x)
846 valid_outputs_0 = []
847 valid_outputs_1 = []
848 vec4_0 = unpack_4x8(unpack_1x8_func, u32_0, func_opts)
849 vec4_1 = unpack_4x8(unpack_1x8_func, u32_1, func_opts)
850 assert isinstance(vec4_0[0], float32)
851 assert isinstance(vec4_0[1], float32)
852 assert isinstance(vec4_0[2], float32)
853 assert isinstance(vec4_0[3], float32)
854 assert isinstance(vec4_1[0], float32)
855 assert isinstance(vec4_1[1], float32)
856 assert isinstance(vec4_1[2], float32)
857 assert isinstance(vec4_1[3], float32)
858 valid_outputs_0.append((glsl_literal(vec4_0[0]),
859 glsl_literal(vec4_0[1]),
860 glsl_literal(vec4_0[2]),
861 glsl_literal(vec4_0[3])))
862 valid_outputs_1.append((glsl_literal(vec4_1[0]),
863 glsl_literal(vec4_1[1]),
864 glsl_literal(vec4_1[2]),
865 glsl_literal(vec4_1[3])))
867 inout_seq.append(InOutTuple(input=glsl_literal(u32_0),
868 valid_outputs=valid_outputs_0))
869 inout_seq.append(InOutTuple(input=glsl_literal(u32_1),
870 valid_outputs=valid_outputs_1))
872 return inout_seq
874 # This table maps GLSL pack/unpack function names to the precision of their
875 # return type.
876 result_precision_table = {
877 "packSnorm2x16": "highp",
878 "packSnorm4x8": "highp",
879 "packUnorm2x16": "highp",
880 "packUnorm4x8": "highp",
881 "packHalf2x16": "highp",
882 "unpackSnorm2x16": "highp",
883 "unpackSnorm4x8": "highp",
884 "unpackUnorm2x16": "highp",
885 "unpackUnorm4x8": "highp",
886 "unpackHalf2x16": "mediump"
889 # This table maps GLSL pack/unpack function names to a sequence of InOutTuple.
890 inout_table = {
891 "packSnorm2x16": make_inouts_for_pack_2x16(
892 pack_snorm_1x16, full_input_table["packSnorm2x16"],
893 reduced_input_table["packSnorm2x16"]),
894 "packSnorm4x8": make_inouts_for_pack_4x8(
895 pack_snorm_1x8, full_input_table["packSnorm4x8"]),
896 "packUnorm2x16": make_inouts_for_pack_2x16(
897 pack_unorm_1x16, full_input_table["packUnorm2x16"],
898 reduced_input_table["packUnorm2x16"]),
899 "packUnorm4x8": make_inouts_for_pack_4x8(
900 pack_unorm_1x8, full_input_table["packUnorm4x8"]),
901 "packHalf2x16": make_inouts_for_pack_2x16(
902 pack_half_1x16, full_input_table["packHalf2x16"],
903 reduced_input_table["packHalf2x16"]),
904 "unpackSnorm2x16": make_inouts_for_unpack_2x16(
905 unpack_snorm_1x16, full_input_table["unpackSnorm2x16"]),
906 "unpackSnorm4x8": make_inouts_for_unpack_4x8(
907 unpack_snorm_1x8, full_input_table["unpackSnorm4x8"]),
908 "unpackUnorm2x16": make_inouts_for_unpack_2x16(
909 unpack_unorm_1x16, full_input_table["unpackUnorm2x16"]),
910 "unpackUnorm4x8": make_inouts_for_unpack_4x8(
911 unpack_unorm_1x8, full_input_table["unpackUnorm4x8"]),
912 "unpackHalf2x16": make_inouts_for_unpack_2x16(
913 unpack_half_1x16, full_input_table["unpackHalf2x16"])
917 # ----------------------------------------------------------------------------
918 # Generate test files
919 # ----------------------------------------------------------------------------
922 FuncInfo = namedtuple('FuncInfo', ['name', 'dimension', 'result_precision',
923 'inout_seq', 'num_valid_outputs',
924 'vector_type', 'requirements', 'exact'])
926 def func_info(name, requirements):
927 """Factory function for information for a GLSL pack/unpack function.
929 Properties
930 ----------
931 - name: Name of the GLSL function, such as "packSnorm2x16".
933 - dimension: Dimension of the GLSL function, such as "2x16".
935 - result_precision: Precision of the GLSL function's return type, such as
936 "highp".
938 - inout_seq: A sequence of InOutTuple. The generated test file will test
939 all inputs listed in the sequence.
941 - num_valid_outputs: The number of valid outputs for each input of
942 self.inout_seq. (We assume that each input has the same number of valid
943 outputs).
945 - vector_type: The type of the GLSL function's parameter or return value.
946 E.g., vec4 for a 4x8 function and vec2 for a 2x16 function.
948 - requirements: A set of API/extension requirments to be listed in the
949 .shader_test's [requires] section.
951 - exact: Whether the generated results must be exact (e.g., 0.0 and 1.0
952 should always be converted exactly).
956 if name.endswith("2x16"):
957 dimension = "2x16"
958 vector_type = "vec2"
959 elif name.endswith("4x8"):
960 dimension = "4x8"
961 vector_type = "vec4"
962 else:
963 raise Exception('Invalid pack type {}'.format(name))
965 inout_seq = inout_table[name]
967 return FuncInfo(name, dimension, result_precision_table[name],
968 inout_seq, len(inout_seq[0].valid_outputs), vector_type,
969 requirements, name.endswith("unpackHalf2x16"))
972 class ShaderTest(object):
973 """A .shader_test file."""
975 @staticmethod
976 def all_tests():
977 requirements = "GLSL >= 1.30\nGL_ARB_shading_language_packing"
978 ARB_shading_language_packing_funcs = (
979 func_info("packSnorm2x16", requirements),
980 func_info("packSnorm4x8", requirements),
981 func_info("packUnorm2x16", requirements),
982 func_info("packUnorm4x8", requirements),
983 func_info("packHalf2x16", requirements),
984 func_info("unpackSnorm2x16", requirements),
985 func_info("unpackSnorm4x8", requirements),
986 func_info("unpackUnorm2x16", requirements),
987 func_info("unpackUnorm4x8", requirements),
988 func_info("unpackHalf2x16", requirements)
991 requirements = "GL ES >= 3.0\nGLSL ES >= 3.00"
992 glsl_es_300_funcs = (
993 func_info("packSnorm2x16", requirements),
994 func_info("packUnorm2x16", requirements),
995 func_info("packHalf2x16", requirements),
996 func_info("unpackSnorm2x16", requirements),
997 func_info("unpackUnorm2x16", requirements),
998 func_info("unpackHalf2x16", requirements)
1001 execution_stages = ("const", "vs", "fs")
1003 for s in execution_stages:
1004 for f in glsl_es_300_funcs:
1005 yield ShaderTest(f, s, "glsl-es-3.00")
1006 for f in ARB_shading_language_packing_funcs:
1007 yield ShaderTest(f, s, "ARB_shading_language_packing")
1009 def __init__(self, funcinfo, execution_stage, api):
1010 assert isinstance(funcinfo, FuncInfo)
1011 assert execution_stage in ("const", "vs", "fs")
1012 assert api in ("glsl-es-3.00", "ARB_shading_language_packing")
1014 self.__template = TEMPLATE_TABLE[(execution_stage,
1015 funcinfo.name[0],
1016 funcinfo.dimension)]
1017 self.__func_info = funcinfo
1018 self.__filename = os.path.join(
1019 "spec",
1020 api.lower(),
1021 "execution",
1022 "built-in-functions",
1023 "{0}-{1}.shader_test".format(execution_stage, funcinfo.name))
1025 @property
1026 def filename(self):
1027 return self.__filename
1029 def write_file(self):
1030 dirname = os.path.dirname(self.filename)
1031 utils.safe_makedirs(dirname)
1033 with open(self.filename, "w") as f:
1034 f.write(self.__template.render_unicode(func=self.__func_info))
1037 def main():
1038 parser = optparse.OptionParser(
1039 description="Generate shader tests that test the built-inpacking "
1040 "functions",
1041 usage="usage: %prog [-h] [--names-only]")
1042 parser.add_option(
1043 '--names-only',
1044 dest='names_only',
1045 action='store_true',
1046 help="Don't output files, just generate a list of filenames to stdout")
1048 (options, args) = parser.parse_args()
1050 if len(args) != 0:
1051 # User gave extra args.
1052 parser.print_help()
1053 sys.exit(1)
1055 for test in ShaderTest.all_tests():
1056 print(test.filename)
1058 # Some test files take a long time to generate, so provide status
1059 # updates to the user immediately.
1060 sys.stdout.flush()
1062 if not options.names_only:
1063 test.write_file()
1065 if __name__ == '__main__':
1066 main()