anv/entrypoints_gen: Allow the string map to grow
[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 e.params[0].type not in ('VkDevice', 'VkCommandBuffer'):
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 e.params[0].type not in ('VkDevice', 'VkCommandBuffer'):
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 % if e.core_version:
270 return ${e.core_version.c_vk_version()} <= core_version;
271 % elif e.extension:
272 % if e.extension.type == 'instance':
273 return !device && instance->${e.extension.name[3:]};
274 % else:
275 return !device || device->${e.extension.name[3:]};
276 % endif
277 % else:
278 return true;
279 % endif
280 % endfor
281 default:
282 return false;
283 }
284 }
285
286 static void * __attribute__ ((noinline))
287 anv_resolve_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
288 {
289 if (devinfo == NULL) {
290 return anv_dispatch_table.entrypoints[index];
291 }
292
293 const struct anv_dispatch_table *genX_table;
294 switch (devinfo->gen) {
295 case 11:
296 genX_table = &gen11_dispatch_table;
297 break;
298 case 10:
299 genX_table = &gen10_dispatch_table;
300 break;
301 case 9:
302 genX_table = &gen9_dispatch_table;
303 break;
304 case 8:
305 genX_table = &gen8_dispatch_table;
306 break;
307 case 7:
308 if (devinfo->is_haswell)
309 genX_table = &gen75_dispatch_table;
310 else
311 genX_table = &gen7_dispatch_table;
312 break;
313 default:
314 unreachable("unsupported gen\\n");
315 }
316
317 if (genX_table->entrypoints[index])
318 return genX_table->entrypoints[index];
319 else
320 return anv_dispatch_table.entrypoints[index];
321 }
322
323 int
324 anv_get_entrypoint_index(const char *name)
325 {
326 return string_map_lookup(name);
327 }
328
329 void *
330 anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)
331 {
332 int idx = anv_get_entrypoint_index(name);
333 if (idx < 0)
334 return NULL;
335 return anv_resolve_entrypoint(devinfo, idx);
336 }""", output_encoding='utf-8')
337
338 U32_MASK = 2**32 - 1
339
340 PRIME_FACTOR = 5024183
341 PRIME_STEP = 19
342
343 class StringIntMapEntry(object):
344 def __init__(self, string, num):
345 self.string = string
346 self.num = num
347
348 # Calculate the same hash value that we will calculate in C.
349 h = 0
350 for c in string:
351 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
352 self.hash = h
353
354 self.offset = None
355
356 def round_to_pow2(x):
357 return 2**int(math.ceil(math.log(x, 2)))
358
359 class StringIntMap(object):
360 def __init__(self):
361 self.baked = False
362 self.strings = dict()
363
364 def add_string(self, string, num):
365 assert not self.baked
366 assert string not in self.strings
367 assert num >= 0 and num < 2**31
368 self.strings[string] = StringIntMapEntry(string, num)
369
370 def bake(self):
371 self.sorted_strings = \
372 sorted(self.strings.values(), key=lambda x: x.string)
373 offset = 0
374 for entry in self.sorted_strings:
375 entry.offset = offset
376 offset += len(entry.string) + 1
377
378 # Save off some values that we'll need in C
379 self.hash_size = round_to_pow2(len(self.strings) * 1.25)
380 self.hash_mask = self.hash_size - 1
381 self.prime_factor = PRIME_FACTOR
382 self.prime_step = PRIME_STEP
383
384 self.mapping = [-1] * self.hash_size
385 self.collisions = [0] * 10
386 for idx, s in enumerate(self.sorted_strings):
387 level = 0
388 h = s.hash
389 while self.mapping[h & self.hash_mask] >= 0:
390 h = h + PRIME_STEP
391 level = level + 1
392 self.collisions[min(level, 9)] += 1
393 self.mapping[h & self.hash_mask] = idx
394
395 EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
396
397 class Entrypoint(object):
398 def __init__(self, name, return_type, params, guard = None):
399 self.name = name
400 self.return_type = return_type
401 self.params = params
402 self.guard = guard
403 self.enabled = False
404 self.num = None
405 # Extensions which require this entrypoint
406 self.core_version = None
407 self.extension = None
408
409 def prefixed_name(self, prefix):
410 assert self.name.startswith('vk')
411 return prefix + '_' + self.name[2:]
412
413 def decl_params(self):
414 return ', '.join(p.decl for p in self.params)
415
416 def call_params(self):
417 return ', '.join(p.name for p in self.params)
418
419 def get_entrypoints(doc, entrypoints_to_defines, start_index):
420 """Extract the entry points from the registry."""
421 entrypoints = OrderedDict()
422
423 for command in doc.findall('./commands/command'):
424 ret_type = command.find('./proto/type').text
425 fullname = command.find('./proto/name').text
426 params = [EntrypointParam(
427 type = p.find('./type').text,
428 name = p.find('./name').text,
429 decl = ''.join(p.itertext())
430 ) for p in command.findall('./param')]
431 guard = entrypoints_to_defines.get(fullname)
432 # They really need to be unique
433 assert fullname not in entrypoints
434 entrypoints[fullname] = Entrypoint(fullname, ret_type, params, guard)
435
436 enabled_commands = set()
437 for feature in doc.findall('./feature'):
438 assert feature.attrib['api'] == 'vulkan'
439 version = VkVersion(feature.attrib['number'])
440 if version > MAX_API_VERSION:
441 continue
442
443 for command in feature.findall('./require/command'):
444 e = entrypoints[command.attrib['name']]
445 e.enabled = True
446 assert e.core_version is None
447 e.core_version = version
448
449 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
450 for extension in doc.findall('.extensions/extension'):
451 ext_name = extension.attrib['name']
452 if ext_name not in supported_exts:
453 continue
454
455 if extension.attrib['supported'] != 'vulkan':
456 continue
457
458 ext = supported_exts[ext_name]
459 ext.type = extension.attrib['type']
460
461 for command in extension.findall('./require/command'):
462 e = entrypoints[command.attrib['name']]
463 e.enabled = True
464 assert e.core_version is None
465 assert e.extension is None
466 e.extension = ext
467
468 return [e for e in entrypoints.itervalues() if e.enabled]
469
470
471 def get_entrypoints_defines(doc):
472 """Maps entry points to extension defines."""
473 entrypoints_to_defines = {}
474
475 for extension in doc.findall('./extensions/extension[@protect]'):
476 define = extension.attrib['protect']
477
478 for entrypoint in extension.findall('./require/command'):
479 fullname = entrypoint.attrib['name']
480 entrypoints_to_defines[fullname] = define
481
482 return entrypoints_to_defines
483
484
485 def main():
486 parser = argparse.ArgumentParser()
487 parser.add_argument('--outdir', help='Where to write the files.',
488 required=True)
489 parser.add_argument('--xml',
490 help='Vulkan API XML file.',
491 required=True,
492 action='append',
493 dest='xml_files')
494 args = parser.parse_args()
495
496 entrypoints = []
497
498 for filename in args.xml_files:
499 doc = et.parse(filename)
500 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
501 start_index=len(entrypoints))
502
503 # Manually add CreateDmaBufImageINTEL for which we don't have an extension
504 # defined.
505 entrypoints.append(Entrypoint('vkCreateDmaBufImageINTEL', 'VkResult', [
506 EntrypointParam('VkDevice', 'device', 'VkDevice device'),
507 EntrypointParam('VkDmaBufImageCreateInfo', 'pCreateInfo',
508 'const VkDmaBufImageCreateInfo* pCreateInfo'),
509 EntrypointParam('VkAllocationCallbacks', 'pAllocator',
510 'const VkAllocationCallbacks* pAllocator'),
511 EntrypointParam('VkDeviceMemory', 'pMem', 'VkDeviceMemory* pMem'),
512 EntrypointParam('VkImage', 'pImage', 'VkImage* pImage')
513 ]))
514
515 strmap = StringIntMap()
516 for num, e in enumerate(entrypoints):
517 strmap.add_string(e.name, num)
518 e.num = num
519 strmap.bake()
520
521 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
522 # per entry point.
523 try:
524 with open(os.path.join(args.outdir, 'anv_entrypoints.h'), 'wb') as f:
525 f.write(TEMPLATE_H.render(entrypoints=entrypoints,
526 LAYERS=LAYERS,
527 filename=os.path.basename(__file__)))
528 with open(os.path.join(args.outdir, 'anv_entrypoints.c'), 'wb') as f:
529 f.write(TEMPLATE_C.render(entrypoints=entrypoints,
530 LAYERS=LAYERS,
531 strmap=strmap,
532 filename=os.path.basename(__file__)))
533 except Exception:
534 # In the even there's an error this imports some helpers from mako
535 # to print a useful stack trace and prints it, then exits with
536 # status 1, if python is run with debug; otherwise it just raises
537 # the exception
538 if __debug__:
539 import sys
540 from mako import exceptions
541 sys.stderr.write(exceptions.text_error_template().render() + '\n')
542 sys.exit(1)
543 raise
544
545
546 if __name__ == '__main__':
547 main()