util: Add dedicated depth-stencil packing/unpacking functions.
[mesa.git] / src / gallium / auxiliary / util / u_format_pack.py
1 #!/usr/bin/env python
2
3 '''
4 /**************************************************************************
5 *
6 * Copyright 2009-2010 VMware, Inc.
7 * All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sub license, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice (including the
18 * next paragraph) shall be included in all copies or substantial portions
19 * of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
24 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
25 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 *
29 **************************************************************************/
30
31 /**
32 * @file
33 * Pixel format packing and unpacking functions.
34 *
35 * @author Jose Fonseca <jfonseca@vmware.com>
36 */
37 '''
38
39
40 import sys
41 import math
42
43 from u_format_parse import *
44
45
46 def generate_format_type(format):
47 '''Generate a structure that describes the format.'''
48
49 assert format.layout == PLAIN
50
51 print 'union util_format_%s {' % format.short_name()
52
53 if format.block_size() in (8, 16, 32, 64):
54 print ' uint%u_t value;' % (format.block_size(),)
55
56 use_bitfields = False
57 for channel in format.channels:
58 if channel.size % 8 or not is_pot(channel.size):
59 use_bitfields = True
60
61 print ' struct {'
62 for channel in format.channels:
63 if use_bitfields:
64 if channel.type == VOID:
65 if channel.size:
66 print ' unsigned %s:%u;' % (channel.name, channel.size)
67 elif channel.type == UNSIGNED:
68 print ' unsigned %s:%u;' % (channel.name, channel.size)
69 elif channel.type in (SIGNED, FIXED):
70 print ' int %s:%u;' % (channel.name, channel.size)
71 elif channel.type == FLOAT:
72 if channel.size == 64:
73 print ' double %s;' % (channel.name)
74 elif channel.size == 32:
75 print ' float %s;' % (channel.name)
76 else:
77 print ' unsigned %s:%u;' % (channel.name, channel.size)
78 else:
79 assert 0
80 else:
81 assert channel.size % 8 == 0 and is_pot(channel.size)
82 if channel.type == VOID:
83 if channel.size:
84 print ' uint%u_t %s;' % (channel.size, channel.name)
85 elif channel.type == UNSIGNED:
86 print ' uint%u_t %s;' % (channel.size, channel.name)
87 elif channel.type in (SIGNED, FIXED):
88 print ' int%u_t %s;' % (channel.size, channel.name)
89 elif channel.type == FLOAT:
90 if channel.size == 64:
91 print ' double %s;' % (channel.name)
92 elif channel.size == 32:
93 print ' float %s;' % (channel.name)
94 elif channel.size == 16:
95 print ' uint16_t %s;' % (channel.name)
96 else:
97 assert 0
98 else:
99 assert 0
100 print ' } chan;'
101 print '};'
102 print
103
104
105 def bswap_format(format):
106 '''Generate a structure that describes the format.'''
107
108 if format.is_bitmask() and not format.is_array() and format.block_size() > 8:
109 print '#ifdef PIPE_ARCH_BIG_ENDIAN'
110 print ' pixel.value = util_bswap%u(pixel.value);' % format.block_size()
111 print '#endif'
112
113
114 def is_format_supported(format):
115 '''Determines whether we actually have the plumbing necessary to generate the
116 to read/write to/from this format.'''
117
118 # FIXME: Ideally we would support any format combination here.
119
120 if format.layout != PLAIN:
121 return False
122
123 for i in range(4):
124 channel = format.channels[i]
125 if channel.type not in (VOID, UNSIGNED, SIGNED, FLOAT, FIXED):
126 return False
127 if channel.type == FLOAT and channel.size not in (16, 32, 64):
128 return False
129
130 return True
131
132
133 def native_type(format):
134 '''Get the native appropriate for a format.'''
135
136 if format.layout == PLAIN:
137 if not format.is_array():
138 # For arithmetic pixel formats return the integer type that matches the whole pixel
139 return 'uint%u_t' % format.block_size()
140 else:
141 # For array pixel formats return the integer type that matches the color channel
142 channel = format.channels[0]
143 if channel.type in (UNSIGNED, VOID):
144 return 'uint%u_t' % channel.size
145 elif channel.type in (SIGNED, FIXED):
146 return 'int%u_t' % channel.size
147 elif channel.type == FLOAT:
148 if channel.size == 16:
149 return 'uint16_t'
150 elif channel.size == 32:
151 return 'float'
152 elif channel.size == 64:
153 return 'double'
154 else:
155 assert False
156 else:
157 assert False
158 else:
159 assert False
160
161
162 def intermediate_native_type(bits, sign):
163 '''Find a native type adequate to hold intermediate results of the request bit size.'''
164
165 bytes = 4 # don't use anything smaller than 32bits
166 while bytes * 8 < bits:
167 bytes *= 2
168 bits = bytes*8
169
170 if sign:
171 return 'int%u_t' % bits
172 else:
173 return 'uint%u_t' % bits
174
175
176 def get_one_shift(type):
177 '''Get the number of the bit that matches unity for this type.'''
178 if type.type == 'FLOAT':
179 assert False
180 if not type.norm:
181 return 0
182 if type.type == UNSIGNED:
183 return type.size
184 if type.type == SIGNED:
185 return type.size - 1
186 if type.type == FIXED:
187 return type.size / 2
188 assert False
189
190
191 def value_to_native(type, value):
192 '''Get the value of unity for this type.'''
193 if type.type == FLOAT:
194 return value
195 if type.type == FIXED:
196 return int(value * (1 << (type.size/2)))
197 if not type.norm:
198 return int(value)
199 if type.type == UNSIGNED:
200 return int(value * ((1 << type.size) - 1))
201 if type.type == SIGNED:
202 return int(value * ((1 << (type.size - 1)) - 1))
203 assert False
204
205
206 def native_to_constant(type, value):
207 '''Get the value of unity for this type.'''
208 if type.type == FLOAT:
209 if type.size <= 32:
210 return "%ff" % value
211 else:
212 return "%ff" % value
213 else:
214 return str(int(value))
215
216
217 def get_one(type):
218 '''Get the value of unity for this type.'''
219 return value_to_native(type, 1)
220
221
222 def clamp_expr(src_channel, dst_channel, dst_native_type, value):
223 '''Generate the expression to clamp the value in the source type to the
224 destination type range.'''
225
226 if src_channel == dst_channel:
227 return value
228
229 src_min = src_channel.min()
230 src_max = src_channel.max()
231 dst_min = dst_channel.min()
232 dst_max = dst_channel.max()
233
234 # Translate the destination range to the src native value
235 dst_min_native = value_to_native(src_channel, dst_min)
236 dst_max_native = value_to_native(src_channel, dst_max)
237
238 if src_min < dst_min and src_max > dst_max:
239 return 'CLAMP(%s, %s, %s)' % (value, dst_min_native, dst_max_native)
240
241 if src_max > dst_max:
242 return 'MIN2(%s, %s)' % (value, dst_max_native)
243
244 if src_min < dst_min:
245 return 'MAX2(%s, %s)' % (value, dst_min_native)
246
247 return value
248
249
250 def conversion_expr(src_channel,
251 dst_channel, dst_native_type,
252 value,
253 clamp=True,
254 src_colorspace = 'rgb',
255 dst_colorspace = 'rgb'):
256 '''Generate the expression to convert a value between two types.'''
257
258 if src_colorspace != dst_colorspace:
259 if src_colorspace == 'srgb':
260 assert src_channel.type == UNSIGNED
261 assert src_channel.norm
262 assert src_channel.size == 8
263 assert dst_colorspace == 'rgb'
264 if dst_channel.type == FLOAT:
265 return 'util_format_srgb_8unorm_to_linear_float(%s)' % value
266 else:
267 assert dst_channel.type == UNSIGNED
268 assert dst_channel.norm
269 assert dst_channel.size == 8
270 return 'util_format_srgb_to_linear_8unorm(%s)' % value
271 elif dst_colorspace == 'srgb':
272 assert dst_channel.type == UNSIGNED
273 assert dst_channel.norm
274 assert dst_channel.size == 8
275 assert src_colorspace == 'rgb'
276 if src_channel.type == FLOAT:
277 return 'util_format_linear_float_to_srgb_8unorm(%s)' % value
278 else:
279 assert src_channel.type == UNSIGNED
280 assert src_channel.norm
281 assert src_channel.size == 8
282 return 'util_format_linear_to_srgb_8unorm(%s)' % value
283 elif src_colorspace == 'zs':
284 pass
285 elif dst_colorspace == 'zs':
286 pass
287 else:
288 assert 0
289
290 if src_channel == dst_channel:
291 return value
292
293 src_type = src_channel.type
294 src_size = src_channel.size
295 src_norm = src_channel.norm
296
297 # Promote half to float
298 if src_type == FLOAT and src_size == 16:
299 value = 'util_half_to_float(%s)' % value
300 src_size = 32
301
302 if clamp:
303 if dst_channel.type != FLOAT or src_type != FLOAT:
304 value = clamp_expr(src_channel, dst_channel, dst_native_type, value)
305
306 if src_type in (SIGNED, UNSIGNED) and dst_channel.type in (SIGNED, UNSIGNED):
307 if not src_norm and not dst_channel.norm:
308 # neither is normalized -- just cast
309 return '(%s)%s' % (dst_native_type, value)
310
311 src_one = get_one(src_channel)
312 dst_one = get_one(dst_channel)
313
314 if src_one > dst_one and src_norm and dst_channel.norm:
315 # We can just bitshift
316 src_shift = get_one_shift(src_channel)
317 dst_shift = get_one_shift(dst_channel)
318 value = '(%s >> %s)' % (value, src_shift - dst_shift)
319 else:
320 # We need to rescale using an intermediate type big enough to hold the multiplication of both
321 tmp_native_type = intermediate_native_type(src_size + dst_channel.size, src_channel.sign and dst_channel.sign)
322 value = '((%s)%s)' % (tmp_native_type, value)
323 value = '(%s * 0x%x / 0x%x)' % (value, dst_one, src_one)
324 value = '(%s)%s' % (dst_native_type, value)
325 return value
326
327 # Promote to either float or double
328 if src_type != FLOAT:
329 if src_norm or src_type == FIXED:
330 one = get_one(src_channel)
331 if src_size <= 23:
332 value = '(%s * (1.0f/0x%x))' % (value, one)
333 if dst_channel.size <= 32:
334 value = '(float)%s' % value
335 src_size = 32
336 else:
337 # bigger than single precision mantissa, use double
338 value = '(%s * (1.0/0x%x))' % (value, one)
339 src_size = 64
340 src_norm = False
341 else:
342 if src_size <= 23 or dst_channel.size <= 32:
343 value = '(float)%s' % value
344 src_size = 32
345 else:
346 # bigger than single precision mantissa, use double
347 value = '(double)%s' % value
348 src_size = 64
349 src_type = FLOAT
350
351 # Convert double or float to non-float
352 if dst_channel.type != FLOAT:
353 if dst_channel.norm or dst_channel.type == FIXED:
354 dst_one = get_one(dst_channel)
355 if dst_channel.size <= 23:
356 value = '(%s * 0x%x)' % (value, dst_one)
357 else:
358 # bigger than single precision mantissa, use double
359 value = '(%s * (double)0x%x)' % (value, dst_one)
360 value = '(%s)%s' % (dst_native_type, value)
361 else:
362 # Cast double to float when converting to either half or float
363 if dst_channel.size <= 32 and src_size > 32:
364 value = '(float)%s' % value
365 src_size = 32
366
367 if dst_channel.size == 16:
368 value = 'util_float_to_half(%s)' % value
369 elif dst_channel.size == 64 and src_size < 64:
370 value = '(double)%s' % value
371
372 return value
373
374
375 def generate_unpack_kernel(format, dst_channel, dst_native_type):
376
377 if not is_format_supported(format):
378 return
379
380 assert format.layout == PLAIN
381
382 src_native_type = native_type(format)
383
384 if format.is_bitmask():
385 depth = format.block_size()
386 print ' uint%u_t value = *(const uint%u_t *)src;' % (depth, depth)
387
388 # Declare the intermediate variables
389 for i in range(format.nr_channels()):
390 src_channel = format.channels[i]
391 if src_channel.type == UNSIGNED:
392 print ' uint%u_t %s;' % (depth, src_channel.name)
393 elif src_channel.type == SIGNED:
394 print ' int%u_t %s;' % (depth, src_channel.name)
395
396 if depth > 8:
397 print '#ifdef PIPE_ARCH_BIG_ENDIAN'
398 print ' value = util_bswap%u(value);' % depth
399 print '#endif'
400
401 # Compute the intermediate unshifted values
402 shift = 0
403 for i in range(format.nr_channels()):
404 src_channel = format.channels[i]
405 value = 'value'
406 if src_channel.type == UNSIGNED:
407 if shift:
408 value = '%s >> %u' % (value, shift)
409 if shift + src_channel.size < depth:
410 value = '(%s) & 0x%x' % (value, (1 << src_channel.size) - 1)
411 elif src_channel.type == SIGNED:
412 if shift + src_channel.size < depth:
413 # Align the sign bit
414 lshift = depth - (shift + src_channel.size)
415 value = '%s << %u' % (value, lshift)
416 # Cast to signed
417 value = '(int%u_t)(%s) ' % (depth, value)
418 if src_channel.size < depth:
419 # Align the LSB bit
420 rshift = depth - src_channel.size
421 value = '(%s) >> %u' % (value, rshift)
422 else:
423 value = None
424
425 if value is not None:
426 print ' %s = %s;' % (src_channel.name, value)
427
428 shift += src_channel.size
429
430 # Convert, swizzle, and store final values
431 for i in range(4):
432 swizzle = format.swizzles[i]
433 if swizzle < 4:
434 src_channel = format.channels[swizzle]
435 src_colorspace = format.colorspace
436 if src_colorspace == 'srgb' and i == 3:
437 # Alpha channel is linear
438 src_colorspace = 'rgb'
439 value = src_channel.name
440 value = conversion_expr(src_channel,
441 dst_channel, dst_native_type,
442 value,
443 src_colorspace = src_colorspace)
444 elif swizzle == SWIZZLE_0:
445 value = '0'
446 elif swizzle == SWIZZLE_1:
447 value = get_one(dst_channel)
448 elif swizzle == SWIZZLE_NONE:
449 value = '0'
450 else:
451 assert False
452 print ' dst[%u] = %s; /* %s */' % (i, value, 'rgba'[i])
453
454 else:
455 print ' union util_format_%s pixel;' % format.short_name()
456 print ' memcpy(&pixel, src, sizeof pixel);'
457 bswap_format(format)
458
459 for i in range(4):
460 swizzle = format.swizzles[i]
461 if swizzle < 4:
462 src_channel = format.channels[swizzle]
463 src_colorspace = format.colorspace
464 if src_colorspace == 'srgb' and i == 3:
465 # Alpha channel is linear
466 src_colorspace = 'rgb'
467 value = 'pixel.chan.%s' % src_channel.name
468 value = conversion_expr(src_channel,
469 dst_channel, dst_native_type,
470 value,
471 src_colorspace = src_colorspace)
472 elif swizzle == SWIZZLE_0:
473 value = '0'
474 elif swizzle == SWIZZLE_1:
475 value = get_one(dst_channel)
476 elif swizzle == SWIZZLE_NONE:
477 value = '0'
478 else:
479 assert False
480 print ' dst[%u] = %s; /* %s */' % (i, value, 'rgba'[i])
481
482
483 def generate_pack_kernel(format, src_channel, src_native_type):
484
485 if not is_format_supported(format):
486 return
487
488 dst_native_type = native_type(format)
489
490 assert format.layout == PLAIN
491
492 inv_swizzle = format.inv_swizzles()
493
494 if format.is_bitmask():
495 depth = format.block_size()
496 print ' uint%u_t value = 0;' % depth
497
498 shift = 0
499 for i in range(4):
500 dst_channel = format.channels[i]
501 if inv_swizzle[i] is not None:
502 value ='src[%u]' % inv_swizzle[i]
503 dst_colorspace = format.colorspace
504 if dst_colorspace == 'srgb' and inv_swizzle[i] == 3:
505 # Alpha channel is linear
506 dst_colorspace = 'rgb'
507 value = conversion_expr(src_channel,
508 dst_channel, dst_native_type,
509 value,
510 dst_colorspace = dst_colorspace)
511 if dst_channel.type in (UNSIGNED, SIGNED):
512 if shift + dst_channel.size < depth:
513 value = '(%s) & 0x%x' % (value, (1 << dst_channel.size) - 1)
514 if shift:
515 value = '(%s) << %u' % (value, shift)
516 if dst_channel.type == SIGNED:
517 # Cast to unsigned
518 value = '(uint%u_t)(%s) ' % (depth, value)
519 else:
520 value = None
521 if value is not None:
522 print ' value |= %s;' % (value)
523
524 shift += dst_channel.size
525
526 if depth > 8:
527 print '#ifdef PIPE_ARCH_BIG_ENDIAN'
528 print ' value = util_bswap%u(value);' % depth
529 print '#endif'
530
531 print ' *(uint%u_t *)dst = value;' % depth
532
533 else:
534 print ' union util_format_%s pixel;' % format.short_name()
535
536 for i in range(4):
537 dst_channel = format.channels[i]
538 width = dst_channel.size
539 if inv_swizzle[i] is None:
540 continue
541 dst_colorspace = format.colorspace
542 if dst_colorspace == 'srgb' and inv_swizzle[i] == 3:
543 # Alpha channel is linear
544 dst_colorspace = 'rgb'
545 value ='src[%u]' % inv_swizzle[i]
546 value = conversion_expr(src_channel,
547 dst_channel, dst_native_type,
548 value,
549 dst_colorspace = dst_colorspace)
550 print ' pixel.chan.%s = %s;' % (dst_channel.name, value)
551
552 bswap_format(format)
553 print ' memcpy(dst, &pixel, sizeof pixel);'
554
555
556 def generate_format_unpack(format, dst_channel, dst_native_type, dst_suffix):
557 '''Generate the function to unpack pixels from a particular format'''
558
559 name = format.short_name()
560
561 print 'static INLINE void'
562 print 'util_format_%s_unpack_%s(%s *dst_row, unsigned dst_stride, const uint8_t *src_row, unsigned src_stride, unsigned width, unsigned height)' % (name, dst_suffix, dst_native_type)
563 print '{'
564
565 if is_format_supported(format):
566 print ' unsigned x, y;'
567 print ' for(y = 0; y < height; y += %u) {' % (format.block_height,)
568 print ' %s *dst = dst_row;' % (dst_native_type)
569 print ' const uint8_t *src = src_row;'
570 print ' for(x = 0; x < width; x += %u) {' % (format.block_width,)
571
572 generate_unpack_kernel(format, dst_channel, dst_native_type)
573
574 print ' src += %u;' % (format.block_size() / 8,)
575 print ' dst += 4;'
576 print ' }'
577 print ' src_row += src_stride;'
578 print ' dst_row += dst_stride/sizeof(*dst_row);'
579 print ' }'
580
581 print '}'
582 print
583
584
585 def generate_format_pack(format, src_channel, src_native_type, src_suffix):
586 '''Generate the function to pack pixels to a particular format'''
587
588 name = format.short_name()
589
590 print 'static INLINE void'
591 print 'util_format_%s_pack_%s(uint8_t *dst_row, unsigned dst_stride, const %s *src_row, unsigned src_stride, unsigned width, unsigned height)' % (name, src_suffix, src_native_type)
592 print '{'
593
594 if is_format_supported(format):
595 print ' unsigned x, y;'
596 print ' for(y = 0; y < height; y += %u) {' % (format.block_height,)
597 print ' const %s *src = src_row;' % (src_native_type)
598 print ' uint8_t *dst = dst_row;'
599 print ' for(x = 0; x < width; x += %u) {' % (format.block_width,)
600
601 generate_pack_kernel(format, src_channel, src_native_type)
602
603 print ' src += 4;'
604 print ' dst += %u;' % (format.block_size() / 8,)
605 print ' }'
606 print ' dst_row += dst_stride;'
607 print ' src_row += src_stride/sizeof(*src_row);'
608 print ' }'
609
610 print '}'
611 print
612
613
614 def generate_format_fetch(format, dst_channel, dst_native_type, dst_suffix):
615 '''Generate the function to unpack pixels from a particular format'''
616
617 name = format.short_name()
618
619 print 'static INLINE void'
620 print 'util_format_%s_fetch_%s(%s *dst, const uint8_t *src, unsigned i, unsigned j)' % (name, dst_suffix, dst_native_type)
621 print '{'
622
623 if is_format_supported(format):
624 generate_unpack_kernel(format, dst_channel, dst_native_type)
625
626 print '}'
627 print
628
629
630 def is_format_hand_written(format):
631 return format.layout in ('s3tc', 'subsampled', 'other') or format.colorspace == ZS
632
633
634 def generate(formats):
635 print
636 print '#include "pipe/p_compiler.h"'
637 print '#include "u_math.h"'
638 print '#include "u_half.h"'
639 print '#include "u_format.h"'
640 print '#include "u_format_other.h"'
641 print '#include "u_format_srgb.h"'
642 print '#include "u_format_yuv.h"'
643 print '#include "u_format_zs.h"'
644 print
645
646 for format in formats:
647 if not is_format_hand_written(format):
648
649 if is_format_supported(format):
650 generate_format_type(format)
651
652 channel = Channel(FLOAT, False, 32)
653 native_type = 'float'
654 suffix = 'rgba_float'
655
656 generate_format_unpack(format, channel, native_type, suffix)
657 generate_format_pack(format, channel, native_type, suffix)
658 generate_format_fetch(format, channel, native_type, suffix)
659
660 channel = Channel(UNSIGNED, True, 8)
661 native_type = 'uint8_t'
662 suffix = 'rgba_8unorm'
663
664 generate_format_unpack(format, channel, native_type, suffix)
665 generate_format_pack(format, channel, native_type, suffix)
666