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