Edit

IABSD.fr/xenocara/lib/mesa/src/vulkan/util/gen_enum_to_str.py

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2024-04-02 10:41:59
    Hash : f3c1e4e9
    Message : Merge Mesa 23.3.6

  • lib/mesa/src/vulkan/util/gen_enum_to_str.py
  • # 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 <string.h>
        #include <vulkan/vulkan_core.h>
        #include <vulkan/vk_android_native_buffer.h>
        #include <vulkan/vk_layer.h>
        #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 <vulkan/vulkan.h>
        #include <vulkan/vk_android_native_buffer.h>
    
        #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 <vulkan/vulkan_core.h>
        #include <vulkan/vk_android_native_buffer.h>
    
        #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'(?<![A-Z])([A-Z])', r'_\1', s[1:])).upper()
    
    def compute_max_enum_name(s):
        if s == "VkSwapchainImageUsageFlagBitsANDROID":
            return "VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM"
        max_enum_name = CamelCase_to_SHOUT_CASE(s)
        last_prefix = max_enum_name.rsplit('_', 1)[-1]
        # Those special prefixes need to be always at the end
        if last_prefix in ['AMD', 'EXT', 'INTEL', 'KHR', 'NV', 'LUNARG', 'QCOM', 'MSFT'] :
            max_enum_name = "_".join(max_enum_name.split('_')[:-1])
            max_enum_name = max_enum_name + "_MAX_ENUM_" + last_prefix
        else:
            max_enum_name = max_enum_name + "_MAX_ENUM"
    
        return max_enum_name
    
    class VkEnum(object):
        """Simple struct-like class representing a single Vulkan Enum."""
    
        def __init__(self, name, bitwidth=32, values=None):
            self.name = name
            self.max_enum_name = compute_max_enum_name(name)
            self.bitwidth = bitwidth
            self.extension = None
            # Maps numbers to names
            self.values = values or dict()
            self.name_to_value = dict()
            self.guard = None
            self.name_to_alias_list = {}
    
        def all_bits_name(self):
            assert self.name.startswith('Vk')
            assert re.search(r'FlagBits[A-Z]*$', self.name)
    
            return 'VK_ALL_' + CamelCase_to_SHOUT_CASE(self.name[2:])
    
        def all_bits_value(self):
            return functools.reduce(lambda a,b: a | b, self.values.keys(), 0)
    
        def add_value(self, name, value=None,
                      extnum=None, offset=None, alias=None,
                      error=False):
            if alias is not None:
                assert value is None and offset is None
                if alias not in self.name_to_value:
                    # We don't have this alias yet.  Just record the alias and
                    # we'll deal with it later.
                    alias_list = self.name_to_alias_list.setdefault(alias, [])
                    alias_list.append(name);
                    return
    
                # Use the value from the alias
                value = self.name_to_value[alias]
    
            assert value is not None or extnum is not None
            if value is None:
                value = 1000000000 + (extnum - 1) * 1000 + offset
                if error:
                    value = -value
    
            self.name_to_value[name] = value
            if value not in self.values:
                self.values[value] = name
            elif len(self.values[value]) > 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()