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 24from __future__ import print_function 25import argparse 26import os 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 #pragma GCC diagnostic push 76 #pragma GCC diagnostic ignored "-Wswitch" 77 switch(input) { 78 % for v in sorted(enum.values.keys()): 79 case ${v}: 80 return "${enum.values[v]}"; 81 % endfor 82 } 83 #pragma GCC diagnostic pop 84 unreachable("Undefined enum value."); 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 #pragma GCC diagnostic push 95 #pragma GCC diagnostic ignored "-Wswitch" 96 switch(item->sType) { 97 % for struct in structs: 98 % if struct.extension is not None and struct.extension.define is not None: 99 #ifdef ${struct.extension.define} 100 case ${struct.stype}: return sizeof(${struct.name}); 101 #endif 102 % else: 103 case ${struct.stype}: return sizeof(${struct.name}); 104 % endif 105 %endfor 106 case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: return sizeof(VkLayerInstanceCreateInfo); 107 case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: return sizeof(VkLayerDeviceCreateInfo); 108 } 109 #pragma GCC diagnostic pop 110 unreachable("Undefined struct type."); 111 } 112 113 void vk_load_instance_commands(VkInstance instance, 114 PFN_vkGetInstanceProcAddr gpa, 115 struct vk_instance_dispatch_table *table) 116 { 117 memset(table, 0, sizeof(*table)); 118 table->GetInstanceProcAddr = gpa; 119 % for cmd in commands: 120 % if not cmd.device_entrypoint and cmd.name != 'vkGetInstanceProcAddr': 121 % if cmd.extension is not None and cmd.extension.define is not None: 122 #ifdef ${cmd.extension.define} 123 table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(instance, "${cmd.name}"); 124 #endif 125 % else: 126 table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(instance, "${cmd.name}"); 127 % endif 128 % endif 129 %endfor 130 } 131 132 void vk_load_device_commands(VkDevice device, 133 PFN_vkGetDeviceProcAddr gpa, 134 struct vk_device_dispatch_table *table) 135 { 136 memset(table, 0, sizeof(*table)); 137 table->GetDeviceProcAddr = gpa; 138 % for cmd in commands: 139 % if cmd.device_entrypoint and cmd.name != 'vkGetDeviceProcAddr': 140 % if cmd.extension is not None and cmd.extension.define is not None: 141 #ifdef ${cmd.extension.define} 142 table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(device, "${cmd.name}"); 143 #endif 144 % else: 145 table->${cmd.name[2:]} = (PFN_${cmd.name}) gpa(device, "${cmd.name}"); 146 % endif 147 % endif 148 %endfor 149 } 150 """), 151 output_encoding='utf-8') 152 153H_TEMPLATE = Template(textwrap.dedent(u"""\ 154 /* Autogenerated file -- do not edit 155 * generated by ${file} 156 * 157 ${copyright} 158 */ 159 160 #ifndef MESA_VK_ENUM_TO_STR_H 161 #define MESA_VK_ENUM_TO_STR_H 162 163 #include <vulkan/vulkan.h> 164 #include <vulkan/vk_android_native_buffer.h> 165 166 #ifdef __cplusplus 167 extern "C" { 168 #endif 169 170 % for ext in extensions: 171 #define _${ext.name}_number (${ext.number}) 172 % endfor 173 174 % for enum in enums: 175 % if enum.guard: 176#ifdef ${enum.guard} 177 % endif 178 const char * vk_${enum.name[2:]}_to_str(${enum.name} input); 179 % if enum.guard: 180#endif 181 % endif 182 % endfor 183 184 size_t vk_structure_type_size(const struct VkBaseInStructure *item); 185 186 struct vk_instance_dispatch_table { 187 PFN_vkGetInstanceProcAddr GetInstanceProcAddr; 188 % for cmd in commands: 189 % if not cmd.device_entrypoint and cmd.name != 'vkGetInstanceProcAddr': 190 % if cmd.extension is not None and cmd.extension.define is not None: 191 #ifdef ${cmd.extension.define} 192 PFN_${cmd.name} ${cmd.name[2:]}; 193 #endif 194 % else: 195 PFN_${cmd.name} ${cmd.name[2:]}; 196 % endif 197 % endif 198 %endfor 199 }; 200 201 struct vk_device_dispatch_table { 202 PFN_vkGetDeviceProcAddr GetDeviceProcAddr; 203 % for cmd in commands: 204 % if cmd.device_entrypoint and cmd.name != 'vkGetDeviceProcAddr': 205 % if cmd.extension is not None and cmd.extension.define is not None: 206 #ifdef ${cmd.extension.define} 207 PFN_${cmd.name} ${cmd.name[2:]}; 208 #endif 209 % else: 210 PFN_${cmd.name} ${cmd.name[2:]}; 211 % endif 212 % endif 213 %endfor 214 }; 215 216 void vk_load_instance_commands(VkInstance instance, PFN_vkGetInstanceProcAddr gpa, struct vk_instance_dispatch_table *table); 217 void vk_load_device_commands(VkDevice device, PFN_vkGetDeviceProcAddr gpa, struct vk_device_dispatch_table *table); 218 219 #ifdef __cplusplus 220 } /* extern "C" */ 221 #endif 222 223 #endif"""), 224 output_encoding='utf-8') 225 226 227class NamedFactory(object): 228 """Factory for creating enums.""" 229 230 def __init__(self, type_): 231 self.registry = {} 232 self.type = type_ 233 234 def __call__(self, name, **kwargs): 235 try: 236 return self.registry[name] 237 except KeyError: 238 n = self.registry[name] = self.type(name, **kwargs) 239 return n 240 241 def get(self, name): 242 return self.registry.get(name) 243 244 245class VkExtension(object): 246 """Simple struct-like class representing extensions""" 247 248 def __init__(self, name, number=None, define=None): 249 self.name = name 250 self.number = number 251 self.define = define 252 253 254class VkEnum(object): 255 """Simple struct-like class representing a single Vulkan Enum.""" 256 257 def __init__(self, name, values=None): 258 self.name = name 259 self.extension = None 260 # Maps numbers to names 261 self.values = values or dict() 262 self.name_to_value = dict() 263 self.guard = None 264 self.name_to_alias_list = {} 265 266 def add_value(self, name, value=None, 267 extnum=None, offset=None, alias=None, 268 error=False): 269 if alias is not None: 270 assert value is None and offset is None 271 if alias not in self.name_to_value: 272 # We don't have this alias yet. Just record the alias and 273 # we'll deal with it later. 274 alias_list = self.name_to_alias_list.get(alias, []) 275 alias_list.append(name); 276 return 277 278 # Use the value from the alias 279 value = self.name_to_value[alias] 280 281 assert value is not None or extnum is not None 282 if value is None: 283 value = 1000000000 + (extnum - 1) * 1000 + offset 284 if error: 285 value = -value 286 287 self.name_to_value[name] = value 288 if value not in self.values: 289 self.values[value] = name 290 elif len(self.values[value]) > len(name): 291 self.values[value] = name 292 293 # Now that the value has been fully added, resolve aliases, if any. 294 if name in self.name_to_alias_list: 295 for alias in self.name_to_alias_list[name]: 296 add_value(alias, value) 297 del self.name_to_alias_list[name] 298 299 def add_value_from_xml(self, elem, extension=None): 300 self.extension = extension 301 if 'value' in elem.attrib: 302 self.add_value(elem.attrib['name'], 303 value=int(elem.attrib['value'], base=0)) 304 elif 'alias' in elem.attrib: 305 self.add_value(elem.attrib['name'], alias=elem.attrib['alias']) 306 else: 307 error = 'dir' in elem.attrib and elem.attrib['dir'] == '-' 308 if 'extnumber' in elem.attrib: 309 extnum = int(elem.attrib['extnumber']) 310 else: 311 extnum = extension.number 312 self.add_value(elem.attrib['name'], 313 extnum=extnum, 314 offset=int(elem.attrib['offset']), 315 error=error) 316 317 def set_guard(self, g): 318 self.guard = g 319 320 321class VkCommand(object): 322 """Simple struct-like class representing a single Vulkan command""" 323 324 def __init__(self, name, device_entrypoint=False): 325 self.name = name 326 self.device_entrypoint = device_entrypoint 327 self.extension = None 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 345 346def parse_xml(cmd_factory, enum_factory, ext_factory, struct_factory, filename): 347 """Parse the XML file. Accumulate results into the factories. 348 349 This parser is a memory efficient iterative XML parser that returns a list 350 of VkEnum objects. 351 """ 352 353 xml = et.parse(filename) 354 355 for enum_type in xml.findall('./enums[@type="enum"]'): 356 enum = enum_factory(enum_type.attrib['name']) 357 for value in enum_type.findall('./enum'): 358 enum.add_value_from_xml(value) 359 360 for value in xml.findall('./feature/require/enum[@extends]'): 361 enum = enum_factory.get(value.attrib['extends']) 362 if enum is not None: 363 enum.add_value_from_xml(value) 364 365 for command in xml.findall('./commands/command'): 366 name = command.find('./proto/name') 367 first_arg = command.find('./param/type') 368 # Some commands are alias KHR -> nonKHR, ignore those 369 if name is not None: 370 cmd_factory(name.text, 371 device_entrypoint=(first_arg.text in ('VkDevice', 'VkCommandBuffer', 'VkQueue'))) 372 373 for struct_type in xml.findall('./types/type[@category="struct"]'): 374 name = struct_type.attrib['name'] 375 stype = struct_get_stype(struct_type) 376 if stype is not None: 377 struct_factory(name, stype=stype) 378 379 platform_define = {} 380 for platform in xml.findall('./platforms/platform'): 381 name = platform.attrib['name'] 382 define = platform.attrib['protect'] 383 platform_define[name] = define 384 385 for ext_elem in xml.findall('./extensions/extension[@supported="vulkan"]'): 386 define = None 387 if "platform" in ext_elem.attrib: 388 define = platform_define[ext_elem.attrib['platform']] 389 extension = ext_factory(ext_elem.attrib['name'], 390 number=int(ext_elem.attrib['number']), 391 define=define) 392 393 for value in ext_elem.findall('./require/enum[@extends]'): 394 enum = enum_factory.get(value.attrib['extends']) 395 if enum is not None: 396 enum.add_value_from_xml(value, extension) 397 for t in ext_elem.findall('./require/type'): 398 struct = struct_factory.get(t.attrib['name']) 399 if struct is not None: 400 struct.extension = extension 401 402 if define: 403 for value in ext_elem.findall('./require/type[@name]'): 404 enum = enum_factory.get(value.attrib['name']) 405 if enum is not None: 406 enum.set_guard(define) 407 408 for t in ext_elem.findall('./require/command'): 409 command = cmd_factory.get(t.attrib['name']) 410 if command is not None: 411 command.extension = extension 412 413 414def main(): 415 parser = argparse.ArgumentParser() 416 parser.add_argument('--xml', required=True, 417 help='Vulkan API XML files', 418 action='append', 419 dest='xml_files') 420 parser.add_argument('--outdir', 421 help='Directory to put the generated files in', 422 required=True) 423 424 args = parser.parse_args() 425 426 command_factory = NamedFactory(VkCommand) 427 enum_factory = NamedFactory(VkEnum) 428 ext_factory = NamedFactory(VkExtension) 429 struct_factory = NamedFactory(VkChainStruct) 430 for filename in args.xml_files: 431 parse_xml(command_factory, enum_factory, ext_factory, struct_factory, filename) 432 commands = sorted(command_factory.registry.values(), key=lambda e: e.name) 433 enums = sorted(enum_factory.registry.values(), key=lambda e: e.name) 434 extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name) 435 structs = sorted(struct_factory.registry.values(), key=lambda e: e.name) 436 437 for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')), 438 (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h'))]: 439 with open(file_, 'wb') as f: 440 f.write(template.render( 441 file=os.path.basename(__file__), 442 commands=commands, 443 enums=enums, 444 extensions=extensions, 445 structs=structs, 446 copyright=COPYRIGHT)) 447 448 449if __name__ == '__main__': 450 main() 451