gl-1.1: Test that unused normal array in ff shader doesn't affect other arrays
[piglit.git] / generated_tests / gen_builtin_packing_tests.py
blob9715902a58f7a5cad5d15662e4b79ce1d33c14c4
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 import math
36 import optparse
37 import os
38 import sys
39 from collections import namedtuple
40 from math import copysign, fabs, fmod, frexp, isinf, isnan, modf
42 from numpy import int8, int16, uint8, uint16, uint32, float32
44 from templates import template_dir
45 from modules import utils
47 TEMPLATES = template_dir(os.path.basename(os.path.splitext(__file__)[0]))
49 # pylint: disable=bad-whitespace,line-too-long
50 TEMPLATE_TABLE = {
51 ("const", "p", "2x16"): TEMPLATES.get_template('const_pack.shader_test.mako'),
52 ("const", "p", "4x8"): TEMPLATES.get_template('const_pack.shader_test.mako'),
53 ("const", "u", "2x16"): TEMPLATES.get_template('const_unpack.shader_test.mako'),
54 ("const", "u", "4x8"): TEMPLATES.get_template('const_unpack.shader_test.mako'),
55 ("vs", "p", "2x16"): TEMPLATES.get_template('vs_pack.shader_test.mako'),
56 ("vs", "p", "4x8"): TEMPLATES.get_template('vs_pack.shader_test.mako'),
57 ("vs", "u", "2x16"): TEMPLATES.get_template('vs_unpack.shader_test.mako'),
58 ("vs", "u", "4x8"): TEMPLATES.get_template('vs_unpack.shader_test.mako'),
59 ("fs", "p", "2x16"): TEMPLATES.get_template('fs_pack.shader_test.mako'),
60 ("fs", "p", "4x8"): TEMPLATES.get_template('fs_pack.shader_test.mako'),
61 ("fs", "u", "2x16"): TEMPLATES.get_template('fs_unpack.shader_test.mako'),
62 ("fs", "u", "4x8"): TEMPLATES.get_template('fs_unpack.shader_test.mako'),
64 # pylint: enable=bad-whitespace,line-too-long
66 # TODO: all of the invalid names should be fixed (mostly one and two letter
67 # variable names), but there are lots of them, and they get in the way.
68 # TODO: Docstrings...
69 # pylint: disable=invalid-name,missing-docstring
71 class FuncOpts(object): # pylint: disable=too-few-public-methods
72 """Options that modify the evaluation of the GLSL pack/unpack functions.
74 Given an input and a pack/unpack function, there exist multiple valid
75 outputs because the GLSL specs permit variation in the implementation of
76 the function. The actual output is dependent on the GLSL compiler's and
77 hardware's choice of rounding mode (for example, to even or to nearest).
79 This class attempts to capture the permitted variation in rounding
80 behavior. To select a particular behavior, pass the appropriate enum to
81 the constructor, as described below.
83 Rounding mode
84 -------------
85 For some packing functions, the GLSL ES 3.00 specification's definition of
86 the function's behavior involves round(), whose behavior at
87 0.5 is an implementation detail. From section 8.3 of the spec:
88 The fraction 0.5 will round in a direction chosen by the
89 implementation, presumably the direction that is fastest.
91 The constructor parameter 'round_mode' selects the rounding behavior.
92 Valid values are:
93 - ROUND_TO_EVEN
94 - ROUND_TO_NEAREST
95 """
97 ROUND_TO_EVEN = 0
98 ROUND_TO_NEAREST = 1
100 def __init__(self, round_mode=ROUND_TO_EVEN):
101 if round_mode == FuncOpts.ROUND_TO_EVEN:
102 self.__round_func = round_to_even
103 elif round_mode == FuncOpts.ROUND_TO_NEAREST:
104 self.__round_func = round_to_nearest
105 else:
106 raise Exception('Must round to even or nearest.\n'
107 'round function: {}'.format(round_mode))
109 def round(self, x):
110 """Round a float according to the requested rounding mode."""
111 assert any(isinstance(x, T) for T in [float, float32])
113 # Drop the floating-point precision from 64 to 32 bits before
114 # rounding. The loss of precision may shift the float's fractional
115 # value to 0.5, which will affect the rounding.
116 x = float32(x)
117 return self.__round_func(x)
120 def clamp(x, min_, max_):
121 if x < min_:
122 return min_
123 elif x > max_:
124 return max_
125 else:
126 return x
129 def round_to_nearest(x):
130 # Get fractional and integral parts.
131 (f, i) = modf(x)
133 if fabs(f) < 0.5:
134 return i
135 else:
136 return i + copysign(1.0, x)
139 def round_to_even(x):
140 # Get fractional and integral parts.
141 (f, i) = modf(x)
143 if fabs(f) < 0.5:
144 return i
145 elif fabs(f) == 0.5:
146 return i + fmod(i, 2.0)
147 else:
148 return i + copysign(1.0, x)
151 def pack_2x16(pack_1x16_func, x, y, func_opts):
152 """Evaluate a GLSL pack2x16 function.
154 :param pack_1x16_func: the component-wise function of the GLSL pack2x16
155 function
156 :param x,y: each a float32
157 :return: a uint32
159 assert isinstance(x, float32)
160 assert isinstance(y, float32)
162 ux = pack_1x16_func(x, func_opts)
163 uy = pack_1x16_func(y, func_opts)
165 assert isinstance(ux, uint16)
166 assert isinstance(uy, uint16)
168 return uint32((uy << 16) | ux)
171 def pack_4x8(pack_1x8_func, x, y, z, w, func_opts):
172 # pylint: disable=too-many-arguments
173 """Evaluate a GLSL pack4x8 function.
175 :param pack_1x8_func: the component-wise function of the GLSL pack4x8
176 function
177 :param x,y,z,w: each a float32
178 :return: a uint32
180 assert isinstance(x, float32)
181 assert isinstance(y, float32)
182 assert isinstance(z, float32)
183 assert isinstance(w, float32)
185 ux = pack_1x8_func(x, func_opts)
186 uy = pack_1x8_func(y, func_opts)
187 uz = pack_1x8_func(z, func_opts)
188 uw = pack_1x8_func(w, func_opts)
190 assert isinstance(ux, uint8)
191 assert isinstance(uy, uint8)
192 assert isinstance(uz, uint8)
193 assert isinstance(uw, uint8)
195 return uint32((uw << 24) | (uz << 16) | (uy << 8) | ux)
198 def unpack_2x16(unpack_1x16_func, u, _):
199 """Evaluate a GLSL unpack2x16 function.
201 :param unpack_1x16_func: the component-wise function of the GLSL
202 unpack2x16 function
203 :param u: a uint32
204 :return: a 2-tuple of float32
206 assert isinstance(u, uint32)
208 ux = uint16(u & 0xffff)
209 uy = uint16(u >> 16)
211 x = unpack_1x16_func(ux)
212 y = unpack_1x16_func(uy)
214 assert isinstance(x, float32)
215 assert isinstance(y, float32)
217 return (x, y)
220 def unpack_4x8(unpack_1x8_func, u, _):
221 """Evaluate a GLSL unpack4x8 function.
223 :param unpack_1x8_func: the component-wise function of the GLSL
224 unpack4x8 function
225 :param u: a uint32
226 :return: a 4-tuple of float32
228 assert isinstance(u, uint32)
230 ux = uint8(u & 0xff)
231 uy = uint8((u >> 8) & 0xff)
232 uz = uint8((u >> 16) & 0xff)
233 uw = uint8((u >> 24) & 0xff)
235 x = unpack_1x8_func(ux)
236 y = unpack_1x8_func(uy)
237 z = unpack_1x8_func(uz)
238 w = unpack_1x8_func(uw)
240 assert isinstance(x, float32)
241 assert isinstance(y, float32)
242 assert isinstance(z, float32)
243 assert isinstance(w, float32)
245 return (x, y, z, w)
248 def pack_snorm_1x8(f32, func_opts):
249 """Component-wise function of packSnorm4x8."""
250 assert isinstance(f32, float32)
251 return uint8(int8(func_opts.round(clamp(f32, -1.0, +1.0) * 127.0)))
254 def pack_snorm_1x16(f32, func_opts):
255 """Component-wise function of packSnorm2x16."""
256 assert isinstance(f32, float32)
257 return uint16(int16(func_opts.round(clamp(f32, -1.0, +1.0) * 32767.0)))
260 def unpack_snorm_1x8(u8):
261 """Component-wise function of unpackSnorm4x8."""
262 assert isinstance(u8, uint8)
263 return float32(clamp(int8(u8) / 127.0, -1.0, +1.0))
266 def unpack_snorm_1x16(u16):
267 """Component-wise function of unpackSnorm2x16."""
268 assert isinstance(u16, uint16)
269 return float32(clamp(int16(u16) / 32767.0, -1.0, +1.0))
272 def pack_unorm_1x8(f32, func_opts):
273 """Component-wise function of packUnorm4x8."""
274 assert isinstance(f32, float32)
275 return uint8(func_opts.round(clamp(f32, 0.0, 1.0) * 255.0))
278 def pack_unorm_1x16(f32, func_opts):
279 """Component-wise function of packUnorm2x16."""
280 assert isinstance(f32, float32)
281 return uint16(func_opts.round(clamp(f32, 0.0, 1.0) * 65535.0))
284 def unpack_unorm_1x8(u8):
285 """Component-wise function of unpackUnorm4x8."""
286 assert isinstance(u8, uint8)
287 return float32(u8 / 255.0)
290 def unpack_unorm_1x16(u16):
291 """Component-wise function of unpackUnorm2x16."""
292 assert isinstance(u16, uint16)
293 return float32(u16 / 65535.0)
296 def pack_half_1x16(f32, func_opts):
297 """Component-wise function of packHalf2x16."""
298 assert isinstance(f32, float32)
300 # The bit layout of a float16 is:
302 # sign: 15
303 # exponent: 10:14
304 # mantissa: 0:9
306 # The sign, exponent, and mantissa determine its value by:
308 # if e = 0 and m = 0, then zero: (-1)^s * 0
309 # if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10
310 # if 0 < e < 31, then normal: (-1)^s * 2^(e - 15) * (1 + m / 2^10)
311 # if e = 31 and m = 0, then inf: (-1)^s * inf
312 # if e = 31 and m != 0, then nan
314 # where 0 <= m < 2^10.
316 # Some key boundary values of float16 are:
318 # min_normal16 = 2^(1 - 15) * (1 + 0 / 2^10)
319 # max_normal16 = 2^(30 - 15) * (1 + 1023 / 2^10)
321 # The maximum float16 step value is:
323 # max_step16 = 2^5
325 # Observe that each of the above boundary values lies in the range of
326 # normal float32 values. If we represent each of the above boundary values
327 # in the form returned by frexpf() for normal float32 values, 2^E
328 # * F where 0.5 <= F < 1, then:
330 # min_normal16 = 2^(-13) * 0.5
331 # max_normal16 = 2^16 * 0.99951171875
333 # The resultant float16's sign, exponent, and mantissa bits.
334 s = 0
335 e = 0
336 m = 0
338 # Calculate sign bit.
339 # Use copysign() to handle the case where x is -0.0.
340 if copysign(1.0, f32) < 0.0:
341 s = 1
343 # To reduce the number of cases in the if-tree below, decompose `abs(f32)`
344 # rather than `f32`.
345 (F, E) = frexp(fabs(f32))
347 # The output of frexp falls into three classes:
348 # - If f32 is NaN, then F is NaN .
349 # - If f32 is ±inf, then F is ±inf .
350 # - If f32 is ±0.0, then F is ±0.0 .
351 # - Otherwise, f32 = 2^E * F where 0.5 <= F < 1.0 .
353 # Since we decomposed `abs(f32)`, we only need be concerned with the
354 # positive cases.
355 if isnan(F):
356 # The resultant float16 is NaN.
357 e = 31
358 m = 1
359 elif isinf(F):
360 # The resultant float16 is infinite.
361 e = 31
362 m = 0
363 elif F == 0:
364 # f32 is zero, therefore the resultant float16 is zero.
365 e = 0
366 m = 0
367 elif E < -13:
368 # f32 lies in the range (0.0, min_normal16). Round f32 to a nearby
369 # float16 value. The resultant float16 will be either zero, subnormal,
370 # or normal.
371 e = 0
372 m = int(func_opts.round(2**(E + 24) * F))
373 elif E <= 16:
374 # f32 lies in the range [min_normal16, max_normal16 + max_step16).
375 # Round f32 to a nearby float16 value. The resultant float16 will be
376 # either normal or infinite.
377 e = int(E + 14)
378 m = int(func_opts.round(2**11 * F - 2**10))
379 else:
380 # f32 lies in the range [max_normal16 + max_step16, inf), which is
381 # outside the range of finite float16 values. The resultant float16 is
382 # infinite.
383 e = 31
384 m = 0
386 if m == 1024:
387 # f32 was rounded upwards into the range of the next exponent. This
388 # correctly handles the case where f32 should be rounded up to float16
389 # infinity.
390 e += 1
391 m = 0
393 assert s == 0 or s == 1
394 assert 0 <= e and e <= 31
395 assert 0 <= m and m <= 1023
397 return uint16((s << 15) | (e << 10) | m)
400 def unpack_half_1x16(u16):
401 """Component-wise function of unpackHalf2x16."""
402 assert isinstance(u16, uint16)
404 # The bit layout of a float16 is:
406 # sign: 15
407 # exponent: 10:14
408 # mantissa: 0:9
410 # The sign, exponent, and mantissa determine its value by:
412 # if e = 0 and m = 0, then zero: (-1)^s * 0
413 # if e = 0 and m != 0, then subnormal: (-1)^s * 2^(e - 14) * m / 2^10
414 # if 0 < e < 31, then normal: (-1)^s * 2^(e - 15) * (1 + m / 2^10)
415 # if e = 31 and m = 0, then inf: (-1)^s * inf
416 # if e = 31 and m != 0, then nan
418 # where 0 <= m < 2^10.
420 s = (u16 >> 15) & 0x1
421 e = (u16 >> 10) & 0x1f
422 m = u16 & 0x3ff
424 if s == 0:
425 sign = 1.0
426 else:
427 sign = -1.0
429 if e == 0:
430 return float32(sign * 2.0**(-14) * (m / 2.0**10))
431 elif 1 <= e and e <= 30:
432 return float32(sign * 2.0**(e - 15.0) * (1.0 + m / 2.0**10))
433 elif e == 31 and m == 0:
434 return float32(sign * float32("inf"))
435 elif e == 31 and m != 0:
436 return float32("NaN")
437 else:
438 raise Exception('invalid inputs')
440 # ----------------------------------------------------------------------------
441 # Inputs for GLSL functions
442 # ----------------------------------------------------------------------------
444 # This table maps GLSL pack/unpack function names to a sequence of inputs to
445 # the respective component-wise function. It contains four types of mappings:
446 # - name of a pack2x16 function to a sequence of float32
447 # - name of a pack4x8 function to a sequence of float32
448 # - name of a unpack2x16 function to a sequence of uint16
449 # - name of a unpack4x8 function to a sequence of uint8
450 full_input_table = dict()
452 # This table maps each GLSL pack/unpack function name to a subset of
453 # ``full_input_table[name]``.
455 # To sufficiently test some functions, we must test a fairly large set of
456 # component-wise inputs, so large that its cartesian product explodes. The
457 # test such functions, we test over the cartesian product of full_input_table
458 # and reduced_input_table. See make_inouts_for_pack_2x16.
460 reduced_input_table = dict()
463 def make_inputs_for_pack_snorm_2x16():
464 # The domain of packSnorm2x16 is [-inf, +inf]^2. The function clamps
465 # its input into the range [-1, +1]^2.
466 pos = (
467 0.0, # zero
468 0.1, # near zero
469 0.9, # slightly below the clamp boundary
470 1.0, # the clamp boundary
471 1.1, # slightly above the clamp boundary
472 float("+inf")
474 neg = tuple(reversed(tuple(-x for x in pos)))
475 return tuple(float32(x) for x in pos + neg)
477 full_input_table["packSnorm2x16"] = make_inputs_for_pack_snorm_2x16()
478 reduced_input_table["packSnorm2x16"] = None
480 full_input_table["packSnorm4x8"] = full_input_table["packSnorm2x16"]
482 # XXX: Perhaps there is a better choice of test inputs?
483 full_input_table["unpackSnorm2x16"] = tuple(uint16(u) for u in (
484 0, 1, 2, 3,
485 2**15 - 1,
486 2**15,
487 2**15 + 1,
488 2**16 - 1 # max uint16
491 # XXX: Perhaps there is a better choice of test inputs?
492 full_input_table["unpackSnorm4x8"] = tuple(uint8(u) for u in (
493 0, 1, 2, 3,
494 2**7 - 1,
495 2**7,
496 2**7 + 1,
497 2**8 - 1 # max uint8
500 full_input_table["packUnorm2x16"] = tuple(float32(x) for x in (
501 # The domain of packUnorm2x16 is [-inf, +inf]^2. The function clamps its
502 # input into the range [0, 1]^2.
504 "-inf",
505 -0.1, # slightly below the inner clamp boundary
506 -0.0, # infintesimally below the inner clamp boundary
507 +0.0, # the inner clamp boundary
508 +0.1, # slightly above the inner clamp boundary
509 +0.9, # slightly below the outer clamp boundary
510 +1.0, # the outer clamp boundary
511 +1.1, # slightly above the outer clamp boundary
512 "+inf"
515 reduced_input_table["packUnorm2x16"] = None
517 full_input_table["packUnorm4x8"] = full_input_table["packUnorm2x16"]
519 # XXX: Perhaps there is a better choice of test inputs?
520 full_input_table["unpackUnorm2x16"] = full_input_table["unpackSnorm2x16"]
521 full_input_table["unpackUnorm4x8"] = full_input_table["unpackSnorm4x8"]
524 def make_inputs_for_pack_half_2x16():
525 # The domain of packHalf2x16 is ([-inf, +inf] + {NaN})^2. The function
526 # does not clamp its input.
528 # We test both -0.0 and +0.0 in order to stress the implementation's
529 # handling of zero.
531 subnormal_min = 2.0**(-14) * (1.0 / 2.0**10)
532 normal_min = 2.0**(-14) * (1.0 + 0.0 / 2.0**10)
533 normal_max = 2.0**15 * (1.0 + 1023.0 / 2.0**10)
534 min_step = 2.0**(-24)
535 max_step = 2.0**5
537 pos = tuple(float32(x) for x in (
538 # Inputs that result in 0.0 .
539 0.0,
540 0.0 + 0.25 * min_step,
542 # A thorny input...
544 # if round_to_even:
545 # f16 := 0.0
546 # elif round_to_nearest:
547 # f16 := subnormal_min
549 0.0 + 0.50 * min_step,
551 # Inputs that result in a subnormal
552 # float16.
554 0.0 + 0.75 * min_step,
555 subnormal_min + 0.00 * min_step,
556 subnormal_min + 0.25 * min_step,
557 subnormal_min + 0.50 * min_step,
558 subnormal_min + 0.75 * min_step,
559 subnormal_min + 1.00 * min_step,
560 subnormal_min + 1.25 * min_step,
561 subnormal_min + 1.50 * min_step,
562 subnormal_min + 1.75 * min_step,
563 subnormal_min + 2.00 * min_step,
565 normal_min - 2.00 * min_step,
566 normal_min - 1.75 * min_step,
567 normal_min - 1.50 * min_step,
568 normal_min - 1.25 * min_step,
569 normal_min - 1.00 * min_step,
570 normal_min - 0.75 * min_step,
572 # Inputs that result in a normal float16.
574 normal_min - 0.50 * min_step,
575 normal_min - 0.25 * min_step,
576 normal_min + 0.00 * min_step,
577 normal_min + 0.25 * min_step,
578 normal_min + 0.50 * min_step,
579 normal_min + 0.75 * min_step,
580 normal_min + 1.00 * min_step,
581 normal_min + 1.25 * min_step,
582 normal_min + 1.50 * min_step,
583 normal_min + 1.75 * min_step,
584 normal_min + 2.00 * min_step,
586 2.0 * normal_min + 0.50 * min_step,
587 2.0 * normal_min + 0.75 * min_step,
588 2.0 * normal_min + 1.00 * min_step,
590 0.5,
591 1.0,
592 1.5,
594 normal_max - 2.00 * max_step,
595 normal_max - 1.75 * max_step,
596 normal_max - 1.50 * max_step,
597 normal_max - 1.25 * max_step,
598 normal_max - 1.00 * max_step,
599 normal_max - 0.75 * max_step,
600 normal_max - 0.50 * max_step,
601 normal_max - 0.25 * max_step,
602 normal_max + 0.00 * max_step,
603 normal_max + 0.25 * max_step,
605 # Inputs that result in infinity.
607 normal_max + 0.50 * max_step,
608 normal_max + 0.75 * max_step,
609 normal_max + 1.00 * max_step,
610 normal_max + 2.00 * max_step,
612 "+inf"))
614 neg = tuple(reversed([-x for x in pos]))
615 return neg + pos
617 full_input_table["packHalf2x16"] = make_inputs_for_pack_half_2x16()
619 reduced_input_table["packHalf2x16"] = tuple(float32(x) for x in (
620 "-inf",
621 -2.0,
622 -1.0,
623 -0.0,
624 +0.0,
625 +1.0,
626 +2.0,
627 "+inf"
631 def make_inputs_for_unpack_half_2x16():
632 # For each of the two classes of float16 values, subnormal and normalized,
633 # below are listed the exponent and mantissa of the class's boundary
634 # values and some values slightly inside the bounds.
635 # pylint: disable=bad-whitespace
636 bounds = (
637 (0, 0), # zero
638 (0, 1), # subnormal_min
639 (0, 2), # subnormal_min + min_step
640 (0, 1022), # subnormal_max - min_step
641 (0, 1023), # subnormal_max
642 (1, 0), # normal_min
643 (1, 1), # normal_min + min_step
644 (30, 1022), # normal_max - max_step
645 (30, 1023), # normal_max
646 (31, 0) # inf
648 # pylint: enable=bad-whitespace
650 def make_uint16(s, e, m):
651 return uint16((s << 15) | (e << 10) | m)
653 pos = tuple(make_uint16(0, e, m) for (e, m) in bounds)
654 neg = tuple(make_uint16(1, e, m) for (e, m) in reversed(bounds))
655 return neg + pos
657 full_input_table["unpackHalf2x16"] = make_inputs_for_unpack_half_2x16()
659 # ----------------------------------------------------------------------------
660 # Expected outputs for GLSL functions
661 # ----------------------------------------------------------------------------
663 # For a given input to a GLSL function, InOutTuple lists all valid outputs.
665 # There are multiple types of InOutTuple, described below. In each
666 # description, the numerical types actually refer to strings that represent
667 # a GLSL literal of that type.
669 # - That for a pack2x16 function: the input is a 2-tuple of float32 and each
670 # output is a uint32. For example, ``InOutTuple(input=("0.0", "0.0"),
671 # valid_outputs=("0u", "0u", "0u"))``.
673 # - That for a unpack2x16 function: the input is a uint32 and each output is
674 # a 2-tuple of float32. For example, ``InOutTuple(input="0x80000000u",
675 # valid_outputs=(("0.0", "-0.0"),))``.
677 InOutTuple = namedtuple("InOutTuple", ("input", "valid_outputs"))
680 def glsl_literal(x):
681 """Convert the given number to a string that represents a GLSL literal.
683 :param x: a uint32 or float32
685 if isinstance(x, uint32):
686 return "{0}u".format(uint32(x))
687 elif isinstance(x, float32):
688 if math.isnan(x):
689 # GLSL ES 3.00 and GLSL 4.10 do not require implementations to
690 # support NaN, so we do not test it.
691 raise Exception('NaN is not tested.')
692 elif math.isinf(x):
693 # GLSL ES 3.00 lacks a literal for infinity. However, ±1.0e256
694 # suffices because it lies sufficientlyoutside the range of finite
695 # float32 values.
697 # From page 31 of the GLSL ES 3.00 spec:
699 # If the value of the floating point number is too large (small)
700 # to be stored as a single precision value, it is converted to
701 # positive (negative) infinity.
703 return repr(copysign(1.0e256, x))
704 elif x == 0 and copysign(1.0, x) == -1.0:
705 # Workaround for numpy-1.7.0, in which repr(float32(-0.0)) does
706 # not return a float literal.
707 # See https://github.com/numpy/numpy/issues/2935 .
708 return "-0.0"
709 else:
710 return repr(x)
711 else:
712 raise Exception('Unsupported GLSL litteral')
715 def make_inouts_for_pack_2x16(pack_1x16_func,
716 all_float32_inputs,
717 reduced_inputs=None):
718 """Determine valid outputs for a given GLSL pack2x16 function.
720 If the reduced_float32_inputs parameter is None, then it is assumed to be
721 the same as all_float32_inputs.
723 The set of vec2 inputs constructed by this function is the union of
724 cartesian products:
725 (all_float32_inputs x reduced_inputs)
726 + (reduced_inputs x all_float32_inputs)
728 :param pack_1x16_func: the component-wise function of the pack2x16
729 function
730 :param float32_inputs: a sequence of inputs to pack_1x16_func
731 :return: a sequence of InOutTuple
733 inout_seq = []
735 func_opt_seq = (FuncOpts(FuncOpts.ROUND_TO_EVEN),
736 FuncOpts(FuncOpts.ROUND_TO_NEAREST))
738 if reduced_inputs is None:
739 reduced_inputs = all_float32_inputs
741 def add_vec2_input(x, y):
742 assert isinstance(x, float32)
743 assert isinstance(y, float32)
745 valid_outputs = []
746 for func_opts in func_opt_seq:
747 u32 = pack_2x16(pack_1x16_func, x, y, func_opts)
748 assert isinstance(u32, uint32)
749 valid_outputs.append(glsl_literal(u32))
751 inout_seq.append(
752 InOutTuple(input=(glsl_literal(x), glsl_literal(y)),
753 valid_outputs=valid_outputs))
755 for y in reduced_inputs:
756 for x in all_float32_inputs:
757 add_vec2_input(x, y)
758 add_vec2_input(y, x)
760 return inout_seq
763 def make_inouts_for_pack_4x8(pack_1x8_func, float32_inputs):
764 """Determine valid outputs for a given GLSL pack4x8 function.
766 :param pack_1x8_func: the component-wise function of the pack4x8
767 function
768 :param float32_inputs: a sequence of inputs to pack_1x8_func
769 :return: a sequence of InOutTuple
771 inout_seq = []
773 func_opt_seq = (FuncOpts(FuncOpts.ROUND_TO_EVEN),
774 FuncOpts(FuncOpts.ROUND_TO_NEAREST))
776 for y in float32_inputs:
777 for x in float32_inputs:
778 assert isinstance(x, float32)
780 valid_outputs_0 = []
781 valid_outputs_1 = []
782 for func_opts in func_opt_seq:
783 u32_0 = pack_4x8(pack_1x8_func, x, y, x, y, func_opts)
784 u32_1 = pack_4x8(pack_1x8_func, x, x, y, y, func_opts)
785 assert isinstance(u32_0, uint32)
786 assert isinstance(u32_1, uint32)
787 valid_outputs_0.append(glsl_literal(u32_0))
788 valid_outputs_1.append(glsl_literal(u32_1))
790 inout_seq.append(
791 InOutTuple(input=(glsl_literal(x), glsl_literal(y),
792 glsl_literal(x), glsl_literal(y)),
793 valid_outputs=valid_outputs_0))
794 inout_seq.append(
795 InOutTuple(input=(glsl_literal(x), glsl_literal(x),
796 glsl_literal(y), glsl_literal(y)),
797 valid_outputs=valid_outputs_1))
798 return inout_seq
801 def make_inouts_for_unpack_2x16(unpack_1x16_func, uint16_inputs):
802 """Determine expected outputs of a given GLSL unpack2x16 function.
804 :param unpack_1x16_func: the component-wise function of the unpack2x16
805 function
806 :param uint16_inputs: a sequence of inputs to unpack_1x16_func
807 :return: a sequence of InOutTuple
809 inout_seq = []
810 func_opts = FuncOpts()
812 for y in uint16_inputs:
813 for x in uint16_inputs:
814 assert isinstance(x, uint16)
815 u32 = uint32((y << 16) | x)
816 vec2 = unpack_2x16(unpack_1x16_func, u32, func_opts)
817 assert isinstance(vec2[0], float32)
818 assert isinstance(vec2[1], float32)
819 inout_seq.append(
820 InOutTuple(input=glsl_literal(u32),
821 valid_outputs=[(glsl_literal(vec2[0]),
822 glsl_literal(vec2[1]))]))
824 return inout_seq
827 def make_inouts_for_unpack_4x8(unpack_1x8_func, uint8_inputs):
828 """Determine expected outputs of a given GLSL unpack4x8 function.
830 :param unpack_1x8_func: the component-wise function of the unpack4x8
831 function
832 :param uint8_inputs: a sequence of inputs to unpack_1x8_func
833 :return: a sequence of InOutTuple
835 inout_seq = []
837 func_opts = FuncOpts()
839 for y in uint8_inputs:
840 for x in uint8_inputs:
841 assert isinstance(x, uint8)
842 u32_0 = uint32((y << 24) | (x << 16) | (y << 8) | x)
843 u32_1 = uint32((y << 24) | (y << 16) | (x << 8) | x)
845 valid_outputs_0 = []
846 valid_outputs_1 = []
847 vec4_0 = unpack_4x8(unpack_1x8_func, u32_0, func_opts)
848 vec4_1 = unpack_4x8(unpack_1x8_func, u32_1, func_opts)
849 assert isinstance(vec4_0[0], float32)
850 assert isinstance(vec4_0[1], float32)
851 assert isinstance(vec4_0[2], float32)
852 assert isinstance(vec4_0[3], float32)
853 assert isinstance(vec4_1[0], float32)
854 assert isinstance(vec4_1[1], float32)
855 assert isinstance(vec4_1[2], float32)
856 assert isinstance(vec4_1[3], float32)
857 valid_outputs_0.append((glsl_literal(vec4_0[0]),
858 glsl_literal(vec4_0[1]),
859 glsl_literal(vec4_0[2]),
860 glsl_literal(vec4_0[3])))
861 valid_outputs_1.append((glsl_literal(vec4_1[0]),
862 glsl_literal(vec4_1[1]),
863 glsl_literal(vec4_1[2]),
864 glsl_literal(vec4_1[3])))
866 inout_seq.append(InOutTuple(input=glsl_literal(u32_0),
867 valid_outputs=valid_outputs_0))
868 inout_seq.append(InOutTuple(input=glsl_literal(u32_1),
869 valid_outputs=valid_outputs_1))
871 return inout_seq
873 # This table maps GLSL pack/unpack function names to the precision of their
874 # return type.
875 result_precision_table = {
876 "packSnorm2x16": "highp",
877 "packSnorm4x8": "highp",
878 "packUnorm2x16": "highp",
879 "packUnorm4x8": "highp",
880 "packHalf2x16": "highp",
881 "unpackSnorm2x16": "highp",
882 "unpackSnorm4x8": "highp",
883 "unpackUnorm2x16": "highp",
884 "unpackUnorm4x8": "highp",
885 "unpackHalf2x16": "mediump"
888 # This table maps GLSL pack/unpack function names to a sequence of InOutTuple.
889 inout_table = {
890 "packSnorm2x16": make_inouts_for_pack_2x16(
891 pack_snorm_1x16, full_input_table["packSnorm2x16"],
892 reduced_input_table["packSnorm2x16"]),
893 "packSnorm4x8": make_inouts_for_pack_4x8(
894 pack_snorm_1x8, full_input_table["packSnorm4x8"]),
895 "packUnorm2x16": make_inouts_for_pack_2x16(
896 pack_unorm_1x16, full_input_table["packUnorm2x16"],
897 reduced_input_table["packUnorm2x16"]),
898 "packUnorm4x8": make_inouts_for_pack_4x8(
899 pack_unorm_1x8, full_input_table["packUnorm4x8"]),
900 "packHalf2x16": make_inouts_for_pack_2x16(
901 pack_half_1x16, full_input_table["packHalf2x16"],
902 reduced_input_table["packHalf2x16"]),
903 "unpackSnorm2x16": make_inouts_for_unpack_2x16(
904 unpack_snorm_1x16, full_input_table["unpackSnorm2x16"]),
905 "unpackSnorm4x8": make_inouts_for_unpack_4x8(
906 unpack_snorm_1x8, full_input_table["unpackSnorm4x8"]),
907 "unpackUnorm2x16": make_inouts_for_unpack_2x16(
908 unpack_unorm_1x16, full_input_table["unpackUnorm2x16"]),
909 "unpackUnorm4x8": make_inouts_for_unpack_4x8(
910 unpack_unorm_1x8, full_input_table["unpackUnorm4x8"]),
911 "unpackHalf2x16": make_inouts_for_unpack_2x16(
912 unpack_half_1x16, full_input_table["unpackHalf2x16"])
916 # ----------------------------------------------------------------------------
917 # Generate test files
918 # ----------------------------------------------------------------------------
921 FuncInfo = namedtuple('FuncInfo', ['name', 'dimension', 'result_precision',
922 'inout_seq', 'num_valid_outputs',
923 'vector_type', 'requirements', 'exact'])
925 def func_info(name, requirements):
926 """Factory function for information for a GLSL pack/unpack function.
928 Properties
929 ----------
930 - name: Name of the GLSL function, such as "packSnorm2x16".
932 - dimension: Dimension of the GLSL function, such as "2x16".
934 - result_precision: Precision of the GLSL function's return type, such as
935 "highp".
937 - inout_seq: A sequence of InOutTuple. The generated test file will test
938 all inputs listed in the sequence.
940 - num_valid_outputs: The number of valid outputs for each input of
941 self.inout_seq. (We assume that each input has the same number of valid
942 outputs).
944 - vector_type: The type of the GLSL function's parameter or return value.
945 E.g., vec4 for a 4x8 function and vec2 for a 2x16 function.
947 - requirements: A set of API/extension requirments to be listed in the
948 .shader_test's [requires] section.
950 - exact: Whether the generated results must be exact (e.g., 0.0 and 1.0
951 should always be converted exactly).
955 if name.endswith("2x16"):
956 dimension = "2x16"
957 vector_type = "vec2"
958 elif name.endswith("4x8"):
959 dimension = "4x8"
960 vector_type = "vec4"
961 else:
962 raise Exception('Invalid pack type {}'.format(name))
964 inout_seq = inout_table[name]
966 return FuncInfo(name, dimension, result_precision_table[name],
967 inout_seq, len(inout_seq[0].valid_outputs), vector_type,
968 requirements, name.endswith("unpackHalf2x16"))
971 class ShaderTest(object):
972 """A .shader_test file."""
974 @staticmethod
975 def all_tests():
976 requirements = "GLSL >= 1.30\nGL_ARB_shading_language_packing"
977 ARB_shading_language_packing_funcs = (
978 func_info("packSnorm2x16", requirements),
979 func_info("packSnorm4x8", requirements),
980 func_info("packUnorm2x16", requirements),
981 func_info("packUnorm4x8", requirements),
982 func_info("packHalf2x16", requirements),
983 func_info("unpackSnorm2x16", requirements),
984 func_info("unpackSnorm4x8", requirements),
985 func_info("unpackUnorm2x16", requirements),
986 func_info("unpackUnorm4x8", requirements),
987 func_info("unpackHalf2x16", requirements)
990 requirements = "GL ES >= 3.0\nGLSL ES >= 3.00"
991 glsl_es_300_funcs = (
992 func_info("packSnorm2x16", requirements),
993 func_info("packUnorm2x16", requirements),
994 func_info("packHalf2x16", requirements),
995 func_info("unpackSnorm2x16", requirements),
996 func_info("unpackUnorm2x16", requirements),
997 func_info("unpackHalf2x16", requirements)
1000 execution_stages = ("const", "vs", "fs")
1002 for s in execution_stages:
1003 for f in glsl_es_300_funcs:
1004 yield ShaderTest(f, s, "glsl-es-3.00")
1005 for f in ARB_shading_language_packing_funcs:
1006 yield ShaderTest(f, s, "ARB_shading_language_packing")
1008 def __init__(self, funcinfo, execution_stage, api):
1009 assert isinstance(funcinfo, FuncInfo)
1010 assert execution_stage in ("const", "vs", "fs")
1011 assert api in ("glsl-es-3.00", "ARB_shading_language_packing")
1013 self.__template = TEMPLATE_TABLE[(execution_stage,
1014 funcinfo.name[0],
1015 funcinfo.dimension)]
1016 self.__func_info = funcinfo
1017 self.__filename = os.path.join(
1018 "spec",
1019 api.lower(),
1020 "execution",
1021 "built-in-functions",
1022 "{0}-{1}.shader_test".format(execution_stage, funcinfo.name))
1024 @property
1025 def filename(self):
1026 return self.__filename
1028 def write_file(self):
1029 dirname = os.path.dirname(self.filename)
1030 utils.safe_makedirs(dirname)
1032 with open(self.filename, "w") as f:
1033 f.write(self.__template.render_unicode(func=self.__func_info))
1036 def main():
1037 parser = optparse.OptionParser(
1038 description="Generate shader tests that test the built-inpacking "
1039 "functions",
1040 usage="usage: %prog [-h] [--names-only]")
1041 parser.add_option(
1042 '--names-only',
1043 dest='names_only',
1044 action='store_true',
1045 help="Don't output files, just generate a list of filenames to stdout")
1047 (options, args) = parser.parse_args()
1049 if len(args) != 0:
1050 # User gave extra args.
1051 parser.print_help()
1052 sys.exit(1)
1054 for test in ShaderTest.all_tests():
1055 print(test.filename)
1057 # Some test files take a long time to generate, so provide status
1058 # updates to the user immediately.
1059 sys.stdout.flush()
1061 if not options.names_only:
1062 test.write_file()
1064 if __name__ == '__main__':
1065 main()