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