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 default: 82 unreachable("Undefined enum 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 unreachable("Undefined enum 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 249 250class VkEnum(object): 251 """Simple struct-like class representing a single Vulkan Enum.""" 252 253 def __init__(self, name, bitwidth=32, values=None): 254 self.name = name 255 self.bitwidth = bitwidth 256 self.extension = None 257 # Maps numbers to names 258 self.values = values or dict() 259 self.name_to_value = dict() 260 self.guard = None 261 self.name_to_alias_list = {} 262 263 def all_bits_name(self): 264 assert self.name.startswith('Vk') 265 assert re.search(r'FlagBits[A-Z]*$', self.name) 266 267 return 'VK_ALL_' + CamelCase_to_SHOUT_CASE(self.name[2:]) 268 269 def all_bits_value(self): 270 return functools.reduce(lambda a,b: a | b, self.values.keys(), 0) 271 272 def add_value(self, name, value=None, 273 extnum=None, offset=None, alias=None, 274 error=False): 275 if alias is not None: 276 assert value is None and offset is None 277 if alias not in self.name_to_value: 278 # We don't have this alias yet. Just record the alias and 279 # we'll deal with it later. 280 alias_list = self.name_to_alias_list.setdefault(alias, []) 281 alias_list.append(name); 282 return 283 284 # Use the value from the alias 285 value = self.name_to_value[alias] 286 287 assert value is not None or extnum is not None 288 if value is None: 289 value = 1000000000 + (extnum - 1) * 1000 + offset 290 if error: 291 value = -value 292 293 self.name_to_value[name] = value 294 if value not in self.values: 295 self.values[value] = name 296 elif len(self.values[value]) > len(name): 297 self.values[value] = name 298 299 # Now that the value has been fully added, resolve aliases, if any. 300 if name in self.name_to_alias_list: 301 for alias in self.name_to_alias_list[name]: 302 self.add_value(alias, value) 303 del self.name_to_alias_list[name] 304 305 def add_value_from_xml(self, elem, extension=None): 306 self.extension = extension 307 if 'value' in elem.attrib: 308 self.add_value(elem.attrib['name'], 309 value=int(elem.attrib['value'], base=0)) 310 elif 'bitpos' in elem.attrib: 311 self.add_value(elem.attrib['name'], 312 value=(1 << int(elem.attrib['bitpos'], base=0))) 313 elif 'alias' in elem.attrib: 314 self.add_value(elem.attrib['name'], alias=elem.attrib['alias']) 315 else: 316 error = 'dir' in elem.attrib and elem.attrib['dir'] == '-' 317 if 'extnumber' in elem.attrib: 318 extnum = int(elem.attrib['extnumber']) 319 else: 320 extnum = extension.number 321 self.add_value(elem.attrib['name'], 322 extnum=extnum, 323 offset=int(elem.attrib['offset']), 324 error=error) 325 326 def set_guard(self, g): 327 self.guard = g 328 329 330class VkChainStruct(object): 331 """Simple struct-like class representing a single Vulkan struct identified with a VkStructureType""" 332 def __init__(self, name, stype): 333 self.name = name 334 self.stype = stype 335 self.extension = None 336 337 338def struct_get_stype(xml_node): 339 for member in xml_node.findall('./member'): 340 name = member.findall('./name') 341 if len(name) > 0 and name[0].text == "sType": 342 return member.get('values') 343 return None 344 345class VkObjectType(object): 346 """Simple struct-like class representing a single Vulkan object type""" 347 def __init__(self, name): 348 self.name = name 349 self.enum_to_name = dict() 350 351 352def parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, 353 obj_type_factory, filename): 354 """Parse the XML file. Accumulate results into the factories. 355 356 This parser is a memory efficient iterative XML parser that returns a list 357 of VkEnum objects. 358 """ 359 360 xml = et.parse(filename) 361 362 for enum_type in xml.findall('./enums[@type="enum"]'): 363 enum = enum_factory(enum_type.attrib['name']) 364 for value in enum_type.findall('./enum'): 365 enum.add_value_from_xml(value) 366 367 # For bitmask we only add the Enum selected for convenience. 368 for enum_type in xml.findall('./enums[@type="bitmask"]'): 369 bitwidth = int(enum_type.attrib.get('bitwidth', 32)) 370 enum = bitmask_factory(enum_type.attrib['name'], bitwidth=bitwidth) 371 for value in enum_type.findall('./enum'): 372 enum.add_value_from_xml(value) 373 374 for value in xml.findall('./feature/require/enum[@extends]'): 375 extends = value.attrib['extends'] 376 enum = enum_factory.get(extends) 377 if enum is not None: 378 enum.add_value_from_xml(value) 379 enum = bitmask_factory.get(extends) 380 if enum is not None: 381 enum.add_value_from_xml(value) 382 383 for struct_type in xml.findall('./types/type[@category="struct"]'): 384 name = struct_type.attrib['name'] 385 stype = struct_get_stype(struct_type) 386 if stype is not None: 387 struct_factory(name, stype=stype) 388 389 platform_define = {} 390 for platform in xml.findall('./platforms/platform'): 391 name = platform.attrib['name'] 392 define = platform.attrib['protect'] 393 platform_define[name] = define 394 395 for ext_elem in xml.findall('./extensions/extension[@supported="vulkan"]'): 396 define = None 397 if "platform" in ext_elem.attrib: 398 define = platform_define[ext_elem.attrib['platform']] 399 extension = ext_factory(ext_elem.attrib['name'], 400 number=int(ext_elem.attrib['number']), 401 define=define) 402 403 for value in ext_elem.findall('./require/enum[@extends]'): 404 extends = value.attrib['extends'] 405 enum = enum_factory.get(extends) 406 if enum is not None: 407 enum.add_value_from_xml(value, extension) 408 enum = bitmask_factory.get(extends) 409 if enum is not None: 410 enum.add_value_from_xml(value, extension) 411 for t in ext_elem.findall('./require/type'): 412 struct = struct_factory.get(t.attrib['name']) 413 if struct is not None: 414 struct.extension = extension 415 416 if define: 417 for value in ext_elem.findall('./require/type[@name]'): 418 enum = enum_factory.get(value.attrib['name']) 419 if enum is not None: 420 enum.set_guard(define) 421 422 obj_types = obj_type_factory("VkObjectType") 423 for object_type in xml.findall('./types/type[@category="handle"]'): 424 for object_name in object_type.findall('./name'): 425 # Convert to int to avoid undefined enums 426 enum = object_type.attrib['objtypeenum'] 427 enum_val = enum_factory.get("VkObjectType").name_to_value[enum] 428 obj_types.enum_to_name[enum_val] = object_name.text 429 430 431def main(): 432 parser = argparse.ArgumentParser() 433 parser.add_argument('--xml', required=True, 434 help='Vulkan API XML files', 435 action='append', 436 dest='xml_files') 437 parser.add_argument('--outdir', 438 help='Directory to put the generated files in', 439 required=True) 440 441 args = parser.parse_args() 442 443 enum_factory = NamedFactory(VkEnum) 444 ext_factory = NamedFactory(VkExtension) 445 struct_factory = NamedFactory(VkChainStruct) 446 obj_type_factory = NamedFactory(VkObjectType) 447 bitmask_factory = NamedFactory(VkEnum) 448 449 for filename in args.xml_files: 450 parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, 451 obj_type_factory, filename) 452 enums = sorted(enum_factory.registry.values(), key=lambda e: e.name) 453 extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name) 454 structs = sorted(struct_factory.registry.values(), key=lambda e: e.name) 455 bitmasks = sorted(bitmask_factory.registry.values(), key=lambda e: e.name) 456 object_types = sorted(obj_type_factory.registry.values(), key=lambda e: e.name) 457 458 for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')), 459 (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h')), 460 (H_DEFINE_TEMPLATE, os.path.join(args.outdir, 'vk_enum_defines.h'))]: 461 with open(file_, 'w') as f: 462 f.write(template.render( 463 file=os.path.basename(__file__), 464 enums=enums, 465 extensions=extensions, 466 structs=structs, 467 bitmasks=bitmasks, 468 object_types=object_types, 469 copyright=COPYRIGHT)) 470 471 472if __name__ == '__main__': 473 main() 474