1# coding=utf-8
2#
3# Copyright © 2015, 2017 Intel Corporation
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the "Software"),
7# to deal in the Software without restriction, including without limitation
8# the rights to use, copy, modify, merge, publish, distribute, sublicense,
9# and/or sell copies of the Software, and to permit persons to whom the
10# Software is furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice (including the next
13# paragraph) shall be included in all copies or substantial portions of the
14# Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23#
24
25import argparse
26import functools
27import math
28import os
29import xml.etree.cElementTree as et
30
31from collections import OrderedDict, namedtuple
32from mako.template import Template
33
34from tu_extensions import VkVersion, MAX_API_VERSION, EXTENSIONS
35
36# We generate a static hash table for entry point lookup
37# (vkGetProcAddress). We use a linear congruential generator for our hash
38# function and a power-of-two size table. The prime numbers are determined
39# experimentally.
40
41# We currently don't use layers in tu, but keeping the ability for anv
42# anyways, so we can use it for device groups.
43LAYERS = [
44    'tu'
45]
46
47TEMPLATE_H = Template("""\
48/* This file generated from ${filename}, don't edit directly. */
49
50struct tu_dispatch_table {
51   union {
52      void *entrypoints[${len(entrypoints)}];
53      struct {
54      % for e in entrypoints:
55        % if e.guard is not None:
56#ifdef ${e.guard}
57          PFN_${e.name} ${e.name};
58#else
59          void *${e.name};
60# endif
61        % else:
62          PFN_${e.name} ${e.name};
63        % endif
64      % endfor
65      };
66   };
67};
68
69% for e in entrypoints:
70  % if e.alias:
71    <% continue %>
72  % endif
73  % if e.guard is not None:
74#ifdef ${e.guard}
75  % endif
76  % for layer in LAYERS:
77  ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()});
78  % endfor
79  % if e.guard is not None:
80#endif // ${e.guard}
81  % endif
82% endfor
83""", output_encoding='utf-8')
84
85TEMPLATE_C = Template(u"""\
86/*
87 * Copyright © 2015 Intel Corporation
88 *
89 * Permission is hereby granted, free of charge, to any person obtaining a
90 * copy of this software and associated documentation files (the "Software"),
91 * to deal in the Software without restriction, including without limitation
92 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
93 * and/or sell copies of the Software, and to permit persons to whom the
94 * Software is furnished to do so, subject to the following conditions:
95 *
96 * The above copyright notice and this permission notice (including the next
97 * paragraph) shall be included in all copies or substantial portions of the
98 * Software.
99 *
100 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
101 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
102 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
103 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
104 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
105 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
106 * IN THE SOFTWARE.
107 */
108
109/* This file generated from ${filename}, don't edit directly. */
110
111#include "tu_private.h"
112
113struct string_map_entry {
114   uint32_t name;
115   uint32_t hash;
116   uint32_t num;
117};
118
119/* We use a big string constant to avoid lots of relocations from the entry
120 * point table to lots of little strings. The entries in the entry point table
121 * store the index into this big string.
122 */
123
124static const char strings[] =
125% for s in strmap.sorted_strings:
126    "${s.string}\\0"
127% endfor
128;
129
130static const struct string_map_entry string_map_entries[] = {
131% for s in strmap.sorted_strings:
132    { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
133% endfor
134};
135
136/* Hash table stats:
137 * size ${len(strmap.sorted_strings)} entries
138 * collisions entries:
139% for i in range(10):
140 *     ${i}${'+' if i == 9 else ' '}     ${strmap.collisions[i]}
141% endfor
142 */
143
144#define none 0xffff
145static const uint16_t string_map[${strmap.hash_size}] = {
146% for e in strmap.mapping:
147    ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
148% endfor
149};
150
151/* Weak aliases for all potential implementations. These will resolve to
152 * NULL if they're not defined, which lets the resolve_entrypoint() function
153 * either pick the correct entry point.
154 */
155
156% for layer in LAYERS:
157  % for e in entrypoints:
158    % if e.alias:
159      <% continue %>
160    % endif
161    % if e.guard is not None:
162#ifdef ${e.guard}
163    % endif
164    ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}) __attribute__ ((weak));
165    % if e.guard is not None:
166#endif // ${e.guard}
167    % endif
168  % endfor
169
170  const struct tu_dispatch_table ${layer}_layer = {
171  % for e in entrypoints:
172    % if e.guard is not None:
173#ifdef ${e.guard}
174    % endif
175    .${e.name} = ${e.prefixed_name(layer)},
176    % if e.guard is not None:
177#endif // ${e.guard}
178    % endif
179  % endfor
180  };
181% endfor
182
183static void * __attribute__ ((noinline))
184tu_resolve_entrypoint(uint32_t index)
185{
186   return tu_layer.entrypoints[index];
187}
188
189/** Return true if the core version or extension in which the given entrypoint
190 * is defined is enabled.
191 *
192 * If instance is NULL, we only allow the 3 commands explicitly allowed by the vk
193 * spec.
194 *
195 * If device is NULL, all device extensions are considered enabled.
196 */
197static bool
198tu_entrypoint_is_enabled(int index, uint32_t core_version,
199                         const struct tu_instance_extension_table *instance,
200                         const struct tu_device_extension_table *device)
201{
202   switch (index) {
203% for e in entrypoints:
204   case ${e.num}:
205   % if not e.device_command:
206      if (device) return false;
207   % endif
208   % if e.name == 'vkCreateInstance' or e.name == 'vkEnumerateInstanceExtensionProperties' or e.name == 'vkEnumerateInstanceLayerProperties' or e.name == 'vkEnumerateInstanceVersion':
209      return !device;
210   % elif e.core_version:
211      return instance && ${e.core_version.c_vk_version()} <= core_version;
212   % elif e.extensions:
213      % for ext in e.extensions:
214         % if ext.type == 'instance':
215      if (instance && instance->${ext.name[3:]}) return true;
216         % else:
217      if (instance && (!device || device->${ext.name[3:]})) return true;
218         % endif
219      %endfor
220      return false;
221   % else:
222      return instance;
223   % endif
224% endfor
225   default:
226      return false;
227   }
228}
229
230static int
231tu_lookup_entrypoint(const char *name)
232{
233   static const uint32_t prime_factor = ${strmap.prime_factor};
234   static const uint32_t prime_step = ${strmap.prime_step};
235   const struct string_map_entry *e;
236   uint32_t hash, h;
237   uint16_t i;
238   const char *p;
239
240   hash = 0;
241   for (p = name; *p; p++)
242       hash = hash * prime_factor + *p;
243
244   h = hash;
245   while (1) {
246       i = string_map[h & ${strmap.hash_mask}];
247       if (i == none)
248          return -1;
249       e = &string_map_entries[i];
250       if (e->hash == hash && strcmp(name, strings + e->name) == 0)
251           return e->num;
252       h += prime_step;
253   }
254
255   return -1;
256}
257
258void *
259tu_lookup_entrypoint_unchecked(const char *name)
260{
261   int index = tu_lookup_entrypoint(name);
262   if (index < 0)
263      return NULL;
264   return tu_resolve_entrypoint(index);
265}
266
267void *
268tu_lookup_entrypoint_checked(const char *name,
269                              uint32_t core_version,
270                              const struct tu_instance_extension_table *instance,
271                              const struct tu_device_extension_table *device)
272{
273   int index = tu_lookup_entrypoint(name);
274   if (index < 0 || !tu_entrypoint_is_enabled(index, core_version, instance, device))
275      return NULL;
276   return tu_resolve_entrypoint(index);
277}""", output_encoding='utf-8')
278
279U32_MASK = 2**32 - 1
280
281PRIME_FACTOR = 5024183
282PRIME_STEP = 19
283
284def round_to_pow2(x):
285    return 2**int(math.ceil(math.log(x, 2)))
286
287class StringIntMapEntry(object):
288    def __init__(self, string, num):
289        self.string = string
290        self.num = num
291
292        # Calculate the same hash value that we will calculate in C.
293        h = 0
294        for c in string:
295            h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
296        self.hash = h
297
298        self.offset = None
299
300class StringIntMap(object):
301    def __init__(self):
302        self.baked = False
303        self.strings = dict()
304
305    def add_string(self, string, num):
306        assert not self.baked
307        assert string not in self.strings
308        assert num >= 0 and num < 2**31
309        self.strings[string] = StringIntMapEntry(string, num)
310
311    def bake(self):
312        self.sorted_strings = \
313            sorted(self.strings.values(), key=lambda x: x.string)
314        offset = 0
315        for entry in self.sorted_strings:
316            entry.offset = offset
317            offset += len(entry.string) + 1
318
319        # Save off some values that we'll need in C
320        self.hash_size = round_to_pow2(len(self.strings) * 1.25)
321        self.hash_mask = self.hash_size - 1
322        self.prime_factor = PRIME_FACTOR
323        self.prime_step = PRIME_STEP
324
325        self.mapping = [-1] * self.hash_size
326        self.collisions = [0] * 10
327        for idx, s in enumerate(self.sorted_strings):
328            level = 0
329            h = s.hash
330            while self.mapping[h & self.hash_mask] >= 0:
331                h = h + PRIME_STEP
332                level = level + 1
333            self.collisions[min(level, 9)] += 1
334            self.mapping[h & self.hash_mask] = idx
335
336EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
337
338class EntrypointBase(object):
339    def __init__(self, name):
340        self.name = name
341        self.alias = None
342        self.guard = None
343        self.enabled = False
344        self.num = None
345        # Extensions which require this entrypoint
346        self.core_version = None
347        self.extensions = []
348
349class Entrypoint(EntrypointBase):
350    def __init__(self, name, return_type, params, guard = None):
351        super(Entrypoint, self).__init__(name)
352        self.return_type = return_type
353        self.params = params
354        self.guard = guard
355        self.device_command = len(params) > 0 and (params[0].type == 'VkDevice' or params[0].type == 'VkQueue' or params[0].type == 'VkCommandBuffer')
356
357    def prefixed_name(self, prefix):
358        assert self.name.startswith('vk')
359        return prefix + '_' + self.name[2:]
360
361    def decl_params(self):
362        return ', '.join(p.decl for p in self.params)
363
364    def call_params(self):
365        return ', '.join(p.name for p in self.params)
366
367class EntrypointAlias(EntrypointBase):
368    def __init__(self, name, entrypoint):
369        super(EntrypointAlias, self).__init__(name)
370        self.alias = entrypoint
371        self.device_command = entrypoint.device_command
372
373    def prefixed_name(self, prefix):
374        return self.alias.prefixed_name(prefix)
375
376def get_entrypoints(doc, entrypoints_to_defines, start_index):
377    """Extract the entry points from the registry."""
378    entrypoints = OrderedDict()
379
380    for command in doc.findall('./commands/command'):
381       if 'alias' in command.attrib:
382           alias = command.attrib['name']
383           target = command.attrib['alias']
384           entrypoints[alias] = EntrypointAlias(alias, entrypoints[target])
385       else:
386           name = command.find('./proto/name').text
387           ret_type = command.find('./proto/type').text
388           params = [EntrypointParam(
389               type = p.find('./type').text,
390               name = p.find('./name').text,
391               decl = ''.join(p.itertext())
392           ) for p in command.findall('./param')]
393           guard = entrypoints_to_defines.get(name)
394           # They really need to be unique
395           assert name not in entrypoints
396           entrypoints[name] = Entrypoint(name, ret_type, params, guard)
397
398    for feature in doc.findall('./feature'):
399        assert feature.attrib['api'] == 'vulkan'
400        version = VkVersion(feature.attrib['number'])
401        if version > MAX_API_VERSION:
402            continue
403
404        for command in feature.findall('./require/command'):
405            e = entrypoints[command.attrib['name']]
406            e.enabled = True
407            assert e.core_version is None
408            e.core_version = version
409
410    supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
411    for extension in doc.findall('.extensions/extension'):
412        ext_name = extension.attrib['name']
413        if ext_name not in supported_exts:
414            continue
415
416        ext = supported_exts[ext_name]
417        ext.type = extension.attrib['type']
418
419        for command in extension.findall('./require/command'):
420            e = entrypoints[command.attrib['name']]
421            e.enabled = True
422            assert e.core_version is None
423            e.extensions.append(ext)
424
425    # if the base command is not supported by the driver yet, don't alias aliases
426    for e in entrypoints.values():
427        if e.alias and not e.alias.enabled:
428            e_clone = copy.deepcopy(e.alias)
429            e_clone.enabled = True
430            e_clone.name = e.name
431            entrypoints[e.name] = e_clone
432
433    return [e for e in entrypoints.values() if e.enabled]
434
435
436def get_entrypoints_defines(doc):
437    """Maps entry points to extension defines."""
438    entrypoints_to_defines = {}
439
440    for extension in doc.findall('./extensions/extension[@protect]'):
441        define = extension.attrib['protect']
442
443        for entrypoint in extension.findall('./require/command'):
444            fullname = entrypoint.attrib['name']
445            entrypoints_to_defines[fullname] = define
446
447    platform_define = {}
448    for platform in doc.findall('./platforms/platform'):
449        name = platform.attrib['name']
450        define = platform.attrib['protect']
451        platform_define[name] = define
452
453    for extension in doc.findall('./extensions/extension[@platform]'):
454        platform = extension.attrib['platform']
455        define = platform_define[platform]
456
457        for entrypoint in extension.findall('./require/command'):
458            fullname = entrypoint.attrib['name']
459            entrypoints_to_defines[fullname] = define
460
461    return entrypoints_to_defines
462
463
464def gen_code(entrypoints):
465    """Generate the C code."""
466    strmap = StringIntMap()
467    for e in entrypoints:
468        strmap.add_string(e.name, e.num)
469    strmap.bake()
470
471    return TEMPLATE_C.render(entrypoints=entrypoints,
472                             LAYERS=LAYERS,
473                             strmap=strmap,
474                             filename=os.path.basename(__file__))
475
476
477def main():
478    parser = argparse.ArgumentParser()
479    parser.add_argument('--outdir', help='Where to write the files.',
480                        required=True)
481    parser.add_argument('--xml',
482                        help='Vulkan API XML file.',
483                        required=True,
484                        action='append',
485                        dest='xml_files')
486    args = parser.parse_args()
487
488    entrypoints = []
489
490    for filename in args.xml_files:
491        doc = et.parse(filename)
492        entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
493                                       start_index=len(entrypoints))
494
495    for num, e in enumerate(entrypoints):
496        e.num = num
497
498    # For outputting entrypoints.h we generate a tu_EntryPoint() prototype
499    # per entry point.
500    with open(os.path.join(args.outdir, 'tu_entrypoints.h'), 'wb') as f:
501        f.write(TEMPLATE_H.render(entrypoints=entrypoints,
502                                  LAYERS=LAYERS,
503                                  filename=os.path.basename(__file__)))
504    with open(os.path.join(args.outdir, 'tu_entrypoints.c'), 'wb') as f:
505        f.write(gen_code(entrypoints))
506
507
508if __name__ == '__main__':
509    main()
510