anv: Split dispatch tables into device and instance
[mesa.git] / src / intel / vulkan / anv_entrypoints_gen.py
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
25 import argparse
26 import math
27 import os
28 import xml.etree.cElementTree as et
29
30 from collections import OrderedDict, namedtuple
31 from mako.template import Template
32
33 from anv_extensions import VkVersion, MAX_API_VERSION, EXTENSIONS
34
35 # We generate a static hash table for entry point lookup
36 # (vkGetProcAddress). We use a linear congruential generator for our hash
37 # function and a power-of-two size table. The prime numbers are determined
38 # experimentally.
39
40 LAYERS = [
41 'anv',
42 'gen7',
43 'gen75',
44 'gen8',
45 'gen9',
46 'gen10',
47 'gen11',
48 ]
49
50 TEMPLATE_H = Template("""\
51 /* This file generated from ${filename}, don't edit directly. */
52
53 struct anv_instance_dispatch_table {
54 union {
55 void *entrypoints[${len(instance_entrypoints)}];
56 struct {
57 % for e in instance_entrypoints:
58 % if e.guard is not None:
59 #ifdef ${e.guard}
60 PFN_${e.name} ${e.name};
61 #else
62 void *${e.name};
63 # endif
64 % else:
65 PFN_${e.name} ${e.name};
66 % endif
67 % endfor
68 };
69 };
70 };
71
72 struct anv_device_dispatch_table {
73 union {
74 void *entrypoints[${len(device_entrypoints)}];
75 struct {
76 % for e in device_entrypoints:
77 % if e.guard is not None:
78 #ifdef ${e.guard}
79 PFN_${e.name} ${e.name};
80 #else
81 void *${e.name};
82 # endif
83 % else:
84 PFN_${e.name} ${e.name};
85 % endif
86 % endfor
87 };
88 };
89 };
90
91 extern const struct anv_instance_dispatch_table anv_instance_dispatch_table;
92 %for layer in LAYERS:
93 extern const struct anv_device_dispatch_table ${layer}_device_dispatch_table;
94 %endfor
95 extern const struct anv_device_dispatch_table anv_tramp_device_dispatch_table;
96
97 % for e in instance_entrypoints:
98 % if e.alias:
99 <% continue %>
100 % endif
101 % if e.guard is not None:
102 #ifdef ${e.guard}
103 % endif
104 ${e.return_type} ${e.prefixed_name('anv')}(${e.decl_params()});
105 % if e.guard is not None:
106 #endif // ${e.guard}
107 % endif
108 % endfor
109
110 % for e in device_entrypoints:
111 % if e.alias:
112 <% continue %>
113 % endif
114 % if e.guard is not None:
115 #ifdef ${e.guard}
116 % endif
117 % for layer in LAYERS:
118 ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()});
119 % endfor
120 % if e.guard is not None:
121 #endif // ${e.guard}
122 % endif
123 % endfor
124 """, output_encoding='utf-8')
125
126 TEMPLATE_C = Template(u"""\
127 /*
128 * Copyright © 2015 Intel Corporation
129 *
130 * Permission is hereby granted, free of charge, to any person obtaining a
131 * copy of this software and associated documentation files (the "Software"),
132 * to deal in the Software without restriction, including without limitation
133 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
134 * and/or sell copies of the Software, and to permit persons to whom the
135 * Software is furnished to do so, subject to the following conditions:
136 *
137 * The above copyright notice and this permission notice (including the next
138 * paragraph) shall be included in all copies or substantial portions of the
139 * Software.
140 *
141 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
142 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
143 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
144 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
145 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
146 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
147 * IN THE SOFTWARE.
148 */
149
150 /* This file generated from ${filename}, don't edit directly. */
151
152 #include "anv_private.h"
153
154 struct string_map_entry {
155 uint32_t name;
156 uint32_t hash;
157 uint32_t num;
158 };
159
160 /* We use a big string constant to avoid lots of reloctions from the entry
161 * point table to lots of little strings. The entries in the entry point table
162 * store the index into this big string.
163 */
164
165 <%def name="strmap(strmap, prefix)">
166 static const char ${prefix}_strings[] =
167 % for s in strmap.sorted_strings:
168 "${s.string}\\0"
169 % endfor
170 ;
171
172 static const struct string_map_entry ${prefix}_string_map_entries[] = {
173 % for s in strmap.sorted_strings:
174 { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
175 % endfor
176 };
177
178 /* Hash table stats:
179 * size ${len(strmap.sorted_strings)} entries
180 * collisions entries:
181 % for i in range(10):
182 * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]}
183 % endfor
184 */
185
186 #define none 0xffff
187 static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = {
188 % for e in strmap.mapping:
189 ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
190 % endfor
191 };
192
193 static int
194 ${prefix}_string_map_lookup(const char *str)
195 {
196 static const uint32_t prime_factor = ${strmap.prime_factor};
197 static const uint32_t prime_step = ${strmap.prime_step};
198 const struct string_map_entry *e;
199 uint32_t hash, h;
200 uint16_t i;
201 const char *p;
202
203 hash = 0;
204 for (p = str; *p; p++)
205 hash = hash * prime_factor + *p;
206
207 h = hash;
208 while (1) {
209 i = ${prefix}_string_map[h & ${strmap.hash_mask}];
210 if (i == none)
211 return -1;
212 e = &${prefix}_string_map_entries[i];
213 if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0)
214 return e->num;
215 h += prime_step;
216 }
217
218 return -1;
219 }
220 </%def>
221
222 ${strmap(instance_strmap, 'instance')}
223 ${strmap(device_strmap, 'device')}
224
225 /* Weak aliases for all potential implementations. These will resolve to
226 * NULL if they're not defined, which lets the resolve_entrypoint() function
227 * either pick the correct entry point.
228 */
229
230 % for e in instance_entrypoints:
231 % if e.alias:
232 <% continue %>
233 % endif
234 % if e.guard is not None:
235 #ifdef ${e.guard}
236 % endif
237 ${e.return_type} ${e.prefixed_name('anv')}(${e.decl_params()}) __attribute__ ((weak));
238 % if e.guard is not None:
239 #endif // ${e.guard}
240 % endif
241 % endfor
242
243 const struct anv_instance_dispatch_table anv_instance_dispatch_table = {
244 % for e in instance_entrypoints:
245 % if e.guard is not None:
246 #ifdef ${e.guard}
247 % endif
248 .${e.name} = ${e.prefixed_name('anv')},
249 % if e.guard is not None:
250 #endif // ${e.guard}
251 % endif
252 % endfor
253 };
254
255 % for layer in LAYERS:
256 % for e in device_entrypoints:
257 % if e.alias:
258 <% continue %>
259 % endif
260 % if e.guard is not None:
261 #ifdef ${e.guard}
262 % endif
263 ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}) __attribute__ ((weak));
264 % if e.guard is not None:
265 #endif // ${e.guard}
266 % endif
267 % endfor
268
269 const struct anv_device_dispatch_table ${layer}_device_dispatch_table = {
270 % for e in device_entrypoints:
271 % if e.guard is not None:
272 #ifdef ${e.guard}
273 % endif
274 .${e.name} = ${e.prefixed_name(layer)},
275 % if e.guard is not None:
276 #endif // ${e.guard}
277 % endif
278 % endfor
279 };
280 % endfor
281
282
283 /** Trampoline entrypoints for all device functions */
284
285 % for e in device_entrypoints:
286 % if e.alias:
287 <% continue %>
288 % endif
289 % if e.guard is not None:
290 #ifdef ${e.guard}
291 % endif
292 static ${e.return_type}
293 ${e.prefixed_name('anv_tramp')}(${e.decl_params()})
294 {
295 % if e.params[0].type == 'VkDevice':
296 ANV_FROM_HANDLE(anv_device, anv_device, ${e.params[0].name});
297 return anv_device->dispatch.${e.name}(${e.call_params()});
298 % elif e.params[0].type == 'VkCommandBuffer':
299 ANV_FROM_HANDLE(anv_cmd_buffer, anv_cmd_buffer, ${e.params[0].name});
300 return anv_cmd_buffer->device->dispatch.${e.name}(${e.call_params()});
301 % elif e.params[0].type == 'VkQueue':
302 ANV_FROM_HANDLE(anv_queue, anv_queue, ${e.params[0].name});
303 return anv_queue->device->dispatch.${e.name}(${e.call_params()});
304 % else:
305 assert(!"Unhandled device child trampoline case: ${e.params[0].type}");
306 % endif
307 }
308 % if e.guard is not None:
309 #endif // ${e.guard}
310 % endif
311 % endfor
312
313 const struct anv_device_dispatch_table anv_tramp_device_dispatch_table = {
314 % for e in device_entrypoints:
315 % if e.guard is not None:
316 #ifdef ${e.guard}
317 % endif
318 .${e.name} = ${e.prefixed_name('anv_tramp')},
319 % if e.guard is not None:
320 #endif // ${e.guard}
321 % endif
322 % endfor
323 };
324
325
326 /** Return true if the core version or extension in which the given entrypoint
327 * is defined is enabled.
328 *
329 * If device is NULL, all device extensions are considered enabled.
330 */
331 bool
332 anv_instance_entrypoint_is_enabled(int index, uint32_t core_version,
333 const struct anv_instance_extension_table *instance)
334 {
335 switch (index) {
336 % for e in instance_entrypoints:
337 case ${e.num}:
338 /* ${e.name} */
339 % if e.core_version:
340 return ${e.core_version.c_vk_version()} <= core_version;
341 % elif e.extensions:
342 % for ext in e.extensions:
343 % if ext.type == 'instance':
344 if (instance->${ext.name[3:]}) return true;
345 % else:
346 /* All device extensions are considered enabled at the instance level */
347 return true;
348 % endif
349 % endfor
350 return false;
351 % else:
352 return true;
353 % endif
354 % endfor
355 default:
356 return false;
357 }
358 }
359
360 /** Return true if the core version or extension in which the given entrypoint
361 * is defined is enabled.
362 *
363 * If device is NULL, all device extensions are considered enabled.
364 */
365 bool
366 anv_device_entrypoint_is_enabled(int index, uint32_t core_version,
367 const struct anv_instance_extension_table *instance,
368 const struct anv_device_extension_table *device)
369 {
370 switch (index) {
371 % for e in device_entrypoints:
372 case ${e.num}:
373 /* ${e.name} */
374 % if e.core_version:
375 return ${e.core_version.c_vk_version()} <= core_version;
376 % elif e.extensions:
377 % for ext in e.extensions:
378 % if ext.type == 'instance':
379 <% assert False %>
380 % else:
381 if (!device || device->${ext.name[3:]}) return true;
382 % endif
383 % endfor
384 return false;
385 % else:
386 return true;
387 % endif
388 % endfor
389 default:
390 return false;
391 }
392 }
393
394 int
395 anv_get_instance_entrypoint_index(const char *name)
396 {
397 return instance_string_map_lookup(name);
398 }
399
400 int
401 anv_get_device_entrypoint_index(const char *name)
402 {
403 return device_string_map_lookup(name);
404 }
405
406 static void * __attribute__ ((noinline))
407 anv_resolve_device_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
408 {
409 const struct anv_device_dispatch_table *genX_table;
410 switch (devinfo->gen) {
411 case 11:
412 genX_table = &gen11_device_dispatch_table;
413 break;
414 case 10:
415 genX_table = &gen10_device_dispatch_table;
416 break;
417 case 9:
418 genX_table = &gen9_device_dispatch_table;
419 break;
420 case 8:
421 genX_table = &gen8_device_dispatch_table;
422 break;
423 case 7:
424 if (devinfo->is_haswell)
425 genX_table = &gen75_device_dispatch_table;
426 else
427 genX_table = &gen7_device_dispatch_table;
428 break;
429 default:
430 unreachable("unsupported gen\\n");
431 }
432
433 if (genX_table->entrypoints[index])
434 return genX_table->entrypoints[index];
435 else
436 return anv_device_dispatch_table.entrypoints[index];
437 }
438
439 void *
440 anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)
441 {
442 int idx = anv_get_instance_entrypoint_index(name);
443 if (idx >= 0)
444 return anv_instance_dispatch_table.entrypoints[idx];
445
446 idx = anv_get_device_entrypoint_index(name);
447 if (idx >= 0)
448 return anv_resolve_device_entrypoint(devinfo, idx);
449
450 return NULL;
451 }""", output_encoding='utf-8')
452
453 U32_MASK = 2**32 - 1
454
455 PRIME_FACTOR = 5024183
456 PRIME_STEP = 19
457
458 class StringIntMapEntry(object):
459 def __init__(self, string, num):
460 self.string = string
461 self.num = num
462
463 # Calculate the same hash value that we will calculate in C.
464 h = 0
465 for c in string:
466 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
467 self.hash = h
468
469 self.offset = None
470
471 def round_to_pow2(x):
472 return 2**int(math.ceil(math.log(x, 2)))
473
474 class StringIntMap(object):
475 def __init__(self):
476 self.baked = False
477 self.strings = dict()
478
479 def add_string(self, string, num):
480 assert not self.baked
481 assert string not in self.strings
482 assert num >= 0 and num < 2**31
483 self.strings[string] = StringIntMapEntry(string, num)
484
485 def bake(self):
486 self.sorted_strings = \
487 sorted(self.strings.values(), key=lambda x: x.string)
488 offset = 0
489 for entry in self.sorted_strings:
490 entry.offset = offset
491 offset += len(entry.string) + 1
492
493 # Save off some values that we'll need in C
494 self.hash_size = round_to_pow2(len(self.strings) * 1.25)
495 self.hash_mask = self.hash_size - 1
496 self.prime_factor = PRIME_FACTOR
497 self.prime_step = PRIME_STEP
498
499 self.mapping = [-1] * self.hash_size
500 self.collisions = [0] * 10
501 for idx, s in enumerate(self.sorted_strings):
502 level = 0
503 h = s.hash
504 while self.mapping[h & self.hash_mask] >= 0:
505 h = h + PRIME_STEP
506 level = level + 1
507 self.collisions[min(level, 9)] += 1
508 self.mapping[h & self.hash_mask] = idx
509
510 EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
511
512 class EntrypointBase(object):
513 def __init__(self, name):
514 self.name = name
515 self.alias = None
516 self.guard = None
517 self.enabled = False
518 self.num = None
519 # Extensions which require this entrypoint
520 self.core_version = None
521 self.extensions = []
522
523 class Entrypoint(EntrypointBase):
524 def __init__(self, name, return_type, params, guard = None):
525 super(Entrypoint, self).__init__(name)
526 self.return_type = return_type
527 self.params = params
528 self.guard = guard
529
530 def is_device_entrypoint(self):
531 return self.params[0].type in ('VkDevice', 'VkCommandBuffer', 'VkQueue')
532
533 def prefixed_name(self, prefix):
534 assert self.name.startswith('vk')
535 return prefix + '_' + self.name[2:]
536
537 def decl_params(self):
538 return ', '.join(p.decl for p in self.params)
539
540 def call_params(self):
541 return ', '.join(p.name for p in self.params)
542
543 class EntrypointAlias(EntrypointBase):
544 def __init__(self, name, entrypoint):
545 super(EntrypointAlias, self).__init__(name)
546 self.alias = entrypoint
547
548 def is_device_entrypoint(self):
549 return self.alias.is_device_entrypoint()
550
551 def prefixed_name(self, prefix):
552 return self.alias.prefixed_name(prefix)
553
554 def get_entrypoints(doc, entrypoints_to_defines, start_index):
555 """Extract the entry points from the registry."""
556 entrypoints = OrderedDict()
557
558 for command in doc.findall('./commands/command'):
559 if 'alias' in command.attrib:
560 alias = command.attrib['name']
561 target = command.attrib['alias']
562 entrypoints[alias] = EntrypointAlias(alias, entrypoints[target])
563 else:
564 name = command.find('./proto/name').text
565 ret_type = command.find('./proto/type').text
566 params = [EntrypointParam(
567 type = p.find('./type').text,
568 name = p.find('./name').text,
569 decl = ''.join(p.itertext())
570 ) for p in command.findall('./param')]
571 guard = entrypoints_to_defines.get(name)
572 # They really need to be unique
573 assert name not in entrypoints
574 entrypoints[name] = Entrypoint(name, ret_type, params, guard)
575
576 for feature in doc.findall('./feature'):
577 assert feature.attrib['api'] == 'vulkan'
578 version = VkVersion(feature.attrib['number'])
579 if version > MAX_API_VERSION:
580 continue
581
582 for command in feature.findall('./require/command'):
583 e = entrypoints[command.attrib['name']]
584 e.enabled = True
585 assert e.core_version is None
586 e.core_version = version
587
588 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
589 for extension in doc.findall('.extensions/extension'):
590 ext_name = extension.attrib['name']
591 if ext_name not in supported_exts:
592 continue
593
594 ext = supported_exts[ext_name]
595 ext.type = extension.attrib['type']
596
597 for command in extension.findall('./require/command'):
598 e = entrypoints[command.attrib['name']]
599 e.enabled = True
600 assert e.core_version is None
601 e.extensions.append(ext)
602
603 return [e for e in entrypoints.values() if e.enabled]
604
605
606 def get_entrypoints_defines(doc):
607 """Maps entry points to extension defines."""
608 entrypoints_to_defines = {}
609
610 for extension in doc.findall('./extensions/extension[@platform]'):
611 platform = extension.attrib['platform']
612 ext = '_KHR'
613 if platform.upper() == 'XLIB_XRANDR':
614 ext = '_EXT'
615 define = 'VK_USE_PLATFORM_' + platform.upper() + ext
616
617 for entrypoint in extension.findall('./require/command'):
618 fullname = entrypoint.attrib['name']
619 entrypoints_to_defines[fullname] = define
620
621 return entrypoints_to_defines
622
623
624 def main():
625 parser = argparse.ArgumentParser()
626 parser.add_argument('--outdir', help='Where to write the files.',
627 required=True)
628 parser.add_argument('--xml',
629 help='Vulkan API XML file.',
630 required=True,
631 action='append',
632 dest='xml_files')
633 args = parser.parse_args()
634
635 entrypoints = []
636
637 for filename in args.xml_files:
638 doc = et.parse(filename)
639 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
640 start_index=len(entrypoints))
641
642 # Manually add CreateDmaBufImageINTEL for which we don't have an extension
643 # defined.
644 entrypoints.append(Entrypoint('vkCreateDmaBufImageINTEL', 'VkResult', [
645 EntrypointParam('VkDevice', 'device', 'VkDevice device'),
646 EntrypointParam('VkDmaBufImageCreateInfo', 'pCreateInfo',
647 'const VkDmaBufImageCreateInfo* pCreateInfo'),
648 EntrypointParam('VkAllocationCallbacks', 'pAllocator',
649 'const VkAllocationCallbacks* pAllocator'),
650 EntrypointParam('VkDeviceMemory', 'pMem', 'VkDeviceMemory* pMem'),
651 EntrypointParam('VkImage', 'pImage', 'VkImage* pImage')
652 ]))
653
654 device_entrypoints = []
655 instance_entrypoints = []
656 for e in entrypoints:
657 if e.is_device_entrypoint():
658 device_entrypoints.append(e)
659 else:
660 instance_entrypoints.append(e)
661
662 device_strmap = StringIntMap()
663 for num, e in enumerate(device_entrypoints):
664 device_strmap.add_string(e.name, num)
665 e.num = num
666 device_strmap.bake()
667
668 instance_strmap = StringIntMap()
669 for num, e in enumerate(instance_entrypoints):
670 instance_strmap.add_string(e.name, num)
671 e.num = num
672 instance_strmap.bake()
673
674 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
675 # per entry point.
676 try:
677 with open(os.path.join(args.outdir, 'anv_entrypoints.h'), 'wb') as f:
678 f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints,
679 device_entrypoints=device_entrypoints,
680 LAYERS=LAYERS,
681 filename=os.path.basename(__file__)))
682 with open(os.path.join(args.outdir, 'anv_entrypoints.c'), 'wb') as f:
683 f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints,
684 device_entrypoints=device_entrypoints,
685 LAYERS=LAYERS,
686 instance_strmap=instance_strmap,
687 device_strmap=device_strmap,
688 filename=os.path.basename(__file__)))
689 except Exception:
690 # In the even there's an error this imports some helpers from mako
691 # to print a useful stack trace and prints it, then exits with
692 # status 1, if python is run with debug; otherwise it just raises
693 # the exception
694 if __debug__:
695 import sys
696 from mako import exceptions
697 sys.stderr.write(exceptions.text_error_template().render() + '\n')
698 sys.exit(1)
699 raise
700
701
702 if __name__ == '__main__':
703 main()