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