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