vc4: Introduce XML-based packet header generation like Intel's.
[mesa.git] / src / mesa / main / format_parser.py
1 #
2 # Copyright 2009 VMware, Inc.
3 # Copyright 2014 Intel Corporation
4 # All Rights Reserved.
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sub license, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
13 #
14 # The above copyright notice and this permission notice (including the
15 # next paragraph) shall be included in all copies or substantial portions
16 # of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 # IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 import sys
27
28 VOID = 'x'
29 UNSIGNED = 'u'
30 SIGNED = 's'
31 FLOAT = 'f'
32
33 ARRAY = 'array'
34 PACKED = 'packed'
35 OTHER = 'other'
36
37 RGB = 'rgb'
38 SRGB = 'srgb'
39 YUV = 'yuv'
40 ZS = 'zs'
41
42 VERY_LARGE = 99999999999999999999999
43
44 class Channel:
45 """Describes a color channel."""
46
47 def __init__(self, type, norm, size):
48 self.type = type
49 self.norm = norm
50 self.size = size
51 self.sign = type in (SIGNED, FLOAT)
52 self.name = None # Set when the channels are added to the format
53 self.shift = -1 # Set when the channels are added to the format
54 self.index = -1 # Set when the channels are added to the format
55
56 def __str__(self):
57 s = str(self.type)
58 if self.norm:
59 s += 'n'
60 s += str(self.size)
61 return s
62
63 def __eq__(self, other):
64 return self.type == other.type and self.norm == other.norm and self.size == other.size
65
66 def max(self):
67 """Returns the maximum representable number."""
68 if self.type == FLOAT:
69 return VERY_LARGE
70 if self.norm:
71 return 1
72 if self.type == UNSIGNED:
73 return (1 << self.size) - 1
74 if self.type == SIGNED:
75 return (1 << (self.size - 1)) - 1
76 assert False
77
78 def min(self):
79 """Returns the minimum representable number."""
80 if self.type == FLOAT:
81 return -VERY_LARGE
82 if self.type == UNSIGNED:
83 return 0
84 if self.norm:
85 return -1
86 if self.type == SIGNED:
87 return -(1 << (self.size - 1))
88 assert False
89
90 def one(self):
91 """Returns the value that represents 1.0f."""
92 if self.type == UNSIGNED:
93 return (1 << self.size) - 1
94 if self.type == SIGNED:
95 return (1 << (self.size - 1)) - 1
96 else:
97 return 1
98
99 def datatype(self):
100 """Returns the datatype corresponding to a channel type and size"""
101 return _get_datatype(self.type, self.size)
102
103 class Swizzle:
104 """Describes a swizzle operation.
105
106 A Swizzle is a mapping from one set of channels in one format to the
107 channels in another. Each channel in the destination format is
108 associated with one of the following constants:
109
110 * SWIZZLE_X: The first channel in the source format
111 * SWIZZLE_Y: The second channel in the source format
112 * SWIZZLE_Z: The third channel in the source format
113 * SWIZZLE_W: The fourth channel in the source format
114 * SWIZZLE_ZERO: The numeric constant 0
115 * SWIZZLE_ONE: THe numeric constant 1
116 * SWIZZLE_NONE: No data available for this channel
117
118 Sometimes a Swizzle is represented by a 4-character string. In this
119 case, the source channels are represented by the characters "x", "y",
120 "z", and "w"; the numeric constants are represented as "0" and "1"; and
121 no mapping is represented by "_". For instance, the map from
122 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
123 channels maps to the first luminance-alpha channel and the alpha channel
124 maps to second luminance-alpha channel. The mapping from bgr to rgba is
125 given by "zyx1" because the first three colors are reversed and alpha is
126 always 1.
127 """
128
129 __identity_str = 'xyzw01_'
130
131 SWIZZLE_X = 0
132 SWIZZLE_Y = 1
133 SWIZZLE_Z = 2
134 SWIZZLE_W = 3
135 SWIZZLE_ZERO = 4
136 SWIZZLE_ONE = 5
137 SWIZZLE_NONE = 6
138
139 def __init__(self, swizzle):
140 """Creates a Swizzle object from a string or array."""
141 if isinstance(swizzle, str):
142 swizzle = [Swizzle.__identity_str.index(c) for c in swizzle]
143 else:
144 swizzle = list(swizzle)
145 for s in swizzle:
146 assert isinstance(s, int) and 0 <= s and s <= Swizzle.SWIZZLE_NONE
147
148 assert len(swizzle) <= 4
149
150 self.__list = swizzle + [Swizzle.SWIZZLE_NONE] * (4 - len(swizzle))
151 assert len(self.__list) == 4
152
153 def __iter__(self):
154 """Returns an iterator that iterates over this Swizzle.
155
156 The values that the iterator produces are described by the SWIZZLE_*
157 constants.
158 """
159 return self.__list.__iter__()
160
161 def __str__(self):
162 """Returns a string representation of this Swizzle."""
163 return ''.join(Swizzle.__identity_str[i] for i in self.__list)
164
165 def __getitem__(self, idx):
166 """Returns the SWIZZLE_* constant for the given destination channel.
167
168 Valid values for the destination channel include any of the SWIZZLE_*
169 constants or any of the following single-character strings: "x", "y",
170 "z", "w", "r", "g", "b", "a", "z" "s".
171 """
172
173 if isinstance(idx, int):
174 assert idx >= Swizzle.SWIZZLE_X and idx <= Swizzle.SWIZZLE_NONE
175 if idx <= Swizzle.SWIZZLE_W:
176 return self.__list.__getitem__(idx)
177 else:
178 return idx
179 elif isinstance(idx, str):
180 if idx in 'xyzw':
181 idx = 'xyzw'.find(idx)
182 elif idx in 'rgba':
183 idx = 'rgba'.find(idx)
184 elif idx in 'zs':
185 idx = 'zs'.find(idx)
186 else:
187 assert False
188 return self.__list.__getitem__(idx)
189 else:
190 assert False
191
192 def __mul__(self, other):
193 """Returns the composition of this Swizzle with another Swizzle.
194
195 The resulting swizzle is such that, for any valid input to
196 __getitem__, (a * b)[i] = a[b[i]].
197 """
198 assert isinstance(other, Swizzle)
199 return Swizzle(self[x] for x in other)
200
201 def inverse(self):
202 """Returns a pseudo-inverse of this swizzle.
203
204 Since swizzling isn't necisaraly a bijection, a Swizzle can never
205 be truely inverted. However, the swizzle returned is *almost* the
206 inverse of this swizzle in the sense that, for each i in range(3),
207 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
208 a permutation with no channels added or removed, then this
209 function returns the actual inverse.
210
211 This "pseudo-inverse" idea can be demonstrated by mapping from
212 luminance-alpha to rgba that is given by "xxxy". To get from rgba
213 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
214 This maps the first component in the lumanence-alpha texture is
215 the red component of the rgba image and the second to the alpha
216 component, exactly as you would expect.
217 """
218 rev = [Swizzle.SWIZZLE_NONE] * 4
219 for i in xrange(4):
220 for j in xrange(4):
221 if self.__list[j] == i and rev[i] == Swizzle.SWIZZLE_NONE:
222 rev[i] = j
223 return Swizzle(rev)
224
225
226 class Format:
227 """Describes a pixel format."""
228
229 def __init__(self, name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace):
230 """Constructs a Format from some metadata and a list of channels.
231
232 The channel objects must be unique to this Format and should not be
233 re-used to construct another Format. This is because certain channel
234 information such as shift, offset, and the channel name are set when
235 the Format is created and are calculated based on the entire list of
236 channels.
237
238 Arguments:
239 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
240 layout -- One of 'array', 'packed' 'other', or a compressed layout
241 block_width -- The block width if the format is compressed, 1 otherwise
242 block_height -- The block height if the format is compressed, 1 otherwise
243 block_depth -- The block depth if the format is compressed, 1 otherwise
244 channels -- A list of Channel objects
245 swizzle -- A Swizzle from this format to rgba
246 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
247 """
248 self.name = name
249 self.layout = layout
250 self.block_width = block_width
251 self.block_height = block_height
252 self.block_depth = block_depth
253 self.channels = channels
254 assert isinstance(swizzle, Swizzle)
255 self.swizzle = swizzle
256 self.name = name
257 assert colorspace in (RGB, SRGB, YUV, ZS)
258 self.colorspace = colorspace
259
260 # Name the channels
261 chan_names = ['']*4
262 if self.colorspace in (RGB, SRGB):
263 for (i, s) in enumerate(swizzle):
264 if s < 4:
265 chan_names[s] += 'rgba'[i]
266 elif colorspace == ZS:
267 for (i, s) in enumerate(swizzle):
268 if s < 4:
269 chan_names[s] += 'zs'[i]
270 else:
271 chan_names = ['x', 'y', 'z', 'w']
272
273 for c, name in zip(self.channels, chan_names):
274 assert c.name is None
275 if name == 'rgb':
276 c.name = 'l'
277 elif name == 'rgba':
278 c.name = 'i'
279 elif name == '':
280 c.name = 'x'
281 else:
282 c.name = name
283
284 # Set indices and offsets
285 if self.layout == PACKED:
286 shift = 0
287 for channel in self.channels:
288 assert channel.shift == -1
289 channel.shift = shift
290 shift += channel.size
291 for idx, channel in enumerate(self.channels):
292 assert channel.index == -1
293 channel.index = idx
294 else:
295 pass # Shift means nothing here
296
297 def __str__(self):
298 return self.name
299
300 def short_name(self):
301 """Returns a short name for a format.
302
303 The short name should be suitable to be used as suffix in function
304 names.
305 """
306
307 name = self.name
308 if name.startswith('MESA_FORMAT_'):
309 name = name[len('MESA_FORMAT_'):]
310 name = name.lower()
311 return name
312
313 def block_size(self):
314 """Returns the block size (in bits) of the format."""
315 size = 0
316 for channel in self.channels:
317 size += channel.size
318 return size
319
320 def num_channels(self):
321 """Returns the number of channels in the format."""
322 nr_channels = 0
323 for channel in self.channels:
324 if channel.size:
325 nr_channels += 1
326 return nr_channels
327
328 def array_element(self):
329 """Returns a non-void channel if this format is an array, otherwise None.
330
331 If the returned channel is not None, then this format can be
332 considered to be an array of num_channels() channels identical to the
333 returned channel.
334 """
335 if self.layout == ARRAY:
336 return self.channels[0]
337 elif self.layout == PACKED:
338 ref_channel = self.channels[0]
339 if ref_channel.type == VOID:
340 ref_channel = self.channels[1]
341 for channel in self.channels:
342 if channel.size == 0 or channel.type == VOID:
343 continue
344 if channel.size != ref_channel.size or channel.size % 8 != 0:
345 return None
346 if channel.type != ref_channel.type:
347 return None
348 if channel.norm != ref_channel.norm:
349 return None
350 return ref_channel
351 else:
352 return None
353
354 def is_array(self):
355 """Returns true if this format can be considered an array format.
356
357 This function will return true if self.layout == 'array'. However,
358 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
359 array formats even though they are technically packed.
360 """
361 return self.array_element() != None
362
363 def is_compressed(self):
364 """Returns true if this is a compressed format."""
365 return self.block_width != 1 or self.block_height != 1 or self.block_depth != 1
366
367 def is_int(self):
368 """Returns true if this format is an integer format.
369
370 See also: is_norm()
371 """
372 if self.layout not in (ARRAY, PACKED):
373 return False
374 for channel in self.channels:
375 if channel.type not in (VOID, UNSIGNED, SIGNED):
376 return False
377 return True
378
379 def is_float(self):
380 """Returns true if this format is an floating-point format."""
381 if self.layout not in (ARRAY, PACKED):
382 return False
383 for channel in self.channels:
384 if channel.type not in (VOID, FLOAT):
385 return False
386 return True
387
388 def channel_type(self):
389 """Returns the type of the channels in this format."""
390 _type = VOID
391 for c in self.channels:
392 if c.type == VOID:
393 continue
394 if _type == VOID:
395 _type = c.type
396 assert c.type == _type
397 return _type
398
399 def channel_size(self):
400 """Returns the size (in bits) of the channels in this format.
401
402 This function should only be called if all of the channels have the
403 same size. This is always the case if is_array() returns true.
404 """
405 size = None
406 for c in self.channels:
407 if c.type == VOID:
408 continue
409 if size is None:
410 size = c.size
411 assert c.size == size
412 return size
413
414 def max_channel_size(self):
415 """Returns the size of the largest channel."""
416 size = 0
417 for c in self.channels:
418 if c.type == VOID:
419 continue
420 size = max(size, c.size)
421 return size
422
423 def is_normalized(self):
424 """Returns true if this format is normalized.
425
426 While only integer formats can be normalized, not all integer formats
427 are normalized. Normalized integer formats are those where the
428 integer value is re-interpreted as a fixed point value in the range
429 [0, 1].
430 """
431 norm = None
432 for c in self.channels:
433 if c.type == VOID:
434 continue
435 if norm is None:
436 norm = c.norm
437 assert c.norm == norm
438 return norm
439
440 def has_channel(self, name):
441 """Returns true if this format has the given channel."""
442 if self.is_compressed():
443 # Compressed formats are a bit tricky because the list of channels
444 # contains a single channel of type void. Since we don't have any
445 # channel information there, we pull it from the swizzle.
446 if str(self.swizzle) == 'xxxx':
447 return name == 'i'
448 elif str(self.swizzle)[0:3] in ('xxx', 'yyy'):
449 if name == 'l':
450 return True
451 elif name == 'a':
452 return self.swizzle['a'] <= Swizzle.SWIZZLE_W
453 else:
454 return False
455 elif name in 'rgba':
456 return self.swizzle[name] <= Swizzle.SWIZZLE_W
457 else:
458 return False
459 else:
460 for channel in self.channels:
461 if channel.name == name:
462 return True
463 return False
464
465 def get_channel(self, name):
466 """Returns the channel with the given name if it exists."""
467 for channel in self.channels:
468 if channel.name == name:
469 return channel
470 return None
471
472 def datatype(self):
473 """Returns the datatype corresponding to a format's channel type and size"""
474 if self.layout == PACKED:
475 if self.block_size() == 8:
476 return 'uint8_t'
477 if self.block_size() == 16:
478 return 'uint16_t'
479 if self.block_size() == 32:
480 return 'uint32_t'
481 else:
482 assert False
483 else:
484 return _get_datatype(self.channel_type(), self.channel_size())
485
486 def _get_datatype(type, size):
487 if type == FLOAT:
488 if size == 32:
489 return 'float'
490 elif size == 16:
491 return 'uint16_t'
492 else:
493 assert False
494 elif type == UNSIGNED:
495 if size <= 8:
496 return 'uint8_t'
497 elif size <= 16:
498 return 'uint16_t'
499 elif size <= 32:
500 return 'uint32_t'
501 else:
502 assert False
503 elif type == SIGNED:
504 if size <= 8:
505 return 'int8_t'
506 elif size <= 16:
507 return 'int16_t'
508 elif size <= 32:
509 return 'int32_t'
510 else:
511 assert False
512 else:
513 assert False
514
515 def _parse_channels(fields, layout, colorspace, swizzle):
516 channels = []
517 for field in fields:
518 if not field:
519 continue
520
521 type = field[0] if field[0] else 'x'
522
523 if field[1] == 'n':
524 norm = True
525 size = int(field[2:])
526 else:
527 norm = False
528 size = int(field[1:])
529
530 channel = Channel(type, norm, size)
531 channels.append(channel)
532
533 return channels
534
535 def parse(filename):
536 """Parse a format description in CSV format.
537
538 This function parses the given CSV file and returns an iterable of
539 channels."""
540
541 with open(filename) as stream:
542 for line in stream:
543 try:
544 comment = line.index('#')
545 except ValueError:
546 pass
547 else:
548 line = line[:comment]
549 line = line.strip()
550 if not line:
551 continue
552
553 fields = [field.strip() for field in line.split(',')]
554
555 name = fields[0]
556 layout = fields[1]
557 block_width = int(fields[2])
558 block_height = int(fields[3])
559 block_depth = int(fields[4])
560 colorspace = fields[10]
561
562 try:
563 swizzle = Swizzle(fields[9])
564 except:
565 sys.exit("error parsing swizzle for format " + name)
566 channels = _parse_channels(fields[5:9], layout, colorspace, swizzle)
567
568 yield Format(name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace)