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