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