1# Copyright © 2017 Intel Corporation 2 3# Permission is hereby granted, free of charge, to any person obtaining a copy 4# of this software and associated documentation files (the "Software"), to deal 5# in the Software without restriction, including without limitation the rights 6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7# copies of the Software, and to permit persons to whom the Software is 8# furnished to do so, subject to the following conditions: 9 10# The above copyright notice and this permission notice shall be included in 11# all copies or substantial portions of the Software. 12 13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19# SOFTWARE. 20 21"""Create enum to string functions for vulkan using vk.xml.""" 22 23import argparse 24import functools 25import os 26import re 27import textwrap 28import xml.etree.ElementTree as et 29 30from mako.template import Template 31from vk_extensions import Extension, filter_api, get_all_required 32 33COPYRIGHT = textwrap.dedent(u"""\ 34 * Copyright © 2017 Intel Corporation 35 * 36 * Permission is hereby granted, free of charge, to any person obtaining a copy 37 * of this software and associated documentation files (the "Software"), to deal 38 * in the Software without restriction, including without limitation the rights 39 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 40 * copies of the Software, and to permit persons to whom the Software is 41 * furnished to do so, subject to the following conditions: 42 * 43 * The above copyright notice and this permission notice shall be included in 44 * all copies or substantial portions of the Software. 45 * 46 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 49 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 52 * SOFTWARE.""") 53 54C_TEMPLATE = Template(textwrap.dedent(u"""\ 55 /* Autogenerated file -- do not edit 56 * generated by ${file} 57 * 58 ${copyright} 59 */ 60 61 #include <string.h> 62 #include <vulkan/vulkan_core.h> 63 #include <vulkan/vk_android_native_buffer.h> 64 #include <vulkan/vk_layer.h> 65 #include "util/macros.h" 66 #include "vk_enum_to_str.h" 67 68 % for enum in enums: 69 70 % if enum.guard: 71#ifdef ${enum.guard} 72 % endif 73 const char * 74 vk_${enum.name[2:]}_to_str(${enum.name} input) 75 { 76 switch((int64_t)input) { 77 % for v in sorted(enum.values.keys()): 78 case ${v}: 79 return "${enum.values[v]}"; 80 % endfor 81 case ${enum.max_enum_name}: return "${enum.max_enum_name}"; 82 default: 83 return "Unknown ${enum.name} value."; 84 } 85 } 86 87 % if enum.guard: 88#endif 89 % endif 90 %endfor 91 92 % for enum in bitmasks: 93 94 % if enum.guard: 95#ifdef ${enum.guard} 96 % endif 97 const char * 98 vk_${enum.name[2:]}_to_str(${enum.name} input) 99 { 100 switch((int64_t)input) { 101 % for v in sorted(enum.values.keys()): 102 case ${v}: 103 return "${enum.values[v]}"; 104 % endfor 105 default: 106 return "Unknown ${enum.name} value."; 107 } 108 } 109 110 % if enum.guard: 111#endif 112 % endif 113 %endfor 114 115 size_t vk_structure_type_size(const struct VkBaseInStructure *item) 116 { 117 switch((int)item->sType) { 118 % for struct in structs: 119 % if struct.extension is not None and struct.extension.define is not None: 120 #ifdef ${struct.extension.define} 121 case ${struct.stype}: return sizeof(${struct.name}); 122 #endif 123 % else: 124 case ${struct.stype}: return sizeof(${struct.name}); 125 % endif 126 %endfor 127 case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: return sizeof(VkLayerInstanceCreateInfo); 128 case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: return sizeof(VkLayerDeviceCreateInfo); 129 default: 130 unreachable("Undefined struct type."); 131 } 132 } 133 134 const char * 135 vk_ObjectType_to_ObjectName(VkObjectType type) 136 { 137 switch((int)type) { 138 % for object_type in sorted(object_types[0].enum_to_name.keys()): 139 case ${object_type}: 140 return "${object_types[0].enum_to_name[object_type]}"; 141 % endfor 142 default: 143 return "Unknown VkObjectType value."; 144 } 145 } 146 """)) 147 148H_TEMPLATE = Template(textwrap.dedent(u"""\ 149 /* Autogenerated file -- do not edit 150 * generated by ${file} 151 * 152 ${copyright} 153 */ 154 155 #ifndef MESA_VK_ENUM_TO_STR_H 156 #define MESA_VK_ENUM_TO_STR_H 157 158 #include <vulkan/vulkan.h> 159 #include <vulkan/vk_android_native_buffer.h> 160 161 #ifdef __cplusplus 162 extern "C" { 163 #endif 164 165 % for enum in enums: 166 % if enum.guard: 167#ifdef ${enum.guard} 168 % endif 169 const char * vk_${enum.name[2:]}_to_str(${enum.name} input); 170 % if enum.guard: 171#endif 172 % endif 173 % endfor 174 175 % for enum in bitmasks: 176 % if enum.guard: 177#ifdef ${enum.guard} 178 % endif 179 const char * vk_${enum.name[2:]}_to_str(${enum.name} input); 180 % if enum.guard: 181#endif 182 % endif 183 % endfor 184 185 size_t vk_structure_type_size(const struct VkBaseInStructure *item); 186 187 const char * vk_ObjectType_to_ObjectName(VkObjectType type); 188 189 #ifdef __cplusplus 190 } /* extern "C" */ 191 #endif 192 193 #endif""")) 194 195 196H_DEFINE_TEMPLATE = Template(textwrap.dedent(u"""\ 197 /* Autogenerated file -- do not edit 198 * generated by ${file} 199 * 200 ${copyright} 201 */ 202 203 #ifndef MESA_VK_ENUM_DEFINES_H 204 #define MESA_VK_ENUM_DEFINES_H 205 206 #include <vulkan/vulkan_core.h> 207 #include <vulkan/vk_android_native_buffer.h> 208 209 #ifdef __cplusplus 210 extern "C" { 211 #endif 212 213 % for ext in extensions: 214 #define _${ext.name}_number (${ext.number}) 215 % endfor 216 217 % for enum in bitmasks: 218 % if enum.bitwidth > 32: 219 <% continue %> 220 % endif 221 % if enum.guard: 222#ifdef ${enum.guard} 223 % endif 224 #define ${enum.all_bits_name()} ${hex(enum.all_bits_value())}u 225 % if enum.guard: 226#endif 227 % endif 228 % endfor 229 230 % for enum in bitmasks: 231 % if enum.bitwidth < 64: 232 <% continue %> 233 % endif 234 /* Redefine bitmask values of ${enum.name} */ 235 % if enum.guard: 236#ifdef ${enum.guard} 237 % endif 238 % for n, v in enum.name_to_value.items(): 239 #define ${n} (${hex(v)}ULL) 240 % endfor 241 % if enum.guard: 242#endif 243 % endif 244 % endfor 245 246 static inline VkFormatFeatureFlags 247 vk_format_features2_to_features(VkFormatFeatureFlags2 features2) 248 { 249 return features2 & VK_ALL_FORMAT_FEATURE_FLAG_BITS; 250 } 251 252 #ifdef __cplusplus 253 } /* extern "C" */ 254 #endif 255 256 #endif""")) 257 258 259class NamedFactory(object): 260 """Factory for creating enums.""" 261 262 def __init__(self, type_): 263 self.registry = {} 264 self.type = type_ 265 266 def __call__(self, name, **kwargs): 267 try: 268 return self.registry[name] 269 except KeyError: 270 n = self.registry[name] = self.type(name, **kwargs) 271 return n 272 273 def get(self, name): 274 return self.registry.get(name) 275 276 277class VkExtension(object): 278 """Simple struct-like class representing extensions""" 279 280 def __init__(self, name, number=None, define=None): 281 self.name = name 282 self.number = number 283 self.define = define 284 285 286def CamelCase_to_SHOUT_CASE(s): 287 return (s[:1] + re.sub(r'(?<![A-Z])([A-Z])', r'_\1', s[1:])).upper() 288 289def compute_max_enum_name(s): 290 if s == "VkSwapchainImageUsageFlagBitsANDROID": 291 return "VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM" 292 max_enum_name = CamelCase_to_SHOUT_CASE(s) 293 last_prefix = max_enum_name.rsplit('_', 1)[-1] 294 # Those special prefixes need to be always at the end 295 if last_prefix in ['AMD', 'EXT', 'INTEL', 'KHR', 'NV', 'LUNARG', 'QCOM', 'MSFT'] : 296 max_enum_name = "_".join(max_enum_name.split('_')[:-1]) 297 max_enum_name = max_enum_name + "_MAX_ENUM_" + last_prefix 298 else: 299 max_enum_name = max_enum_name + "_MAX_ENUM" 300 301 return max_enum_name 302 303class VkEnum(object): 304 """Simple struct-like class representing a single Vulkan Enum.""" 305 306 def __init__(self, name, bitwidth=32, values=None): 307 self.name = name 308 self.max_enum_name = compute_max_enum_name(name) 309 self.bitwidth = bitwidth 310 self.extension = None 311 # Maps numbers to names 312 self.values = values or dict() 313 self.name_to_value = dict() 314 self.guard = None 315 self.name_to_alias_list = {} 316 317 def all_bits_name(self): 318 assert self.name.startswith('Vk') 319 assert re.search(r'FlagBits[A-Z]*$', self.name) 320 321 return 'VK_ALL_' + CamelCase_to_SHOUT_CASE(self.name[2:]) 322 323 def all_bits_value(self): 324 return functools.reduce(lambda a,b: a | b, self.values.keys(), 0) 325 326 def add_value(self, name, value=None, 327 extnum=None, offset=None, alias=None, 328 error=False): 329 if alias is not None: 330 assert value is None and offset is None 331 if alias not in self.name_to_value: 332 # We don't have this alias yet. Just record the alias and 333 # we'll deal with it later. 334 alias_list = self.name_to_alias_list.setdefault(alias, []) 335 alias_list.append(name); 336 return 337 338 # Use the value from the alias 339 value = self.name_to_value[alias] 340 341 assert value is not None or extnum is not None 342 if value is None: 343 value = 1000000000 + (extnum - 1) * 1000 + offset 344 if error: 345 value = -value 346 347 self.name_to_value[name] = value 348 if value not in self.values: 349 self.values[value] = name 350 elif len(self.values[value]) > len(name): 351 self.values[value] = name 352 353 # Now that the value has been fully added, resolve aliases, if any. 354 if name in self.name_to_alias_list: 355 for alias in self.name_to_alias_list[name]: 356 self.add_value(alias, value) 357 del self.name_to_alias_list[name] 358 359 def add_value_from_xml(self, elem, extension=None): 360 self.extension = extension 361 if 'value' in elem.attrib: 362 self.add_value(elem.attrib['name'], 363 value=int(elem.attrib['value'], base=0)) 364 elif 'bitpos' in elem.attrib: 365 self.add_value(elem.attrib['name'], 366 value=(1 << int(elem.attrib['bitpos'], base=0))) 367 elif 'alias' in elem.attrib: 368 self.add_value(elem.attrib['name'], alias=elem.attrib['alias']) 369 else: 370 error = 'dir' in elem.attrib and elem.attrib['dir'] == '-' 371 if 'extnumber' in elem.attrib: 372 extnum = int(elem.attrib['extnumber']) 373 else: 374 extnum = extension.number 375 self.add_value(elem.attrib['name'], 376 extnum=extnum, 377 offset=int(elem.attrib['offset']), 378 error=error) 379 380 def set_guard(self, g): 381 self.guard = g 382 383 384class VkChainStruct(object): 385 """Simple struct-like class representing a single Vulkan struct identified with a VkStructureType""" 386 def __init__(self, name, stype): 387 self.name = name 388 self.stype = stype 389 self.extension = None 390 391 392def struct_get_stype(xml_node): 393 for member in xml_node.findall('./member'): 394 name = member.findall('./name') 395 if len(name) > 0 and name[0].text == "sType": 396 return member.get('values') 397 return None 398 399class VkObjectType(object): 400 """Simple struct-like class representing a single Vulkan object type""" 401 def __init__(self, name): 402 self.name = name 403 self.enum_to_name = dict() 404 405 406def parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, 407 obj_type_factory, filename, beta): 408 """Parse the XML file. Accumulate results into the factories. 409 410 This parser is a memory efficient iterative XML parser that returns a list 411 of VkEnum objects. 412 """ 413 414 xml = et.parse(filename) 415 api = 'vulkan' 416 417 required_types = get_all_required(xml, 'type', api, beta) 418 419 for enum_type in xml.findall('./enums[@type="enum"]'): 420 if not filter_api(enum_type, api): 421 continue 422 423 type_name = enum_type.attrib['name'] 424 if not type_name in required_types: 425 continue 426 427 enum = enum_factory(type_name) 428 for value in enum_type.findall('./enum'): 429 if filter_api(value, api): 430 enum.add_value_from_xml(value) 431 432 # For bitmask we only add the Enum selected for convenience. 433 for enum_type in xml.findall('./enums[@type="bitmask"]'): 434 if not filter_api(enum_type, api): 435 continue 436 437 type_name = enum_type.attrib['name'] 438 if not type_name in required_types: 439 continue 440 441 bitwidth = int(enum_type.attrib.get('bitwidth', 32)) 442 enum = bitmask_factory(type_name, bitwidth=bitwidth) 443 for value in enum_type.findall('./enum'): 444 if filter_api(value, api): 445 enum.add_value_from_xml(value) 446 447 for feature in xml.findall('./feature'): 448 if not api in feature.attrib['api'].split(','): 449 continue 450 451 for value in feature.findall('./require/enum[@extends]'): 452 extends = value.attrib['extends'] 453 enum = enum_factory.get(extends) 454 if enum is not None: 455 enum.add_value_from_xml(value) 456 enum = bitmask_factory.get(extends) 457 if enum is not None: 458 enum.add_value_from_xml(value) 459 460 for struct_type in xml.findall('./types/type[@category="struct"]'): 461 if not filter_api(struct_type, api): 462 continue 463 464 name = struct_type.attrib['name'] 465 if name not in required_types: 466 continue 467 468 stype = struct_get_stype(struct_type) 469 if stype is not None: 470 struct_factory(name, stype=stype) 471 472 platform_define = {} 473 for platform in xml.findall('./platforms/platform'): 474 name = platform.attrib['name'] 475 define = platform.attrib['protect'] 476 platform_define[name] = define 477 478 for ext_elem in xml.findall('./extensions/extension'): 479 ext = Extension.from_xml(ext_elem) 480 if api not in ext.supported: 481 continue 482 483 define = platform_define.get(ext.platform, None) 484 extension = ext_factory(ext.name, number=ext.number, define=define) 485 486 for req_elem in ext_elem.findall('./require'): 487 if not filter_api(req_elem, api): 488 continue 489 490 for value in req_elem.findall('./enum[@extends]'): 491 extends = value.attrib['extends'] 492 enum = enum_factory.get(extends) 493 if enum is not None: 494 enum.add_value_from_xml(value, extension) 495 enum = bitmask_factory.get(extends) 496 if enum is not None: 497 enum.add_value_from_xml(value, extension) 498 499 for t in req_elem.findall('./type'): 500 struct = struct_factory.get(t.attrib['name']) 501 if struct is not None: 502 struct.extension = extension 503 504 if define: 505 for value in ext_elem.findall('./require/type[@name]'): 506 enum = enum_factory.get(value.attrib['name']) 507 if enum is not None: 508 enum.set_guard(define) 509 enum = bitmask_factory.get(value.attrib['name']) 510 if enum is not None: 511 enum.set_guard(define) 512 513 obj_type_enum = enum_factory.get("VkObjectType") 514 obj_types = obj_type_factory("VkObjectType") 515 for object_type in xml.findall('./types/type[@category="handle"]'): 516 for object_name in object_type.findall('./name'): 517 # Convert to int to avoid undefined enums 518 enum = object_type.attrib['objtypeenum'] 519 520 # Annoyingly, object types are hard to filter by API so just 521 # look for whether or not we can find the enum name in the 522 # VkObjectType enum. 523 if enum not in obj_type_enum.name_to_value: 524 continue 525 526 enum_val = obj_type_enum.name_to_value[enum] 527 obj_types.enum_to_name[enum_val] = object_name.text 528 529 530def main(): 531 parser = argparse.ArgumentParser() 532 parser.add_argument('--beta', required=True, help='Enable beta extensions.') 533 parser.add_argument('--xml', required=True, 534 help='Vulkan API XML files', 535 action='append', 536 dest='xml_files') 537 parser.add_argument('--outdir', 538 help='Directory to put the generated files in', 539 required=True) 540 541 args = parser.parse_args() 542 543 enum_factory = NamedFactory(VkEnum) 544 ext_factory = NamedFactory(VkExtension) 545 struct_factory = NamedFactory(VkChainStruct) 546 obj_type_factory = NamedFactory(VkObjectType) 547 bitmask_factory = NamedFactory(VkEnum) 548 549 for filename in args.xml_files: 550 parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, 551 obj_type_factory, filename, args.beta) 552 enums = sorted(enum_factory.registry.values(), key=lambda e: e.name) 553 extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name) 554 structs = sorted(struct_factory.registry.values(), key=lambda e: e.name) 555 bitmasks = sorted(bitmask_factory.registry.values(), key=lambda e: e.name) 556 object_types = sorted(obj_type_factory.registry.values(), key=lambda e: e.name) 557 558 for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')), 559 (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h')), 560 (H_DEFINE_TEMPLATE, os.path.join(args.outdir, 'vk_enum_defines.h'))]: 561 with open(file_, 'w', encoding='utf-8') as f: 562 f.write(template.render( 563 file=os.path.basename(__file__), 564 enums=enums, 565 extensions=extensions, 566 structs=structs, 567 bitmasks=bitmasks, 568 object_types=object_types, 569 copyright=COPYRIGHT)) 570 571 572if __name__ == '__main__': 573 main() 574