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