# Copyright © 2017 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Create enum to string functions for vulkan using vk.xml.""" import argparse import functools import os import re import textwrap import xml.etree.ElementTree as et from mako.template import Template from vk_extensions import Extension, filter_api, get_all_required COPYRIGHT = textwrap.dedent(u"""\ * Copyright © 2017 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.""") C_TEMPLATE = Template(textwrap.dedent(u"""\ /* Autogenerated file -- do not edit * generated by ${file} * ${copyright} */ #include #include #include #include #include "util/macros.h" #include "vk_enum_to_str.h" % for enum in enums: % if enum.guard: #ifdef ${enum.guard} % endif const char * vk_${enum.name[2:]}_to_str(${enum.name} input) { switch((int64_t)input) { % for v in sorted(enum.values.keys()): case ${v}: return "${enum.values[v]}"; % endfor case ${enum.max_enum_name}: return "${enum.max_enum_name}"; default: return "Unknown ${enum.name} value."; } } % if enum.guard: #endif % endif %endfor % for enum in bitmasks: % if enum.guard: #ifdef ${enum.guard} % endif const char * vk_${enum.name[2:]}_to_str(${enum.name} input) { switch((int64_t)input) { % for v in sorted(enum.values.keys()): case ${v}: return "${enum.values[v]}"; % endfor default: return "Unknown ${enum.name} value."; } } % if enum.guard: #endif % endif %endfor size_t vk_structure_type_size(const struct VkBaseInStructure *item) { switch((int)item->sType) { % for struct in structs: % if struct.extension is not None and struct.extension.define is not None: #ifdef ${struct.extension.define} case ${struct.stype}: return sizeof(${struct.name}); #endif % else: case ${struct.stype}: return sizeof(${struct.name}); % endif %endfor case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: return sizeof(VkLayerInstanceCreateInfo); case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: return sizeof(VkLayerDeviceCreateInfo); default: unreachable("Undefined struct type."); } } const char * vk_ObjectType_to_ObjectName(VkObjectType type) { switch((int)type) { % for object_type in sorted(object_types[0].enum_to_name.keys()): case ${object_type}: return "${object_types[0].enum_to_name[object_type]}"; % endfor default: return "Unknown VkObjectType value."; } } """)) H_TEMPLATE = Template(textwrap.dedent(u"""\ /* Autogenerated file -- do not edit * generated by ${file} * ${copyright} */ #ifndef MESA_VK_ENUM_TO_STR_H #define MESA_VK_ENUM_TO_STR_H #include #include #ifdef __cplusplus extern "C" { #endif % for enum in enums: % if enum.guard: #ifdef ${enum.guard} % endif const char * vk_${enum.name[2:]}_to_str(${enum.name} input); % if enum.guard: #endif % endif % endfor % for enum in bitmasks: % if enum.guard: #ifdef ${enum.guard} % endif const char * vk_${enum.name[2:]}_to_str(${enum.name} input); % if enum.guard: #endif % endif % endfor size_t vk_structure_type_size(const struct VkBaseInStructure *item); const char * vk_ObjectType_to_ObjectName(VkObjectType type); #ifdef __cplusplus } /* extern "C" */ #endif #endif""")) H_DEFINE_TEMPLATE = Template(textwrap.dedent(u"""\ /* Autogenerated file -- do not edit * generated by ${file} * ${copyright} */ #ifndef MESA_VK_ENUM_DEFINES_H #define MESA_VK_ENUM_DEFINES_H #include #include #ifdef __cplusplus extern "C" { #endif % for ext in extensions: #define _${ext.name}_number (${ext.number}) % endfor % for enum in bitmasks: % if enum.bitwidth > 32: <% continue %> % endif % if enum.guard: #ifdef ${enum.guard} % endif #define ${enum.all_bits_name()} ${hex(enum.all_bits_value())}u % if enum.guard: #endif % endif % endfor % for enum in bitmasks: % if enum.bitwidth < 64: <% continue %> % endif /* Redefine bitmask values of ${enum.name} */ % if enum.guard: #ifdef ${enum.guard} % endif % for n, v in enum.name_to_value.items(): #define ${n} (${hex(v)}ULL) % endfor % if enum.guard: #endif % endif % endfor static inline VkFormatFeatureFlags vk_format_features2_to_features(VkFormatFeatureFlags2 features2) { return features2 & VK_ALL_FORMAT_FEATURE_FLAG_BITS; } #ifdef __cplusplus } /* extern "C" */ #endif #endif""")) class NamedFactory(object): """Factory for creating enums.""" def __init__(self, type_): self.registry = {} self.type = type_ def __call__(self, name, **kwargs): try: return self.registry[name] except KeyError: n = self.registry[name] = self.type(name, **kwargs) return n def get(self, name): return self.registry.get(name) class VkExtension(object): """Simple struct-like class representing extensions""" def __init__(self, name, number=None, define=None): self.name = name self.number = number self.define = define def CamelCase_to_SHOUT_CASE(s): return (s[:1] + re.sub(r'(? len(name): self.values[value] = name # Now that the value has been fully added, resolve aliases, if any. if name in self.name_to_alias_list: for alias in self.name_to_alias_list[name]: self.add_value(alias, value) del self.name_to_alias_list[name] def add_value_from_xml(self, elem, extension=None): self.extension = extension if 'value' in elem.attrib: self.add_value(elem.attrib['name'], value=int(elem.attrib['value'], base=0)) elif 'bitpos' in elem.attrib: self.add_value(elem.attrib['name'], value=(1 << int(elem.attrib['bitpos'], base=0))) elif 'alias' in elem.attrib: self.add_value(elem.attrib['name'], alias=elem.attrib['alias']) else: error = 'dir' in elem.attrib and elem.attrib['dir'] == '-' if 'extnumber' in elem.attrib: extnum = int(elem.attrib['extnumber']) else: extnum = extension.number self.add_value(elem.attrib['name'], extnum=extnum, offset=int(elem.attrib['offset']), error=error) def set_guard(self, g): self.guard = g class VkChainStruct(object): """Simple struct-like class representing a single Vulkan struct identified with a VkStructureType""" def __init__(self, name, stype): self.name = name self.stype = stype self.extension = None def struct_get_stype(xml_node): for member in xml_node.findall('./member'): name = member.findall('./name') if len(name) > 0 and name[0].text == "sType": return member.get('values') return None class VkObjectType(object): """Simple struct-like class representing a single Vulkan object type""" def __init__(self, name): self.name = name self.enum_to_name = dict() def parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, obj_type_factory, filename, beta): """Parse the XML file. Accumulate results into the factories. This parser is a memory efficient iterative XML parser that returns a list of VkEnum objects. """ xml = et.parse(filename) api = 'vulkan' required_types = get_all_required(xml, 'type', api, beta) for enum_type in xml.findall('./enums[@type="enum"]'): if not filter_api(enum_type, api): continue type_name = enum_type.attrib['name'] if not type_name in required_types: continue enum = enum_factory(type_name) for value in enum_type.findall('./enum'): if filter_api(value, api): enum.add_value_from_xml(value) # For bitmask we only add the Enum selected for convenience. for enum_type in xml.findall('./enums[@type="bitmask"]'): if not filter_api(enum_type, api): continue type_name = enum_type.attrib['name'] if not type_name in required_types: continue bitwidth = int(enum_type.attrib.get('bitwidth', 32)) enum = bitmask_factory(type_name, bitwidth=bitwidth) for value in enum_type.findall('./enum'): if filter_api(value, api): enum.add_value_from_xml(value) for feature in xml.findall('./feature'): if not api in feature.attrib['api'].split(','): continue for value in feature.findall('./require/enum[@extends]'): extends = value.attrib['extends'] enum = enum_factory.get(extends) if enum is not None: enum.add_value_from_xml(value) enum = bitmask_factory.get(extends) if enum is not None: enum.add_value_from_xml(value) for struct_type in xml.findall('./types/type[@category="struct"]'): if not filter_api(struct_type, api): continue name = struct_type.attrib['name'] if name not in required_types: continue stype = struct_get_stype(struct_type) if stype is not None: struct_factory(name, stype=stype) platform_define = {} for platform in xml.findall('./platforms/platform'): name = platform.attrib['name'] define = platform.attrib['protect'] platform_define[name] = define for ext_elem in xml.findall('./extensions/extension'): ext = Extension.from_xml(ext_elem) if api not in ext.supported: continue define = platform_define.get(ext.platform, None) extension = ext_factory(ext.name, number=ext.number, define=define) for req_elem in ext_elem.findall('./require'): if not filter_api(req_elem, api): continue for value in req_elem.findall('./enum[@extends]'): extends = value.attrib['extends'] enum = enum_factory.get(extends) if enum is not None: enum.add_value_from_xml(value, extension) enum = bitmask_factory.get(extends) if enum is not None: enum.add_value_from_xml(value, extension) for t in req_elem.findall('./type'): struct = struct_factory.get(t.attrib['name']) if struct is not None: struct.extension = extension if define: for value in ext_elem.findall('./require/type[@name]'): enum = enum_factory.get(value.attrib['name']) if enum is not None: enum.set_guard(define) enum = bitmask_factory.get(value.attrib['name']) if enum is not None: enum.set_guard(define) obj_type_enum = enum_factory.get("VkObjectType") obj_types = obj_type_factory("VkObjectType") for object_type in xml.findall('./types/type[@category="handle"]'): for object_name in object_type.findall('./name'): # Convert to int to avoid undefined enums enum = object_type.attrib['objtypeenum'] # Annoyingly, object types are hard to filter by API so just # look for whether or not we can find the enum name in the # VkObjectType enum. if enum not in obj_type_enum.name_to_value: continue enum_val = obj_type_enum.name_to_value[enum] obj_types.enum_to_name[enum_val] = object_name.text def main(): parser = argparse.ArgumentParser() parser.add_argument('--beta', required=True, help='Enable beta extensions.') parser.add_argument('--xml', required=True, help='Vulkan API XML files', action='append', dest='xml_files') parser.add_argument('--outdir', help='Directory to put the generated files in', required=True) args = parser.parse_args() enum_factory = NamedFactory(VkEnum) ext_factory = NamedFactory(VkExtension) struct_factory = NamedFactory(VkChainStruct) obj_type_factory = NamedFactory(VkObjectType) bitmask_factory = NamedFactory(VkEnum) for filename in args.xml_files: parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory, obj_type_factory, filename, args.beta) enums = sorted(enum_factory.registry.values(), key=lambda e: e.name) extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name) structs = sorted(struct_factory.registry.values(), key=lambda e: e.name) bitmasks = sorted(bitmask_factory.registry.values(), key=lambda e: e.name) object_types = sorted(obj_type_factory.registry.values(), key=lambda e: e.name) for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')), (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h')), (H_DEFINE_TEMPLATE, os.path.join(args.outdir, 'vk_enum_defines.h'))]: with open(file_, 'w', encoding='utf-8') as f: f.write(template.render( file=os.path.basename(__file__), enums=enums, extensions=extensions, structs=structs, bitmasks=bitmasks, object_types=object_types, copyright=COPYRIGHT)) if __name__ == '__main__': main()