• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
31
32COPYRIGHT = textwrap.dedent(u"""\
33    * Copyright © 2017 Intel Corporation
34    *
35    * Permission is hereby granted, free of charge, to any person obtaining a copy
36    * of this software and associated documentation files (the "Software"), to deal
37    * in the Software without restriction, including without limitation the rights
38    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39    * copies of the Software, and to permit persons to whom the Software is
40    * furnished to do so, subject to the following conditions:
41    *
42    * The above copyright notice and this permission notice shall be included in
43    * all copies or substantial portions of the Software.
44    *
45    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
51    * SOFTWARE.""")
52
53C_TEMPLATE = Template(textwrap.dedent(u"""\
54    /* Autogenerated file -- do not edit
55     * generated by ${file}
56     *
57     ${copyright}
58     */
59
60    #include <string.h>
61    #include <vulkan/vulkan.h>
62    #include <vulkan/vk_android_native_buffer.h>
63    #include <vulkan/vk_layer.h>
64    #include "util/macros.h"
65    #include "vk_enum_to_str.h"
66
67    % for enum in enums:
68
69      % if enum.guard:
70#ifdef ${enum.guard}
71      % endif
72    const char *
73    vk_${enum.name[2:]}_to_str(${enum.name} input)
74    {
75        switch((int64_t)input) {
76    % for v in sorted(enum.values.keys()):
77        case ${v}:
78            return "${enum.values[v]}";
79    % endfor
80        case ${enum.max_enum_name}: return "${enum.max_enum_name}";
81        default:
82            return "Unknown ${enum.name} 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            return "Unknown VkObjectType 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
249def compute_max_enum_name(s):
250    max_enum_name = CamelCase_to_SHOUT_CASE(s)
251    last_prefix = max_enum_name.rsplit('_', 1)[-1]
252    # Those special prefixes need to be always at the end
253    if last_prefix in ['AMD', 'EXT', 'INTEL', 'KHR', 'NV'] :
254        max_enum_name = "_".join(max_enum_name.split('_')[:-1])
255        max_enum_name = max_enum_name + "_MAX_ENUM_" + last_prefix
256    else:
257        max_enum_name = max_enum_name + "_MAX_ENUM"
258
259    return max_enum_name
260
261class VkEnum(object):
262    """Simple struct-like class representing a single Vulkan Enum."""
263
264    def __init__(self, name, bitwidth=32, values=None):
265        self.name = name
266        self.max_enum_name = compute_max_enum_name(name)
267        self.bitwidth = bitwidth
268        self.extension = None
269        # Maps numbers to names
270        self.values = values or dict()
271        self.name_to_value = dict()
272        self.guard = None
273        self.name_to_alias_list = {}
274
275    def all_bits_name(self):
276        assert self.name.startswith('Vk')
277        assert re.search(r'FlagBits[A-Z]*$', self.name)
278
279        return 'VK_ALL_' + CamelCase_to_SHOUT_CASE(self.name[2:])
280
281    def all_bits_value(self):
282        return functools.reduce(lambda a,b: a | b, self.values.keys(), 0)
283
284    def add_value(self, name, value=None,
285                  extnum=None, offset=None, alias=None,
286                  error=False):
287        if alias is not None:
288            assert value is None and offset is None
289            if alias not in self.name_to_value:
290                # We don't have this alias yet.  Just record the alias and
291                # we'll deal with it later.
292                alias_list = self.name_to_alias_list.setdefault(alias, [])
293                alias_list.append(name);
294                return
295
296            # Use the value from the alias
297            value = self.name_to_value[alias]
298
299        assert value is not None or extnum is not None
300        if value is None:
301            value = 1000000000 + (extnum - 1) * 1000 + offset
302            if error:
303                value = -value
304
305        self.name_to_value[name] = value
306        if value not in self.values:
307            self.values[value] = name
308        elif len(self.values[value]) > len(name):
309            self.values[value] = name
310
311        # Now that the value has been fully added, resolve aliases, if any.
312        if name in self.name_to_alias_list:
313            for alias in self.name_to_alias_list[name]:
314                self.add_value(alias, value)
315            del self.name_to_alias_list[name]
316
317    def add_value_from_xml(self, elem, extension=None):
318        self.extension = extension
319        if 'value' in elem.attrib:
320            self.add_value(elem.attrib['name'],
321                           value=int(elem.attrib['value'], base=0))
322        elif 'bitpos' in elem.attrib:
323            self.add_value(elem.attrib['name'],
324                           value=(1 << int(elem.attrib['bitpos'], base=0)))
325        elif 'alias' in elem.attrib:
326            self.add_value(elem.attrib['name'], alias=elem.attrib['alias'])
327        else:
328            error = 'dir' in elem.attrib and elem.attrib['dir'] == '-'
329            if 'extnumber' in elem.attrib:
330                extnum = int(elem.attrib['extnumber'])
331            else:
332                extnum = extension.number
333            self.add_value(elem.attrib['name'],
334                           extnum=extnum,
335                           offset=int(elem.attrib['offset']),
336                           error=error)
337
338    def set_guard(self, g):
339        self.guard = g
340
341
342class VkChainStruct(object):
343    """Simple struct-like class representing a single Vulkan struct identified with a VkStructureType"""
344    def __init__(self, name, stype):
345        self.name = name
346        self.stype = stype
347        self.extension = None
348
349
350def struct_get_stype(xml_node):
351    for member in xml_node.findall('./member'):
352        name = member.findall('./name')
353        if len(name) > 0 and name[0].text == "sType":
354            return member.get('values')
355    return None
356
357class VkObjectType(object):
358    """Simple struct-like class representing a single Vulkan object type"""
359    def __init__(self, name):
360        self.name = name
361        self.enum_to_name = dict()
362
363
364def parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory,
365              obj_type_factory, filename):
366    """Parse the XML file. Accumulate results into the factories.
367
368    This parser is a memory efficient iterative XML parser that returns a list
369    of VkEnum objects.
370    """
371
372    xml = et.parse(filename)
373
374    for enum_type in xml.findall('./enums[@type="enum"]'):
375        enum = enum_factory(enum_type.attrib['name'])
376        for value in enum_type.findall('./enum'):
377            enum.add_value_from_xml(value)
378
379    # For bitmask we only add the Enum selected for convenience.
380    for enum_type in xml.findall('./enums[@type="bitmask"]'):
381        bitwidth = int(enum_type.attrib.get('bitwidth', 32))
382        enum = bitmask_factory(enum_type.attrib['name'], bitwidth=bitwidth)
383        for value in enum_type.findall('./enum'):
384            enum.add_value_from_xml(value)
385
386    for value in xml.findall('./feature/require/enum[@extends]'):
387        extends = value.attrib['extends']
388        enum = enum_factory.get(extends)
389        if enum is not None:
390            enum.add_value_from_xml(value)
391        enum = bitmask_factory.get(extends)
392        if enum is not None:
393            enum.add_value_from_xml(value)
394
395    for struct_type in xml.findall('./types/type[@category="struct"]'):
396        name = struct_type.attrib['name']
397        stype = struct_get_stype(struct_type)
398        if stype is not None:
399            struct_factory(name, stype=stype)
400
401    platform_define = {}
402    for platform in xml.findall('./platforms/platform'):
403        name = platform.attrib['name']
404        define = platform.attrib['protect']
405        platform_define[name] = define
406
407    for ext_elem in xml.findall('./extensions/extension[@supported="vulkan"]'):
408        define = None
409        if "platform" in ext_elem.attrib:
410            define = platform_define[ext_elem.attrib['platform']]
411        extension = ext_factory(ext_elem.attrib['name'],
412                                number=int(ext_elem.attrib['number']),
413                                define=define)
414
415        for value in ext_elem.findall('./require/enum[@extends]'):
416            extends = value.attrib['extends']
417            enum = enum_factory.get(extends)
418            if enum is not None:
419                enum.add_value_from_xml(value, extension)
420            enum = bitmask_factory.get(extends)
421            if enum is not None:
422                enum.add_value_from_xml(value, extension)
423        for t in ext_elem.findall('./require/type'):
424            struct = struct_factory.get(t.attrib['name'])
425            if struct is not None:
426                struct.extension = extension
427
428        if define:
429            for value in ext_elem.findall('./require/type[@name]'):
430                enum = enum_factory.get(value.attrib['name'])
431                if enum is not None:
432                    enum.set_guard(define)
433
434    obj_types = obj_type_factory("VkObjectType")
435    for object_type in xml.findall('./types/type[@category="handle"]'):
436        for object_name in object_type.findall('./name'):
437            # Convert to int to avoid undefined enums
438            enum = object_type.attrib['objtypeenum']
439            enum_val = enum_factory.get("VkObjectType").name_to_value[enum]
440            obj_types.enum_to_name[enum_val] = object_name.text
441
442
443def main():
444    parser = argparse.ArgumentParser()
445    parser.add_argument('--xml', required=True,
446                        help='Vulkan API XML files',
447                        action='append',
448                        dest='xml_files')
449    parser.add_argument('--outdir',
450                        help='Directory to put the generated files in',
451                        required=True)
452
453    args = parser.parse_args()
454
455    enum_factory = NamedFactory(VkEnum)
456    ext_factory = NamedFactory(VkExtension)
457    struct_factory = NamedFactory(VkChainStruct)
458    obj_type_factory = NamedFactory(VkObjectType)
459    bitmask_factory = NamedFactory(VkEnum)
460
461    for filename in args.xml_files:
462        parse_xml(enum_factory, ext_factory, struct_factory, bitmask_factory,
463                  obj_type_factory, filename)
464    enums = sorted(enum_factory.registry.values(), key=lambda e: e.name)
465    extensions = sorted(ext_factory.registry.values(), key=lambda e: e.name)
466    structs = sorted(struct_factory.registry.values(), key=lambda e: e.name)
467    bitmasks = sorted(bitmask_factory.registry.values(), key=lambda e: e.name)
468    object_types = sorted(obj_type_factory.registry.values(), key=lambda e: e.name)
469
470    for template, file_ in [(C_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.c')),
471                            (H_TEMPLATE, os.path.join(args.outdir, 'vk_enum_to_str.h')),
472                            (H_DEFINE_TEMPLATE, os.path.join(args.outdir, 'vk_enum_defines.h'))]:
473        with open(file_, 'w', encoding='utf-8') as f:
474            f.write(template.render(
475                file=os.path.basename(__file__),
476                enums=enums,
477                extensions=extensions,
478                structs=structs,
479                bitmasks=bitmasks,
480                object_types=object_types,
481                copyright=COPYRIGHT))
482
483
484if __name__ == '__main__':
485    main()
486