1 2''' 3/************************************************************************** 4 * 5 * Copyright 2009 VMware, Inc. 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 23 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29''' 30 31 32import copy 33import yaml 34import sys 35 36try: 37 from yaml import CSafeLoader as YAMLSafeLoader 38except: 39 from yaml import SafeLoader as YAMLSafeLoader 40 41VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) 42 43SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) 44 45PLAIN = 'plain' 46 47RGB = 'RGB' 48SRGB = 'SRGB' 49YUV = 'YUV' 50ZS = 'ZS' 51 52 53_type_parse_map = { 54 '': VOID, 55 'X': VOID, 56 'U': UNSIGNED, 57 'S': SIGNED, 58 'H': FIXED, 59 'F': FLOAT, 60} 61 62_swizzle_parse_map = { 63 'X': SWIZZLE_X, 64 'Y': SWIZZLE_Y, 65 'Z': SWIZZLE_Z, 66 'W': SWIZZLE_W, 67 '0': SWIZZLE_0, 68 '1': SWIZZLE_1, 69 '_': SWIZZLE_NONE, 70} 71 72 73def is_pot(x): 74 return (x & (x - 1)) == 0 75 76 77VERY_LARGE = 99999999999999999999999 78 79def validate_str(x): 80 if not isinstance(x, str): 81 raise ValueError(type(x)) 82 83def validate_int(x): 84 if not isinstance(x, int): 85 raise ValueError(f"invalid type {type(x)}") 86 87def validate_list_str_4(x): 88 if not isinstance(x, list): 89 raise ValueError(f"invalid type {type(x)}") 90 if len(x) != 4: 91 raise ValueError(f"invalid length {len(x)}") 92 for i in range(len(x)): 93 if isinstance(x[i], int): 94 x[i] = str(x[i]) 95 if not isinstance(x[i], str): 96 raise ValueError(f"invalid member type {type(x[i])}") 97 98def validate_list_str_le4(x): 99 if not isinstance(x, list): 100 raise ValueError(f"invalid type {type(x)}") 101 if len(x) > 4: 102 raise ValueError(f"invalid length {len(x)}") 103 for i in range(len(x)): 104 if isinstance(x[i], int): 105 x[i] = str(x[i]) 106 if not isinstance(x[i], str): 107 raise ValueError(f"invalid member type {type(x[i])}") 108 109 110def get_and_delete(d, k): 111 ret = d[k] 112 del(d[k]) 113 return ret 114 115def do_consume(d, *args): 116 if len(args) == 1: 117 return get_and_delete(d, args[0]) 118 else: 119 return do_consume(d[args[0]], *args[1:]) 120 121def consume(f, validate, d, *args): 122 if len(args) > 1: 123 sub = " under " + ".".join([f"'{a}'" for a in args[:-1]]) 124 else: 125 sub = "" 126 127 try: 128 ret = do_consume(d, *args) 129 validate(ret) 130 return ret 131 except KeyError: 132 raise RuntimeError(f"Key '{args[-1]}' not present{sub} in format {f.name}") 133 except ValueError as e: 134 raise RuntimeError(f"Key '{args[-1]}' invalid{sub} in format {f.name}: {e.args[0]}") 135 136def consume_str(f, d, *args): 137 return consume(f, validate_str, d, *args) 138 139def consume_int(f, d, *args): 140 return consume(f, validate_int, d, *args) 141 142def consume_list_str_4(f, d, *args): 143 return consume(f, validate_list_str_4, d, *args) 144 145def consume_list_str_le4(f, d, *args): 146 return consume(f, validate_list_str_le4, d, *args) 147 148def consumed(f, d, *args): 149 if args: 150 d = do_consume(d, *args) 151 if len(d) > 0: 152 keys = ", ".join([f"'{k}'" for k in d.keys()]) 153 if args: 154 sub = " under " + ".".join([f"'{a}'" for a in args]) 155 else: 156 sub = "" 157 raise RuntimeError(f"Unknown keys ({keys}) present in format {f.name}{sub}") 158 159 160class Channel: 161 '''Describe the channel of a color channel.''' 162 163 def __init__(self, type, norm, pure, size, name=''): 164 self.type = type 165 self.norm = norm 166 self.pure = pure 167 self.size = size 168 self.sign = type in (SIGNED, FIXED, FLOAT) 169 self.name = name 170 171 def __str__(self): 172 s = str(self.type) 173 if self.norm: 174 s += 'n' 175 if self.pure: 176 s += 'p' 177 s += str(self.size) 178 return s 179 180 def __repr__(self): 181 return "Channel({})".format(self.__str__()) 182 183 def __eq__(self, other): 184 if other is None: 185 return False 186 187 return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size 188 189 def __ne__(self, other): 190 return not self == other 191 192 def max(self): 193 '''Maximum representable number.''' 194 if self.type == FLOAT: 195 return VERY_LARGE 196 if self.type == FIXED: 197 return (1 << (self.size // 2)) - 1 198 if self.norm: 199 return 1 200 if self.type == UNSIGNED: 201 return (1 << self.size) - 1 202 if self.type == SIGNED: 203 return (1 << (self.size - 1)) - 1 204 assert False 205 206 def min(self): 207 '''Minimum representable number.''' 208 if self.type == FLOAT: 209 return -VERY_LARGE 210 if self.type == FIXED: 211 return -(1 << (self.size // 2)) 212 if self.type == UNSIGNED: 213 return 0 214 if self.norm: 215 return -1 216 if self.type == SIGNED: 217 return -(1 << (self.size - 1)) 218 assert False 219 220 221class Format: 222 '''Describe a pixel format.''' 223 224 def __init__(self, source): 225 self.name = "unknown" 226 self.name = f"PIPE_FORMAT_{consume_str(self, source, 'name')}" 227 self.layout = consume_str(self, source, 'layout') 228 if 'sublayout' in source: 229 self.sublayout = consume_str(self, source, 'sublayout') 230 else: 231 self.sublayout = None 232 self.block_width = consume_int(self, source, 'block', 'width') 233 self.block_height = consume_int(self, source, 'block', 'height') 234 self.block_depth = consume_int(self, source, 'block', 'depth') 235 consumed(self, source, 'block') 236 self.colorspace = consume_str(self, source, 'colorspace') 237 self.srgb_equivalent = None 238 self.linear_equivalent = None 239 240 # Formats with no endian-dependent swizzling declare their channel and 241 # swizzle layout at the top level. Else they can declare an 242 # endian-dependent swizzle. This only applies to packed formats, 243 # however we can't use is_array() or is_bitmask() to test because they 244 # depend on the channels having already been parsed. 245 if 'swizzles' in source: 246 self.le_swizzles = list(map(lambda x: _swizzle_parse_map[x], 247 consume_list_str_4(self, source, 'swizzles'))) 248 self.le_channels = _parse_channels(consume_list_str_le4(self, source, 'channels'), 249 self.layout, self.colorspace, self.le_swizzles) 250 self.be_swizzles = None 251 self.be_channels = None 252 if source.get('little_endian', {}).get('swizzles') or \ 253 source.get('big_endian', {}).get('swizzles'): 254 raise RuntimeError(f"Format {self.name} must not declare endian-dependent and endian-independent swizzles") 255 else: 256 self.le_swizzles = list(map(lambda x: _swizzle_parse_map[x], 257 consume_list_str_4(self, source, 'little_endian', 'swizzles'))) 258 self.le_channels = _parse_channels(consume_list_str_le4(self, source, 'little_endian', 'channels'), 259 self.layout, self.colorspace, self.le_swizzles) 260 self.be_swizzles = list(map(lambda x: _swizzle_parse_map[x], 261 consume_list_str_4(self, source, 'big_endian', 'swizzles'))) 262 self.be_channels = _parse_channels(consume_list_str_le4(self, source, 'big_endian', 'channels'), 263 self.layout, self.colorspace, self.be_swizzles) 264 if self.is_array(): 265 raise RuntimeError("Array format {self.name} must not define endian-specific swizzles") 266 if self.is_bitmask(): 267 raise RuntimeError("Bitmask format {self.name} must not define endian-specific swizzles") 268 269 self.le_alias = None 270 self.be_alias = None 271 if 'little_endian' in source: 272 if 'alias' in source['little_endian']: 273 self.le_alias = f"PIPE_FORMAT_{consume_str(self, source, 'little_endian', 'alias')}" 274 consumed(self, source, 'little_endian') 275 if 'big_endian' in source: 276 if 'alias' in source['big_endian']: 277 self.be_alias = f"PIPE_FORMAT_{consume_str(self, source, 'big_endian', 'alias')}" 278 consumed(self, source, 'big_endian') 279 280 consumed(self, source) 281 del(source) 282 283 if self.is_bitmask() and not self.is_array(): 284 # Bitmask formats are "load a word the size of the block and 285 # bitshift channels out of it." However, the channel shifts 286 # defined in u_format_table.c are numbered right-to-left on BE 287 # for some historical reason (see below), which is hard to 288 # change due to llvmpipe, so we also have to flip the channel 289 # order and the channel-to-rgba swizzle values to read 290 # right-to-left from the defined (non-VOID) channels so that the 291 # correct shifts happen. 292 # 293 # This is nonsense, but it's the nonsense that makes 294 # u_format_test pass and you get the right colors in softpipe at 295 # least. 296 chans = self.nr_channels() 297 self.be_channels = self.le_channels[chans - 298 1::-1] + self.le_channels[chans:4] 299 300 xyzw = [SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W] 301 chan_map = {SWIZZLE_X: xyzw[chans - 1] if chans >= 1 else SWIZZLE_X, 302 SWIZZLE_Y: xyzw[chans - 2] if chans >= 2 else SWIZZLE_X, 303 SWIZZLE_Z: xyzw[chans - 3] if chans >= 3 else SWIZZLE_X, 304 SWIZZLE_W: xyzw[chans - 4] if chans >= 4 else SWIZZLE_X, 305 SWIZZLE_1: SWIZZLE_1, 306 SWIZZLE_0: SWIZZLE_0, 307 SWIZZLE_NONE: SWIZZLE_NONE} 308 self.be_swizzles = [chan_map[s] for s in self.le_swizzles] 309 elif not self.be_channels: 310 self.be_channels = copy.deepcopy(self.le_channels) 311 self.be_swizzles = self.le_swizzles 312 313 le_shift = 0 314 for channel in self.le_channels: 315 channel.shift = le_shift 316 le_shift += channel.size 317 318 be_shift = 0 319 for channel in reversed(self.be_channels): 320 channel.shift = be_shift 321 be_shift += channel.size 322 323 assert le_shift == be_shift 324 for i in range(4): 325 assert (self.le_swizzles[i] != SWIZZLE_NONE) == ( 326 self.be_swizzles[i] != SWIZZLE_NONE) 327 328 def __str__(self): 329 return self.name 330 331 def __eq__(self, other): 332 if not other: 333 return False 334 return self.name == other.name 335 336 def __hash__(self): 337 return hash(self.name) 338 339 def short_name(self): 340 '''Make up a short norm for a format, suitable to be used as suffix in 341 function names.''' 342 343 name = self.name 344 if name.startswith('PIPE_FORMAT_'): 345 name = name[len('PIPE_FORMAT_'):] 346 name = name.lower() 347 return name 348 349 def block_size(self): 350 size = 0 351 for channel in self.le_channels: 352 size += channel.size 353 return size 354 355 def nr_channels(self): 356 nr_channels = 0 357 for channel in self.le_channels: 358 if channel.size: 359 nr_channels += 1 360 return nr_channels 361 362 def array_element(self): 363 if self.layout != PLAIN: 364 return None 365 ref_channel = self.le_channels[0] 366 if ref_channel.type == VOID: 367 ref_channel = self.le_channels[1] 368 for channel in self.le_channels: 369 if channel.size and (channel.size != ref_channel.size or channel.size % 8): 370 return None 371 if channel.type != VOID: 372 if channel.type != ref_channel.type: 373 return None 374 if channel.norm != ref_channel.norm: 375 return None 376 if channel.pure != ref_channel.pure: 377 return None 378 return ref_channel 379 380 def is_array(self): 381 return self.array_element() != None 382 383 def is_mixed(self): 384 if self.layout != PLAIN: 385 return False 386 ref_channel = self.le_channels[0] 387 if ref_channel.type == VOID: 388 ref_channel = self.le_channels[1] 389 for channel in self.le_channels[1:]: 390 if channel.type != VOID: 391 if channel.type != ref_channel.type: 392 return True 393 if channel.norm != ref_channel.norm: 394 return True 395 if channel.pure != ref_channel.pure: 396 return True 397 return False 398 399 def is_compressed(self): 400 for channel in self.le_channels: 401 if channel.type != VOID: 402 return False 403 return True 404 405 def is_unorm(self): 406 # Non-compressed formats all have unorm or srgb in their name. 407 for keyword in ['_UNORM', '_SRGB']: 408 if keyword in self.name: 409 return True 410 411 # All the compressed formats in GLES3.2 and GL4.6 ("Table 8.14: Generic 412 # and specific compressed internal formats.") that aren't snorm for 413 # border colors are unorm, other than BPTC_*_FLOAT. 414 return self.is_compressed() and not ('FLOAT' in self.name or self.is_snorm()) 415 416 def is_snorm(self): 417 return '_SNORM' in self.name 418 419 def is_pot(self): 420 return is_pot(self.block_size()) 421 422 def is_int(self): 423 if self.layout != PLAIN: 424 return False 425 for channel in self.le_channels: 426 if channel.type not in (VOID, UNSIGNED, SIGNED): 427 return False 428 return True 429 430 def is_float(self): 431 if self.layout != PLAIN: 432 return False 433 for channel in self.le_channels: 434 if channel.type not in (VOID, FLOAT): 435 return False 436 return True 437 438 def is_bitmask(self): 439 if self.layout != PLAIN: 440 return False 441 if self.block_size() not in (8, 16, 32): 442 return False 443 for channel in self.le_channels: 444 if channel.type not in (VOID, UNSIGNED, SIGNED): 445 return False 446 return True 447 448 def is_pure_color(self): 449 if self.layout != PLAIN or self.colorspace == ZS: 450 return False 451 pures = [channel.pure 452 for channel in self.le_channels 453 if channel.type != VOID] 454 for x in pures: 455 assert x == pures[0] 456 return pures[0] 457 458 def channel_type(self): 459 types = [channel.type 460 for channel in self.le_channels 461 if channel.type != VOID] 462 for x in types: 463 assert x == types[0] 464 return types[0] 465 466 def is_pure_signed(self): 467 return self.is_pure_color() and self.channel_type() == SIGNED 468 469 def is_pure_unsigned(self): 470 return self.is_pure_color() and self.channel_type() == UNSIGNED 471 472 def has_channel(self, id): 473 return self.le_swizzles[id] != SWIZZLE_NONE 474 475 def has_depth(self): 476 return self.colorspace == ZS and self.has_channel(0) 477 478 def has_stencil(self): 479 return self.colorspace == ZS and self.has_channel(1) 480 481 def stride(self): 482 return self.block_size()/8 483 484 485 486def _parse_channels(fields, layout, colorspace, swizzles): 487 if layout == PLAIN: 488 names = ['']*4 489 if colorspace in (RGB, SRGB): 490 for i in range(4): 491 swizzle = swizzles[i] 492 if swizzle < 4: 493 names[swizzle] += 'rgba'[i] 494 elif colorspace == ZS: 495 for i in range(4): 496 swizzle = swizzles[i] 497 if swizzle < 4: 498 names[swizzle] += 'zs'[i] 499 else: 500 assert False 501 for i in range(4): 502 if names[i] == '': 503 names[i] = 'x' 504 else: 505 names = ['x', 'y', 'z', 'w'] 506 507 channels = [] 508 for i in range(0, 4): 509 if i < len(fields): 510 field = fields[i] 511 type = _type_parse_map[field[0]] 512 if field[1] == 'N': 513 norm = True 514 pure = False 515 size = int(field[2:]) 516 elif field[1] == 'P': 517 pure = True 518 norm = False 519 size = int(field[2:]) 520 else: 521 norm = False 522 pure = False 523 size = int(field[1:]) 524 else: 525 type = VOID 526 norm = False 527 pure = False 528 size = 0 529 channel = Channel(type, norm, pure, size, names[i]) 530 channels.append(channel) 531 532 return channels 533 534def mostly_equivalent(one, two): 535 if one.layout != two.layout or \ 536 one.sublayout != two.sublayout or \ 537 one.block_width != two.block_width or \ 538 one.block_height != two.block_height or \ 539 one.block_depth != two.block_depth or \ 540 one.le_swizzles != two.le_swizzles or \ 541 one.le_channels != two.le_channels or \ 542 one.be_swizzles != two.be_swizzles or \ 543 one.be_channels != two.be_channels: 544 return False 545 return True 546 547def should_ignore_for_mapping(fmt): 548 # This format is a really special reinterpretation of depth/stencil as 549 # RGB. Until we figure out something better, just special-case it so 550 # we won't consider it as equivalent to anything. 551 if fmt.name == "PIPE_FORMAT_Z24_UNORM_S8_UINT_AS_R8G8B8A8": 552 return True 553 return False 554 555 556def parse(filename): 557 '''Parse the format description in YAML format in terms of the 558 Channel and Format classes above.''' 559 560 stream = open(filename) 561 doc = yaml.load(stream, Loader=YAMLSafeLoader) 562 assert(isinstance(doc, list)) 563 564 ret = [] 565 for entry in doc: 566 assert(isinstance(entry, dict)) 567 try: 568 f = Format(copy.deepcopy(entry)) 569 except Exception as e: 570 raise RuntimeError(f"Failed to parse entry {entry}: {e}") 571 if f in ret: 572 raise RuntimeError(f"Duplicate format entry {f.name}") 573 ret.append(f) 574 575 for fmt in ret: 576 if should_ignore_for_mapping(fmt): 577 continue 578 if fmt.colorspace != RGB and fmt.colorspace != SRGB: 579 continue 580 if fmt.colorspace == RGB: 581 for equiv in ret: 582 if equiv.colorspace != SRGB or not mostly_equivalent(fmt, equiv) or \ 583 should_ignore_for_mapping(equiv): 584 continue 585 assert(fmt.srgb_equivalent == None) 586 fmt.srgb_equivalent = equiv 587 elif fmt.colorspace == SRGB: 588 for equiv in ret: 589 if equiv.colorspace != RGB or not mostly_equivalent(fmt, equiv) or \ 590 should_ignore_for_mapping(equiv): 591 continue 592 assert(fmt.linear_equivalent == None) 593 fmt.linear_equivalent = equiv 594 595 return ret 596