anv: Stop generating weak references for instance entrypoints
[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 const struct anv_instance_dispatch_table anv_instance_dispatch_table = {
231 % for e in instance_entrypoints:
232 % if e.guard is not None:
233 #ifdef ${e.guard}
234 % endif
235 .${e.name} = ${e.prefixed_name('anv')},
236 % if e.guard is not None:
237 #endif // ${e.guard}
238 % endif
239 % endfor
240 };
241
242 % for layer in LAYERS:
243 % for e in device_entrypoints:
244 % if e.alias:
245 <% continue %>
246 % endif
247 % if e.guard is not None:
248 #ifdef ${e.guard}
249 % endif
250 ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}) __attribute__ ((weak));
251 % if e.guard is not None:
252 #endif // ${e.guard}
253 % endif
254 % endfor
255
256 const struct anv_device_dispatch_table ${layer}_device_dispatch_table = {
257 % for e in device_entrypoints:
258 % if e.guard is not None:
259 #ifdef ${e.guard}
260 % endif
261 .${e.name} = ${e.prefixed_name(layer)},
262 % if e.guard is not None:
263 #endif // ${e.guard}
264 % endif
265 % endfor
266 };
267 % endfor
268
269
270 /** Trampoline entrypoints for all device functions */
271
272 % for e in device_entrypoints:
273 % if e.alias:
274 <% continue %>
275 % endif
276 % if e.guard is not None:
277 #ifdef ${e.guard}
278 % endif
279 static ${e.return_type}
280 ${e.prefixed_name('anv_tramp')}(${e.decl_params()})
281 {
282 % if e.params[0].type == 'VkDevice':
283 ANV_FROM_HANDLE(anv_device, anv_device, ${e.params[0].name});
284 return anv_device->dispatch.${e.name}(${e.call_params()});
285 % elif e.params[0].type == 'VkCommandBuffer':
286 ANV_FROM_HANDLE(anv_cmd_buffer, anv_cmd_buffer, ${e.params[0].name});
287 return anv_cmd_buffer->device->dispatch.${e.name}(${e.call_params()});
288 % elif e.params[0].type == 'VkQueue':
289 ANV_FROM_HANDLE(anv_queue, anv_queue, ${e.params[0].name});
290 return anv_queue->device->dispatch.${e.name}(${e.call_params()});
291 % else:
292 assert(!"Unhandled device child trampoline case: ${e.params[0].type}");
293 % endif
294 }
295 % if e.guard is not None:
296 #endif // ${e.guard}
297 % endif
298 % endfor
299
300 const struct anv_device_dispatch_table anv_tramp_device_dispatch_table = {
301 % for e in device_entrypoints:
302 % if e.guard is not None:
303 #ifdef ${e.guard}
304 % endif
305 .${e.name} = ${e.prefixed_name('anv_tramp')},
306 % if e.guard is not None:
307 #endif // ${e.guard}
308 % endif
309 % endfor
310 };
311
312
313 /** Return true if the core version or extension in which the given entrypoint
314 * is defined is enabled.
315 *
316 * If device is NULL, all device extensions are considered enabled.
317 */
318 bool
319 anv_instance_entrypoint_is_enabled(int index, uint32_t core_version,
320 const struct anv_instance_extension_table *instance)
321 {
322 switch (index) {
323 % for e in instance_entrypoints:
324 case ${e.num}:
325 /* ${e.name} */
326 % if e.core_version:
327 return ${e.core_version.c_vk_version()} <= core_version;
328 % elif e.extensions:
329 % for ext in e.extensions:
330 % if ext.type == 'instance':
331 if (instance->${ext.name[3:]}) return true;
332 % else:
333 /* All device extensions are considered enabled at the instance level */
334 return true;
335 % endif
336 % endfor
337 return false;
338 % else:
339 return true;
340 % endif
341 % endfor
342 default:
343 return false;
344 }
345 }
346
347 /** Return true if the core version or extension in which the given entrypoint
348 * is defined is enabled.
349 *
350 * If device is NULL, all device extensions are considered enabled.
351 */
352 bool
353 anv_device_entrypoint_is_enabled(int index, uint32_t core_version,
354 const struct anv_instance_extension_table *instance,
355 const struct anv_device_extension_table *device)
356 {
357 switch (index) {
358 % for e in device_entrypoints:
359 case ${e.num}:
360 /* ${e.name} */
361 % if e.core_version:
362 return ${e.core_version.c_vk_version()} <= core_version;
363 % elif e.extensions:
364 % for ext in e.extensions:
365 % if ext.type == 'instance':
366 <% assert False %>
367 % else:
368 if (!device || device->${ext.name[3:]}) return true;
369 % endif
370 % endfor
371 return false;
372 % else:
373 return true;
374 % endif
375 % endfor
376 default:
377 return false;
378 }
379 }
380
381 int
382 anv_get_instance_entrypoint_index(const char *name)
383 {
384 return instance_string_map_lookup(name);
385 }
386
387 int
388 anv_get_device_entrypoint_index(const char *name)
389 {
390 return device_string_map_lookup(name);
391 }
392
393 static void * __attribute__ ((noinline))
394 anv_resolve_device_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
395 {
396 const struct anv_device_dispatch_table *genX_table;
397 switch (devinfo->gen) {
398 case 11:
399 genX_table = &gen11_device_dispatch_table;
400 break;
401 case 10:
402 genX_table = &gen10_device_dispatch_table;
403 break;
404 case 9:
405 genX_table = &gen9_device_dispatch_table;
406 break;
407 case 8:
408 genX_table = &gen8_device_dispatch_table;
409 break;
410 case 7:
411 if (devinfo->is_haswell)
412 genX_table = &gen75_device_dispatch_table;
413 else
414 genX_table = &gen7_device_dispatch_table;
415 break;
416 default:
417 unreachable("unsupported gen\\n");
418 }
419
420 if (genX_table->entrypoints[index])
421 return genX_table->entrypoints[index];
422 else
423 return anv_device_dispatch_table.entrypoints[index];
424 }
425
426 void *
427 anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)
428 {
429 int idx = anv_get_instance_entrypoint_index(name);
430 if (idx >= 0)
431 return anv_instance_dispatch_table.entrypoints[idx];
432
433 idx = anv_get_device_entrypoint_index(name);
434 if (idx >= 0)
435 return anv_resolve_device_entrypoint(devinfo, idx);
436
437 return NULL;
438 }""", output_encoding='utf-8')
439
440 U32_MASK = 2**32 - 1
441
442 PRIME_FACTOR = 5024183
443 PRIME_STEP = 19
444
445 class StringIntMapEntry(object):
446 def __init__(self, string, num):
447 self.string = string
448 self.num = num
449
450 # Calculate the same hash value that we will calculate in C.
451 h = 0
452 for c in string:
453 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
454 self.hash = h
455
456 self.offset = None
457
458 def round_to_pow2(x):
459 return 2**int(math.ceil(math.log(x, 2)))
460
461 class StringIntMap(object):
462 def __init__(self):
463 self.baked = False
464 self.strings = dict()
465
466 def add_string(self, string, num):
467 assert not self.baked
468 assert string not in self.strings
469 assert num >= 0 and num < 2**31
470 self.strings[string] = StringIntMapEntry(string, num)
471
472 def bake(self):
473 self.sorted_strings = \
474 sorted(self.strings.values(), key=lambda x: x.string)
475 offset = 0
476 for entry in self.sorted_strings:
477 entry.offset = offset
478 offset += len(entry.string) + 1
479
480 # Save off some values that we'll need in C
481 self.hash_size = round_to_pow2(len(self.strings) * 1.25)
482 self.hash_mask = self.hash_size - 1
483 self.prime_factor = PRIME_FACTOR
484 self.prime_step = PRIME_STEP
485
486 self.mapping = [-1] * self.hash_size
487 self.collisions = [0] * 10
488 for idx, s in enumerate(self.sorted_strings):
489 level = 0
490 h = s.hash
491 while self.mapping[h & self.hash_mask] >= 0:
492 h = h + PRIME_STEP
493 level = level + 1
494 self.collisions[min(level, 9)] += 1
495 self.mapping[h & self.hash_mask] = idx
496
497 EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
498
499 class EntrypointBase(object):
500 def __init__(self, name):
501 self.name = name
502 self.alias = None
503 self.guard = None
504 self.enabled = False
505 self.num = None
506 # Extensions which require this entrypoint
507 self.core_version = None
508 self.extensions = []
509
510 class Entrypoint(EntrypointBase):
511 def __init__(self, name, return_type, params, guard = None):
512 super(Entrypoint, self).__init__(name)
513 self.return_type = return_type
514 self.params = params
515 self.guard = guard
516
517 def is_device_entrypoint(self):
518 return self.params[0].type in ('VkDevice', 'VkCommandBuffer', 'VkQueue')
519
520 def prefixed_name(self, prefix):
521 assert self.name.startswith('vk')
522 return prefix + '_' + self.name[2:]
523
524 def decl_params(self):
525 return ', '.join(p.decl for p in self.params)
526
527 def call_params(self):
528 return ', '.join(p.name for p in self.params)
529
530 class EntrypointAlias(EntrypointBase):
531 def __init__(self, name, entrypoint):
532 super(EntrypointAlias, self).__init__(name)
533 self.alias = entrypoint
534
535 def is_device_entrypoint(self):
536 return self.alias.is_device_entrypoint()
537
538 def prefixed_name(self, prefix):
539 return self.alias.prefixed_name(prefix)
540
541 def get_entrypoints(doc, entrypoints_to_defines, start_index):
542 """Extract the entry points from the registry."""
543 entrypoints = OrderedDict()
544
545 for command in doc.findall('./commands/command'):
546 if 'alias' in command.attrib:
547 alias = command.attrib['name']
548 target = command.attrib['alias']
549 entrypoints[alias] = EntrypointAlias(alias, entrypoints[target])
550 else:
551 name = command.find('./proto/name').text
552 ret_type = command.find('./proto/type').text
553 params = [EntrypointParam(
554 type = p.find('./type').text,
555 name = p.find('./name').text,
556 decl = ''.join(p.itertext())
557 ) for p in command.findall('./param')]
558 guard = entrypoints_to_defines.get(name)
559 # They really need to be unique
560 assert name not in entrypoints
561 entrypoints[name] = Entrypoint(name, ret_type, params, guard)
562
563 for feature in doc.findall('./feature'):
564 assert feature.attrib['api'] == 'vulkan'
565 version = VkVersion(feature.attrib['number'])
566 if version > MAX_API_VERSION:
567 continue
568
569 for command in feature.findall('./require/command'):
570 e = entrypoints[command.attrib['name']]
571 e.enabled = True
572 assert e.core_version is None
573 e.core_version = version
574
575 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
576 for extension in doc.findall('.extensions/extension'):
577 ext_name = extension.attrib['name']
578 if ext_name not in supported_exts:
579 continue
580
581 ext = supported_exts[ext_name]
582 ext.type = extension.attrib['type']
583
584 for command in extension.findall('./require/command'):
585 e = entrypoints[command.attrib['name']]
586 e.enabled = True
587 assert e.core_version is None
588 e.extensions.append(ext)
589
590 return [e for e in entrypoints.values() if e.enabled]
591
592
593 def get_entrypoints_defines(doc):
594 """Maps entry points to extension defines."""
595 entrypoints_to_defines = {}
596
597 for extension in doc.findall('./extensions/extension[@platform]'):
598 platform = extension.attrib['platform']
599 ext = '_KHR'
600 if platform.upper() == 'XLIB_XRANDR':
601 ext = '_EXT'
602 define = 'VK_USE_PLATFORM_' + platform.upper() + ext
603
604 for entrypoint in extension.findall('./require/command'):
605 fullname = entrypoint.attrib['name']
606 entrypoints_to_defines[fullname] = define
607
608 return entrypoints_to_defines
609
610
611 def main():
612 parser = argparse.ArgumentParser()
613 parser.add_argument('--outdir', help='Where to write the files.',
614 required=True)
615 parser.add_argument('--xml',
616 help='Vulkan API XML file.',
617 required=True,
618 action='append',
619 dest='xml_files')
620 args = parser.parse_args()
621
622 entrypoints = []
623
624 for filename in args.xml_files:
625 doc = et.parse(filename)
626 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
627 start_index=len(entrypoints))
628
629 # Manually add CreateDmaBufImageINTEL for which we don't have an extension
630 # defined.
631 entrypoints.append(Entrypoint('vkCreateDmaBufImageINTEL', 'VkResult', [
632 EntrypointParam('VkDevice', 'device', 'VkDevice device'),
633 EntrypointParam('VkDmaBufImageCreateInfo', 'pCreateInfo',
634 'const VkDmaBufImageCreateInfo* pCreateInfo'),
635 EntrypointParam('VkAllocationCallbacks', 'pAllocator',
636 'const VkAllocationCallbacks* pAllocator'),
637 EntrypointParam('VkDeviceMemory', 'pMem', 'VkDeviceMemory* pMem'),
638 EntrypointParam('VkImage', 'pImage', 'VkImage* pImage')
639 ]))
640
641 device_entrypoints = []
642 instance_entrypoints = []
643 for e in entrypoints:
644 if e.is_device_entrypoint():
645 device_entrypoints.append(e)
646 else:
647 instance_entrypoints.append(e)
648
649 device_strmap = StringIntMap()
650 for num, e in enumerate(device_entrypoints):
651 device_strmap.add_string(e.name, num)
652 e.num = num
653 device_strmap.bake()
654
655 instance_strmap = StringIntMap()
656 for num, e in enumerate(instance_entrypoints):
657 instance_strmap.add_string(e.name, num)
658 e.num = num
659 instance_strmap.bake()
660
661 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
662 # per entry point.
663 try:
664 with open(os.path.join(args.outdir, 'anv_entrypoints.h'), 'wb') as f:
665 f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints,
666 device_entrypoints=device_entrypoints,
667 LAYERS=LAYERS,
668 filename=os.path.basename(__file__)))
669 with open(os.path.join(args.outdir, 'anv_entrypoints.c'), 'wb') as f:
670 f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints,
671 device_entrypoints=device_entrypoints,
672 LAYERS=LAYERS,
673 instance_strmap=instance_strmap,
674 device_strmap=device_strmap,
675 filename=os.path.basename(__file__)))
676 except Exception:
677 # In the even there's an error this imports some helpers from mako
678 # to print a useful stack trace and prints it, then exits with
679 # status 1, if python is run with debug; otherwise it just raises
680 # the exception
681 if __debug__:
682 import sys
683 from mako import exceptions
684 sys.stderr.write(exceptions.text_error_template().render() + '\n')
685 sys.exit(1)
686 raise
687
688
689 if __name__ == '__main__':
690 main()