• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1COPYRIGHT = """\
2/*
3 * Copyright 2020 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
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25"""
26
27import argparse
28import math
29import os
30
31from mako.template import Template
32
33# Mesa-local imports must be declared in meson variable
34# '{file_without_suffix}_depend_files'.
35from vk_entrypoints import get_entrypoints_from_xml
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
42TEMPLATE_H = Template(COPYRIGHT + """\
43/* This file generated from ${filename}, don't edit directly. */
44
45#ifndef VK_DISPATCH_TABLE_H
46#define VK_DISPATCH_TABLE_H
47
48#include "vulkan/vulkan.h"
49#include "vulkan/vk_android_native_buffer.h"
50
51#include "vk_extensions.h"
52
53/* Windows api conflict */
54#ifdef _WIN32
55#include <windows.h>
56#ifdef CreateSemaphore
57#undef CreateSemaphore
58#endif
59#ifdef CreateEvent
60#undef CreateEvent
61#endif
62#endif
63
64#ifdef __cplusplus
65extern "C" {
66#endif
67
68#ifdef _MSC_VER
69VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void);
70#endif
71
72<%def name="dispatch_table(entrypoints)">
73% for e in entrypoints:
74  % if e.alias:
75    <% continue %>
76  % endif
77  % if e.guard is not None:
78#ifdef ${e.guard}
79  % endif
80  % if e.aliases:
81    union {
82        PFN_vk${e.name} ${e.name};
83      % for a in e.aliases:
84        PFN_vk${a.name} ${a.name};
85      % endfor
86    };
87  % else:
88    PFN_vk${e.name} ${e.name};
89  % endif
90  % if e.guard is not None:
91#else
92    % if e.aliases:
93    union {
94        PFN_vkVoidFunction ${e.name};
95      % for a in e.aliases:
96        PFN_vkVoidFunction ${a.name};
97      % endfor
98    };
99    % else:
100    PFN_vkVoidFunction ${e.name};
101    % endif
102#endif
103  % endif
104% endfor
105</%def>
106
107<%def name="entrypoint_table(type, entrypoints)">
108struct vk_${type}_entrypoint_table {
109% for e in entrypoints:
110  % if e.guard is not None:
111#ifdef ${e.guard}
112  % endif
113    PFN_vk${e.name} ${e.name};
114  % if e.guard is not None:
115#else
116    PFN_vkVoidFunction ${e.name};
117# endif
118  % endif
119% endfor
120};
121</%def>
122
123struct vk_instance_dispatch_table {
124  ${dispatch_table(instance_entrypoints)}
125};
126
127struct vk_physical_device_dispatch_table {
128  ${dispatch_table(physical_device_entrypoints)}
129};
130
131struct vk_device_dispatch_table {
132  ${dispatch_table(device_entrypoints)}
133};
134
135struct vk_dispatch_table {
136    union {
137        struct {
138            struct vk_instance_dispatch_table instance;
139            struct vk_physical_device_dispatch_table physical_device;
140            struct vk_device_dispatch_table device;
141        };
142
143        struct {
144            ${dispatch_table(instance_entrypoints)}
145            ${dispatch_table(physical_device_entrypoints)}
146            ${dispatch_table(device_entrypoints)}
147        };
148    };
149};
150
151${entrypoint_table('instance', instance_entrypoints)}
152${entrypoint_table('physical_device', physical_device_entrypoints)}
153${entrypoint_table('device', device_entrypoints)}
154
155void
156vk_instance_dispatch_table_load(struct vk_instance_dispatch_table *table,
157                                PFN_vkGetInstanceProcAddr gpa,
158                                VkInstance instance);
159void
160vk_physical_device_dispatch_table_load(struct vk_physical_device_dispatch_table *table,
161                                       PFN_vkGetInstanceProcAddr gpa,
162                                       VkInstance instance);
163void
164vk_device_dispatch_table_load(struct vk_device_dispatch_table *table,
165                              PFN_vkGetDeviceProcAddr gpa,
166                              VkDevice device);
167
168void vk_instance_dispatch_table_from_entrypoints(
169    struct vk_instance_dispatch_table *dispatch_table,
170    const struct vk_instance_entrypoint_table *entrypoint_table,
171    bool overwrite);
172
173void vk_physical_device_dispatch_table_from_entrypoints(
174    struct vk_physical_device_dispatch_table *dispatch_table,
175    const struct vk_physical_device_entrypoint_table *entrypoint_table,
176    bool overwrite);
177
178void vk_device_dispatch_table_from_entrypoints(
179    struct vk_device_dispatch_table *dispatch_table,
180    const struct vk_device_entrypoint_table *entrypoint_table,
181    bool overwrite);
182
183PFN_vkVoidFunction
184vk_instance_dispatch_table_get(const struct vk_instance_dispatch_table *table,
185                               const char *name);
186
187PFN_vkVoidFunction
188vk_physical_device_dispatch_table_get(const struct vk_physical_device_dispatch_table *table,
189                                      const char *name);
190
191PFN_vkVoidFunction
192vk_device_dispatch_table_get(const struct vk_device_dispatch_table *table,
193                             const char *name);
194
195PFN_vkVoidFunction
196vk_instance_dispatch_table_get_if_supported(
197    const struct vk_instance_dispatch_table *table,
198    const char *name,
199    uint32_t core_version,
200    const struct vk_instance_extension_table *instance_exts);
201
202PFN_vkVoidFunction
203vk_physical_device_dispatch_table_get_if_supported(
204    const struct vk_physical_device_dispatch_table *table,
205    const char *name,
206    uint32_t core_version,
207    const struct vk_instance_extension_table *instance_exts);
208
209PFN_vkVoidFunction
210vk_device_dispatch_table_get_if_supported(
211    const struct vk_device_dispatch_table *table,
212    const char *name,
213    uint32_t core_version,
214    const struct vk_instance_extension_table *instance_exts,
215    const struct vk_device_extension_table *device_exts);
216
217#ifdef __cplusplus
218}
219#endif
220
221#endif /* VK_DISPATCH_TABLE_H */
222""")
223
224TEMPLATE_C = Template(COPYRIGHT + """\
225/* This file generated from ${filename}, don't edit directly. */
226
227#include "vk_dispatch_table.h"
228
229#include "util/macros.h"
230#include "string.h"
231
232<%def name="load_dispatch_table(type, VkType, ProcAddr, entrypoints)">
233void
234vk_${type}_dispatch_table_load(struct vk_${type}_dispatch_table *table,
235                               PFN_vk${ProcAddr} gpa,
236                               ${VkType} obj)
237{
238% if type != 'physical_device':
239    table->${ProcAddr} = gpa;
240% endif
241% for e in entrypoints:
242  % if e.alias or e.name == '${ProcAddr}':
243    <% continue %>
244  % endif
245  % if e.guard is not None:
246#ifdef ${e.guard}
247  % endif
248    table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${e.name}");
249  % for a in e.aliases:
250    if (table->${e.name} == NULL) {
251        table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${a.name}");
252    }
253  % endfor
254  % if e.guard is not None:
255#endif
256  % endif
257% endfor
258}
259</%def>
260
261${load_dispatch_table('instance', 'VkInstance', 'GetInstanceProcAddr',
262                      instance_entrypoints)}
263
264${load_dispatch_table('physical_device', 'VkInstance', 'GetInstanceProcAddr',
265                      physical_device_entrypoints)}
266
267${load_dispatch_table('device', 'VkDevice', 'GetDeviceProcAddr',
268                      device_entrypoints)}
269
270
271struct string_map_entry {
272   uint32_t name;
273   uint32_t hash;
274   uint32_t num;
275};
276
277/* We use a big string constant to avoid lots of reloctions from the entry
278 * point table to lots of little strings. The entries in the entry point table
279 * store the index into this big string.
280 */
281
282<%def name="strmap(strmap, prefix)">
283static const char ${prefix}_strings[] =
284% for s in strmap.sorted_strings:
285    "${s.string}\\0"
286% endfor
287;
288
289static const struct string_map_entry ${prefix}_string_map_entries[] = {
290% for s in strmap.sorted_strings:
291    { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
292% endfor
293};
294
295/* Hash table stats:
296 * size ${len(strmap.sorted_strings)} entries
297 * collisions entries:
298% for i in range(10):
299 *     ${i}${'+' if i == 9 else ' '}     ${strmap.collisions[i]}
300% endfor
301 */
302
303#define none 0xffff
304static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = {
305% for e in strmap.mapping:
306    ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
307% endfor
308};
309
310static int
311${prefix}_string_map_lookup(const char *str)
312{
313    static const uint32_t prime_factor = ${strmap.prime_factor};
314    static const uint32_t prime_step = ${strmap.prime_step};
315    const struct string_map_entry *e;
316    uint32_t hash, h;
317    uint16_t i;
318    const char *p;
319
320    hash = 0;
321    for (p = str; *p; p++)
322        hash = hash * prime_factor + *p;
323
324    h = hash;
325    while (1) {
326        i = ${prefix}_string_map[h & ${strmap.hash_mask}];
327        if (i == none)
328           return -1;
329        e = &${prefix}_string_map_entries[i];
330        if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0)
331            return e->num;
332        h += prime_step;
333    }
334
335    return -1;
336}
337</%def>
338
339${strmap(instance_strmap, 'instance')}
340${strmap(physical_device_strmap, 'physical_device')}
341${strmap(device_strmap, 'device')}
342
343<% assert len(instance_entrypoints) < 2**8 %>
344static const uint8_t instance_compaction_table[] = {
345% for e in instance_entrypoints:
346    ${e.disp_table_index},
347% endfor
348};
349
350<% assert len(physical_device_entrypoints) < 2**8 %>
351static const uint8_t physical_device_compaction_table[] = {
352% for e in physical_device_entrypoints:
353    ${e.disp_table_index},
354% endfor
355};
356
357<% assert len(device_entrypoints) < 2**16 %>
358static const uint16_t device_compaction_table[] = {
359% for e in device_entrypoints:
360    ${e.disp_table_index},
361% endfor
362};
363
364static bool
365vk_instance_entrypoint_is_enabled(int index, uint32_t core_version,
366                                  const struct vk_instance_extension_table *instance)
367{
368   switch (index) {
369% for e in instance_entrypoints:
370   case ${e.entry_table_index}:
371      /* ${e.name} */
372   % if e.core_version:
373      return ${e.core_version.c_vk_version()} <= core_version;
374   % elif e.extensions:
375     % for ext in e.extensions:
376        % if ext.type == 'instance':
377      if (instance->${ext.name[3:]}) return true;
378        % else:
379      /* All device extensions are considered enabled at the instance level */
380      return true;
381        % endif
382     % endfor
383      return false;
384   % else:
385      return true;
386   % endif
387% endfor
388   default:
389      return false;
390   }
391}
392
393/** Return true if the core version or extension in which the given entrypoint
394 * is defined is enabled.
395 *
396 * If device is NULL, all device extensions are considered enabled.
397 */
398static bool
399vk_physical_device_entrypoint_is_enabled(int index, uint32_t core_version,
400                                         const struct vk_instance_extension_table *instance)
401{
402   switch (index) {
403% for e in physical_device_entrypoints:
404   case ${e.entry_table_index}:
405      /* ${e.name} */
406   % if e.core_version:
407      return ${e.core_version.c_vk_version()} <= core_version;
408   % elif e.extensions:
409     % for ext in e.extensions:
410        % if ext.type == 'instance':
411      if (instance->${ext.name[3:]}) return true;
412        % else:
413      /* All device extensions are considered enabled at the instance level */
414      return true;
415        % endif
416     % endfor
417      return false;
418   % else:
419      return true;
420   % endif
421% endfor
422   default:
423      return false;
424   }
425}
426
427/** Return true if the core version or extension in which the given entrypoint
428 * is defined is enabled.
429 *
430 * If device is NULL, all device extensions are considered enabled.
431 */
432static bool
433vk_device_entrypoint_is_enabled(int index, uint32_t core_version,
434                                const struct vk_instance_extension_table *instance,
435                                const struct vk_device_extension_table *device)
436{
437   switch (index) {
438% for e in device_entrypoints:
439   case ${e.entry_table_index}:
440      /* ${e.name} */
441   % if e.core_version:
442      return ${e.core_version.c_vk_version()} <= core_version;
443   % elif e.extensions:
444     % for ext in e.extensions:
445        % if ext.type == 'instance':
446      if (instance->${ext.name[3:]}) return true;
447        % else:
448      if (!device || device->${ext.name[3:]}) return true;
449        % endif
450     % endfor
451      return false;
452   % else:
453      return true;
454   % endif
455% endfor
456   default:
457      return false;
458   }
459}
460
461#ifdef _MSC_VER
462VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void)
463{
464   unreachable(!"Entrypoint not implemented");
465}
466#endif
467
468<%def name="dispatch_table_from_entrypoints(type)">
469void vk_${type}_dispatch_table_from_entrypoints(
470    struct vk_${type}_dispatch_table *dispatch_table,
471    const struct vk_${type}_entrypoint_table *entrypoint_table,
472    bool overwrite)
473{
474    PFN_vkVoidFunction *disp = (PFN_vkVoidFunction *)dispatch_table;
475    PFN_vkVoidFunction *entry = (PFN_vkVoidFunction *)entrypoint_table;
476
477    if (overwrite) {
478        memset(dispatch_table, 0, sizeof(*dispatch_table));
479        for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) {
480#ifdef _MSC_VER
481            assert(entry[i] != NULL);
482            if (entry[i] == vk_entrypoint_stub)
483#else
484            if (entry[i] == NULL)
485#endif
486                continue;
487            unsigned disp_index = ${type}_compaction_table[i];
488            assert(disp[disp_index] == NULL);
489            disp[disp_index] = entry[i];
490        }
491    } else {
492        for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) {
493            unsigned disp_index = ${type}_compaction_table[i];
494#ifdef _MSC_VER
495            assert(entry[i] != NULL);
496            if (disp[disp_index] == NULL && entry[i] != vk_entrypoint_stub)
497#else
498            if (disp[disp_index] == NULL)
499#endif
500                disp[disp_index] = entry[i];
501        }
502    }
503}
504</%def>
505
506${dispatch_table_from_entrypoints('instance')}
507${dispatch_table_from_entrypoints('physical_device')}
508${dispatch_table_from_entrypoints('device')}
509
510<%def name="lookup_funcs(type)">
511static PFN_vkVoidFunction
512vk_${type}_dispatch_table_get_for_entry_index(
513    const struct vk_${type}_dispatch_table *table, int entry_index)
514{
515    assert(entry_index < ARRAY_SIZE(${type}_compaction_table));
516    int disp_index = ${type}_compaction_table[entry_index];
517    return ((PFN_vkVoidFunction *)table)[disp_index];
518}
519
520PFN_vkVoidFunction
521vk_${type}_dispatch_table_get(
522    const struct vk_${type}_dispatch_table *table, const char *name)
523{
524    int entry_index = ${type}_string_map_lookup(name);
525    if (entry_index < 0)
526        return NULL;
527
528    return vk_${type}_dispatch_table_get_for_entry_index(table, entry_index);
529}
530</%def>
531
532${lookup_funcs('instance')}
533${lookup_funcs('physical_device')}
534${lookup_funcs('device')}
535
536PFN_vkVoidFunction
537vk_instance_dispatch_table_get_if_supported(
538    const struct vk_instance_dispatch_table *table,
539    const char *name,
540    uint32_t core_version,
541    const struct vk_instance_extension_table *instance_exts)
542{
543    int entry_index = instance_string_map_lookup(name);
544    if (entry_index < 0)
545        return NULL;
546
547    if (!vk_instance_entrypoint_is_enabled(entry_index, core_version,
548                                           instance_exts))
549        return NULL;
550
551    return vk_instance_dispatch_table_get_for_entry_index(table, entry_index);
552}
553
554PFN_vkVoidFunction
555vk_physical_device_dispatch_table_get_if_supported(
556    const struct vk_physical_device_dispatch_table *table,
557    const char *name,
558    uint32_t core_version,
559    const struct vk_instance_extension_table *instance_exts)
560{
561    int entry_index = physical_device_string_map_lookup(name);
562    if (entry_index < 0)
563        return NULL;
564
565    if (!vk_physical_device_entrypoint_is_enabled(entry_index, core_version,
566                                                  instance_exts))
567        return NULL;
568
569    return vk_physical_device_dispatch_table_get_for_entry_index(table, entry_index);
570}
571
572PFN_vkVoidFunction
573vk_device_dispatch_table_get_if_supported(
574    const struct vk_device_dispatch_table *table,
575    const char *name,
576    uint32_t core_version,
577    const struct vk_instance_extension_table *instance_exts,
578    const struct vk_device_extension_table *device_exts)
579{
580    int entry_index = device_string_map_lookup(name);
581    if (entry_index < 0)
582        return NULL;
583
584    if (!vk_device_entrypoint_is_enabled(entry_index, core_version,
585                                         instance_exts, device_exts))
586        return NULL;
587
588    return vk_device_dispatch_table_get_for_entry_index(table, entry_index);
589}
590""")
591
592U32_MASK = 2**32 - 1
593
594PRIME_FACTOR = 5024183
595PRIME_STEP = 19
596
597class StringIntMapEntry:
598    def __init__(self, string, num):
599        self.string = string
600        self.num = num
601
602        # Calculate the same hash value that we will calculate in C.
603        h = 0
604        for c in string:
605            h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
606        self.hash = h
607
608        self.offset = None
609
610def round_to_pow2(x):
611    return 2**int(math.ceil(math.log(x, 2)))
612
613class StringIntMap:
614    def __init__(self):
615        self.baked = False
616        self.strings = {}
617
618    def add_string(self, string, num):
619        assert not self.baked
620        assert string not in self.strings
621        assert 0 <= num < 2**31
622        self.strings[string] = StringIntMapEntry(string, num)
623
624    def bake(self):
625        self.sorted_strings = \
626            sorted(self.strings.values(), key=lambda x: x.string)
627        offset = 0
628        for entry in self.sorted_strings:
629            entry.offset = offset
630            offset += len(entry.string) + 1
631
632        # Save off some values that we'll need in C
633        self.hash_size = round_to_pow2(len(self.strings) * 1.25)
634        self.hash_mask = self.hash_size - 1
635        self.prime_factor = PRIME_FACTOR
636        self.prime_step = PRIME_STEP
637
638        self.mapping = [-1] * self.hash_size
639        self.collisions = [0] * 10
640        for idx, s in enumerate(self.sorted_strings):
641            level = 0
642            h = s.hash
643            while self.mapping[h & self.hash_mask] >= 0:
644                h = h + PRIME_STEP
645                level = level + 1
646            self.collisions[min(level, 9)] += 1
647            self.mapping[h & self.hash_mask] = idx
648
649def main():
650    parser = argparse.ArgumentParser()
651    parser.add_argument('--out-c', help='Output C file.')
652    parser.add_argument('--out-h', help='Output H file.')
653    parser.add_argument('--xml',
654                        help='Vulkan API XML file.',
655                        required=True,
656                        action='append',
657                        dest='xml_files')
658    args = parser.parse_args()
659
660    entrypoints = get_entrypoints_from_xml(args.xml_files)
661
662    device_entrypoints = []
663    physical_device_entrypoints = []
664    instance_entrypoints = []
665    for e in entrypoints:
666        if e.is_device_entrypoint():
667            device_entrypoints.append(e)
668        elif e.is_physical_device_entrypoint():
669            physical_device_entrypoints.append(e)
670        else:
671            instance_entrypoints.append(e)
672
673    for i, e in enumerate(e for e in device_entrypoints if not e.alias):
674        e.disp_table_index = i
675
676    device_strmap = StringIntMap()
677    for i, e in enumerate(device_entrypoints):
678        e.entry_table_index = i
679        device_strmap.add_string("vk" + e.name, e.entry_table_index)
680    device_strmap.bake()
681
682    for i, e in enumerate(e for e in physical_device_entrypoints if not e.alias):
683        e.disp_table_index = i
684
685    physical_device_strmap = StringIntMap()
686    for i, e in enumerate(physical_device_entrypoints):
687        e.entry_table_index = i
688        physical_device_strmap.add_string("vk" + e.name, e.entry_table_index)
689    physical_device_strmap.bake()
690
691    for i, e in enumerate(e for e in instance_entrypoints if not e.alias):
692        e.disp_table_index = i
693
694    instance_strmap = StringIntMap()
695    for i, e in enumerate(instance_entrypoints):
696        e.entry_table_index = i
697        instance_strmap.add_string("vk" + e.name, e.entry_table_index)
698    instance_strmap.bake()
699
700    # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
701    # per entry point.
702    try:
703        if args.out_h:
704            with open(args.out_h, 'w') as f:
705                f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints,
706                                          physical_device_entrypoints=physical_device_entrypoints,
707                                          device_entrypoints=device_entrypoints,
708                                          filename=os.path.basename(__file__)))
709        if args.out_c:
710            with open(args.out_c, 'w') as f:
711                f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints,
712                                          physical_device_entrypoints=physical_device_entrypoints,
713                                          device_entrypoints=device_entrypoints,
714                                          instance_strmap=instance_strmap,
715                                          physical_device_strmap=physical_device_strmap,
716                                          device_strmap=device_strmap,
717                                          filename=os.path.basename(__file__)))
718    except Exception:
719        # In the event there's an error, this imports some helpers from mako
720        # to print a useful stack trace and prints it, then exits with
721        # status 1, if python is run with debug; otherwise it just raises
722        # the exception
723        import sys
724        from mako import exceptions
725        print(exceptions.text_error_template().render(), file=sys.stderr)
726        sys.exit(1)
727
728
729if __name__ == '__main__':
730    main()
731