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
22 """This scripts generates tests for the GLSL packing functions, such as
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.
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
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.
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.
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.
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
106 raise Exception('Must round to even or nearest.\n'
107 'round function: {}'.format(round_mode
))
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.
117 return self
.__round
_func
(x
)
120 def clamp(x
, min_
, max_
):
129 def round_to_nearest(x
):
130 # Get fractional and integral parts.
136 return i
+ copysign(1.0, x
)
139 def round_to_even(x
):
140 # Get fractional and integral parts.
146 return i
+ fmod(i
, 2.0)
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
156 :param x,y: each a float32
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
177 :param x,y,z,w: each a float32
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
204 :return: a 2-tuple of float32
206 assert isinstance(u
, uint32
)
208 ux
= uint16(u
& 0xffff)
211 x
= unpack_1x16_func(ux
)
212 y
= unpack_1x16_func(uy
)
214 assert isinstance(x
, float32
)
215 assert isinstance(y
, float32
)
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
226 :return: a 4-tuple of float32
228 assert isinstance(u
, uint32
)
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
)
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:
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:
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.
338 # Calculate sign bit.
339 # Use copysign() to handle the case where x is -0.0.
340 if copysign(1.0, f32
) < 0.0:
343 # To reduce the number of cases in the if-tree below, decompose `abs(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
356 # The resultant float16 is NaN.
360 # The resultant float16 is infinite.
364 # f32 is zero, therefore the resultant float16 is zero.
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,
372 m
= int(func_opts
.round(2**(E
+ 24) * F
))
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.
378 m
= int(func_opts
.round(2**11 * F
- 2**10))
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
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
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:
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
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")
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.
469 0.9, # slightly below the clamp boundary
470 1.0, # the clamp boundary
471 1.1, # slightly above the clamp boundary
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 (
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 (
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.
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
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
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)
537 pos
= tuple(float32(x
) for x
in (
538 # Inputs that result in 0.0 .
540 0.0 + 0.25 * min_step
,
546 # elif round_to_nearest:
547 # f16 := subnormal_min
549 0.0 + 0.50 * min_step
,
551 # Inputs that result in a subnormal
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
,
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
,
614 neg
= tuple(reversed([-x
for x
in pos
]))
617 full_input_table
["packHalf2x16"] = make_inputs_for_pack_half_2x16()
619 reduced_input_table
["packHalf2x16"] = tuple(float32(x
) for x
in (
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
638 (0, 1), # subnormal_min
639 (0, 2), # subnormal_min + min_step
640 (0, 1022), # subnormal_max - min_step
641 (0, 1023), # subnormal_max
643 (1, 1), # normal_min + min_step
644 (30, 1022), # normal_max - max_step
645 (30, 1023), # normal_max
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
))
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"))
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
):
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.')
693 # GLSL ES 3.00 lacks a literal for infinity. However, ±1.0e256
694 # suffices because it lies sufficientlyoutside the range of finite
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 .
712 raise Exception('Unsupported GLSL litteral')
715 def make_inouts_for_pack_2x16(pack_1x16_func
,
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
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
730 :param float32_inputs: a sequence of inputs to pack_1x16_func
731 :return: a sequence of InOutTuple
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
)
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
))
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
:
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
768 :param float32_inputs: a sequence of inputs to pack_1x8_func
769 :return: a sequence of InOutTuple
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
)
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
))
791 InOutTuple(input=(glsl_literal(x
), glsl_literal(y
),
792 glsl_literal(x
), glsl_literal(y
)),
793 valid_outputs
=valid_outputs_0
))
795 InOutTuple(input=(glsl_literal(x
), glsl_literal(x
),
796 glsl_literal(y
), glsl_literal(y
)),
797 valid_outputs
=valid_outputs_1
))
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
806 :param uint16_inputs: a sequence of inputs to unpack_1x16_func
807 :return: a sequence of InOutTuple
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
)
820 InOutTuple(input=glsl_literal(u32
),
821 valid_outputs
=[(glsl_literal(vec2
[0]),
822 glsl_literal(vec2
[1]))]))
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
832 :param uint8_inputs: a sequence of inputs to unpack_1x8_func
833 :return: a sequence of InOutTuple
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
)
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
))
873 # This table maps GLSL pack/unpack function names to the precision of their
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.
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.
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
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
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"):
958 elif name
.endswith("4x8"):
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."""
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
,
1015 funcinfo
.dimension
)]
1016 self
.__func
_info
= funcinfo
1017 self
.__filename
= os
.path
.join(
1021 "built-in-functions",
1022 "{0}-{1}.shader_test".format(execution_stage
, funcinfo
.name
))
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
))
1037 parser
= optparse
.OptionParser(
1038 description
="Generate shader tests that test the built-inpacking "
1040 usage
="usage: %prog [-h] [--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()
1050 # User gave extra args.
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.
1061 if not options
.names_only
:
1064 if __name__
== '__main__':