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        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    size_t vk_structure_type_size(const struct VkBaseInStructure *item)
93    {
94        switch((int)item->sType) {
95    % for struct in structs:
96        % if struct.extension is not None and struct.extension.define is not None:
97    #ifdef ${struct.extension.define}
98        case ${struct.stype}: return sizeof(${struct.name});
99    #endif
100        % else:
101        case ${struct.stype}: return sizeof(${struct.name});
102        % endif
103    %endfor
104        case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO: return sizeof(VkLayerInstanceCreateInfo);
105        case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO: return sizeof(VkLayerDeviceCreateInfo);
106        default:
107            unreachable("Undefined struct type.");
108        }
109    }
110
111    const char *
112    vk_ObjectType_to_ObjectName(VkObjectType type)
113    {
114        switch((int)type) {
115    % for object_type in sorted(object_types[0].enum_to_name.keys()):
116        case ${object_type}:
117            return "${object_types[0].enum_to_name[object_type]}";
118    % endfor
119        default:
120            return "Unknown VkObjectType value.";
121        }
122    }
123    """))
124
125H_TEMPLATE = Template(textwrap.dedent(u"""\
126    /* Autogenerated file -- do not edit
127     * generated by ${file}
128     *
129     ${copyright}
130     */
131
132    #ifndef MESA_VK_ENUM_TO_STR_H
133    #define MESA_VK_ENUM_TO_STR_H
134
135    #include <vulkan/vulkan.h>
136    #include <vulkan/vk_android_native_buffer.h>
137
138    #ifdef __cplusplus
139    extern "C" {
140    #endif
141
142    % for enum in enums:
143      % if enum.guard:
144#ifdef ${enum.guard}
145      % endif
146    const char * vk_${enum.name[2:]}_to_str(${enum.name} input);
147      % if enum.guard:
148#endif
149      % endif
150    % endfor
151
152    size_t vk_structure_type_size(const struct VkBaseInStructure *item);
153
154    const char * vk_ObjectType_to_ObjectName(VkObjectType type);
155
156    #ifdef __cplusplus
157    } /* extern "C" */
158    #endif
159
160    #endif"""))
161
162
163H_DEFINE_TEMPLATE = Template(textwrap.dedent(u"""\
164    /* Autogenerated file -- do not edit
165     * generated by ${file}
166     *
167     ${copyright}
168     */
169
170    #ifndef MESA_VK_ENUM_DEFINES_H
171    #define MESA_VK_ENUM_DEFINES_H
172
173    #include <vulkan/vulkan.h>
174    #include <vulkan/vk_android_native_buffer.h>
175
176    #ifdef __cplusplus
177    extern "C" {
178    #endif
179
180    % for ext in extensions:
181    #define _${ext.name}_number (${ext.number})
182    % endfor
183
184    % for enum in bitmasks:
185      % if enum.bitwidth > 32:
186        <% continue %>
187      % endif
188      % if enum.guard:
189#ifdef ${enum.guard}
190      % endif
191    #define ${enum.all_bits_name()} ${hex(enum.all_bits_value())}u
192      % if enum.guard:
193#endif
194      % endif
195    % endfor
196
197    % for enum in bitmasks:
198      % if enum.bitwidth < 64:
199        <% continue %>
200      % endif
201    /* Redefine bitmask values of ${enum.name} */
202      % if enum.guard:
203#ifdef ${enum.guard}
204      % endif
205      % for n, v in enum.name_to_value.items():
206    #define ${n} (${hex(v)}ULL)
207      % endfor
208      % if enum.guard:
209#endif
210      % endif
211    % endfor
212
213    #ifdef __cplusplus
214    } /* extern "C" */
215    #endif
216
217    #endif"""))
218
219
220class NamedFactory(object):
221    """Factory for creating enums."""
222
223    def __init__(self, type_):
224        self.registry = {}
225        self.type = type_
226
227    def __call__(self, name, **kwargs):
228        try:
229            return self.registry[name]
230        except KeyError:
231            n = self.registry[name] = self.type(name, **kwargs)
232        return n
233
234    def get(self, name):
235        return self.registry.get(name)
236
237
238class VkExtension(object):
239    """Simple struct-like class representing extensions"""
240
241    def __init__(self, name, number=None, define=None):
242        self.name = name
243        self.number = number
244        self.define = define
245
246
247def CamelCase_to_SHOUT_CASE(s):
248   return (s[:1] + re.sub(r'(?<![A-Z])([A-Z])', r'_\1', s[1:])).upper()
249
250def compute_max_enum_name(s):
251    max_enum_name = CamelCase_to_SHOUT_CASE(s)
252    last_prefix = max_enum_name.rsplit('_', 1)[-1]
253    # Those special prefixes need to be always at the end
254    if last_prefix in ['AMD', 'EXT', 'INTEL', 'KHR', 'NV'] :
255        max_enum_name = "_".join(max_enum_name.split('_')[:-1])
256        max_enum_name = max_enum_name + "_MAX_ENUM_" + last_prefix
257    else:
258        max_enum_name = max_enum_name + "_MAX_ENUM"
259
260    return max_enum_name
261
262class VkEnum(object):
263    """Simple struct-like class representing a single Vulkan Enum."""
264
265    def __init__(self, name, bitwidth=32, values=None):
266        self.name = name
267        self.max_enum_name = compute_max_enum_name(name)
268        self.bitwidth = bitwidth
269        self.extension = None
270        # Maps numbers to names
271        self.values = values or dict()
272        self.name_to_value = dict()
273        self.guard = None
274        self.name_to_alias_list = {}
275
276    def all_bits_name(self):
277        assert self.name.startswith('Vk')
278        assert re.search(r'FlagBits[A-Z]*$', self.name)
279
280        return 'VK_ALL_' + CamelCase_to_SHOUT_CASE(self.name[2:])
281
282    def all_bits_value(self):
283        return functools.reduce(lambda a,b: a | b, self.values.keys(), 0)
284
285    def add_value(self, name, value=None,
286                  extnum=None, offset=None, alias=None,
287                  error=False):
288        if alias is not None:
289            assert value is None and offset is None
290            if alias not in self.name_to_value:
291                # We don't have this alias yet.  Just record the alias and
292                # we'll deal with it later.
293                alias_list = self.name_to_alias_list.setdefault(alias, [])
294                alias_list.append(name);
295                return
296
297            # Use the value from the alias
298            value = self.name_to_value[alias]
299
300        assert value is not None or extnum is not None
301        if value is None:
302            value = 1000000000 + (extnum - 1) * 1000 + offset
303            if error:
304                value = -value
305
306        self.name_to_value[name] = value
307        if value not in self.values:
308            self.values[value] = name
309        elif len(self.values[value]) > len(name):
310            self.values[value] = name
311
312        # Now that the value has been fully added, resolve aliases, if any.
313        if name in self.name_to_alias_list:
314            for alias in self.name_to_alias_list[name]:
315                self.add_value(alias, value)
316            del self.name_to_alias_list[name]
317
318    def add_value_from_xml(self, elem, extension=None):
319        self.extension = extension
320        if 'value' in elem.attrib:
321            self.add_value(elem.attrib['name'],
322                           value=int(elem.attrib['value'], base=0))
323        elif 'bitpos' in elem.attrib:
324            self.add_value(elem.attrib['name'],
325                           value=(1 << int(elem.attrib['bitpos'], base=0)))
326        elif 'alias' in elem.attrib:
327            self.add_value(elem.attrib['name'], alias=elem.attrib['alias'])
328        else:
329            error = 'dir' in elem.attrib and elem.attrib['dir'] == '-'
330            if 'extnumber' in elem.attrib:
331                extnum = int(elem.attrib['extnumber'])
332            else:
333                extnum = extension.number
334            self.add_value(elem.attrib['name'],
335                           extnum=extnum,
336                           offset=int(elem.attrib['offset']),
337                           error=error)
338
339    def set_guard(self, g):
340        self.guard = g
341
342
343class VkChainStruct(object):
344    """Simple struct-like class representing a single Vulkan struct identified with a VkStructureType"""
345    def __init__(self, name, stype):
346        self.name = name
347        self.stype = stype
348        self.extension = None
349
350
351def struct_get_stype(xml_node):
352    for member in xml_node.findall('./member'):
353        name = member.findall('./name')
354        if len(name) > 0 and name[0].text == "sType":
355            return member.get('values')
356    return None
357
358class VkObjectType(object):
359    """Simple struct-like class representing a single Vulkan object type"""
360    def __init__(self, name):
361        self.name = name
362        self.enum_to_name = dict()
363
364
365def parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory,
366              obj_type_factory, filename):
367    """Parse the XML file. Accumulate results into the factories.
368
369    This parser is a memory efficient iterative XML parser that returns a list
370    of VkEnum objects.
371    """
372
373    xml = et.parse(filename)
374
375    for enum_type in xml.findall('./enums[@type="enum"]'):
376        enum = enum_factory(enum_type.attrib['name'])
377        for value in enum_type.findall('./enum'):
378            enum.add_value_from_xml(value)
379
380    # For bitmask we only add the Enum selected for convenience.
381    for enum_type in xml.findall('./enums[@type="bitmask"]'):
382        bitwidth = int(enum_type.attrib.get('bitwidth', 32))
383        enum = bitmask_factory(enum_type.attrib['name'], bitwidth=bitwidth)
384        for value in enum_type.findall('./enum'):
385            enum.add_value_from_xml(value)
386
387    for value in xml.findall('./feature/require/enum[@extends]'):
388        extends = value.attrib['extends']
389        enum = enum_factory.get(extends)
390        if enum is not None:
391            enum.add_value_from_xml(value)
392        enum = bitmask_factory.get(extends)
393        if enum is not None:
394            enum.add_value_from_xml(value)
395
396    for struct_type in xml.findall('./types/type[@category="struct"]'):
397        name = struct_type.attrib['name']
398        stype = struct_get_stype(struct_type)
399        if stype is not None:
400            struct_factory(name, stype=stype)
401
402    platform_define = {}
403    for platform in xml.findall('./platforms/platform'):
404        name = platform.attrib['name']
405        define = platform.attrib['protect']
406        platform_define[name] = define
407
408    for ext_elem in xml.findall('./extensions/extension[@supported="vulkan"]'):
409        define = None
410        if "platform" in ext_elem.attrib:
411            define = platform_define[ext_elem.attrib['platform']]
412        extension = ext_factory(ext_elem.attrib['name'],
413                                number=int(ext_elem.attrib['number']),
414                                define=define)
415
416        for value in ext_elem.findall('./require/enum[@extends]'):
417            extends = value.attrib['extends']
418            enum = enum_factory.get(extends)
419            if enum is not None:
420                enum.add_value_from_xml(value, extension)
421            enum = bitmask_factory.get(extends)
422            if enum is not None:
423                enum.add_value_from_xml(value, extension)
424        for t in ext_elem.findall('./require/type'):
425            struct = struct_factory.get(t.attrib['name'])
426            if struct is not None:
427                struct.extension = extension
428
429        if define:
430            for value in ext_elem.findall('./require/type[@name]'):
431                enum = enum_factory.get(value.attrib['name'])
432                if enum is not None:
433                    enum.set_guard(define)
434
435    obj_types = obj_type_factory("VkObjectType")
436    for object_type in xml.findall('./types/type[@category="handle"]'):
437        for object_name in object_type.findall('./name'):
438            # Convert to int to avoid undefined enums
439            enum = object_type.attrib['objtypeenum']
440            enum_val = enum_factory.get("VkObjectType").name_to_value[enum]
441            obj_types.enum_to_name[enum_val] = object_name.text
442
443
444def main():
445    parser = argparse.ArgumentParser()
446    parser.add_argument('--xml', required=True,
447                        help='Vulkan API XML files',
448                        action='append',
449                        dest='xml_files')
450    parser.add_argument('--outdir',
451                        help='Directory to put the generated files in',
452                        required=True)
453
454    args = parser.parse_args()
455
456    enum_factory = NamedFactory(VkEnum)
457    ext_factory = NamedFactory(VkExtension)
458    struct_factory = NamedFactory(VkChainStruct)
459    obj_type_factory = NamedFactory(VkObjectType)
460    bitmask_factory = NamedFactory(VkEnum)
461
462    for filename in args.xml_files:
463        parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory,
464                  obj_type_factory, filename)
465    enums = sorted(enum_factory.registry.values(), key=lambda e: e.name)
466    extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name)
467    structs = sorted(struct_factory.registry.values(), key=lambda e: e.name)
468    bitmasks = sorted(bitmask_factory.registry.values(), key=lambda e: e.name)
469    object_types = sorted(obj_type_factory.registry.values(), key=lambda e: e.name)
470
471    for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')),
472                            (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h')),
473                            (H_DEFINE_TEMPLATE, os.path.join(args.outdir, 'vk_enum_defines.h'))]:
474        with open(file_, 'w') as f:
475            f.write(template.render(
476                file=os.path.basename(__file__),
477                enums=enums,
478                extensions=extensions,
479                structs=structs,
480                bitmasks=bitmasks,
481                object_types=object_types,
482                copyright=COPYRIGHT))
483
484
485if __name__ == '__main__':
486    main()
487