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