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