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