radv: gather info about PS inputs in the shader info pass
[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 bool
231 radv_entrypoint_is_enabled_physical_device(int index, uint32_t core_version,
232 const struct radv_instance_extension_table *instance)
233 {
234 switch (index) {
235 % for e in entrypoints:
236 %if e.physical_device_command:
237 case ${e.num}:
238 % if e.core_version:
239 return instance && ${e.core_version.c_vk_version()} <= core_version;
240 % elif e.extensions:
241 % for ext in e.extensions:
242 % if ext.type == 'instance':
243 if (instance && instance->${ext.name[3:]}) return true;
244 % else:
245 return true;
246 % endif
247 %endfor
248 return false;
249 % else:
250 return instance;
251 % endif
252 %endif
253 % endfor
254 default:
255 return false;
256 }
257 }
258
259 static int
260 radv_lookup_entrypoint(const char *name)
261 {
262 static const uint32_t prime_factor = ${strmap.prime_factor};
263 static const uint32_t prime_step = ${strmap.prime_step};
264 const struct string_map_entry *e;
265 uint32_t hash, h;
266 uint16_t i;
267 const char *p;
268
269 hash = 0;
270 for (p = name; *p; p++)
271 hash = hash * prime_factor + *p;
272
273 h = hash;
274 while (1) {
275 i = string_map[h & ${strmap.hash_mask}];
276 if (i == none)
277 return -1;
278 e = &string_map_entries[i];
279 if (e->hash == hash && strcmp(name, strings + e->name) == 0)
280 return e->num;
281 h += prime_step;
282 }
283
284 return -1;
285 }
286
287 void *
288 radv_lookup_entrypoint_unchecked(const char *name)
289 {
290 int index = radv_lookup_entrypoint(name);
291 if (index < 0)
292 return NULL;
293 return radv_resolve_entrypoint(index);
294 }
295
296 void *
297 radv_lookup_entrypoint_checked(const char *name,
298 uint32_t core_version,
299 const struct radv_instance_extension_table *instance,
300 const struct radv_device_extension_table *device)
301 {
302 int index = radv_lookup_entrypoint(name);
303 if (index < 0 || !radv_entrypoint_is_enabled(index, core_version, instance, device))
304 return NULL;
305 return radv_resolve_entrypoint(index);
306 }
307
308 void *
309 radv_lookup_physical_device_entrypoint_checked(const char *name,
310 uint32_t core_version,
311 const struct radv_instance_extension_table *instance)
312 {
313 int index = radv_lookup_entrypoint(name);
314 if (index < 0 || !radv_entrypoint_is_enabled_physical_device(index, core_version, instance))
315 return NULL;
316 return radv_resolve_entrypoint(index);
317 }
318
319 """, output_encoding='utf-8')
320
321 U32_MASK = 2**32 - 1
322
323 PRIME_FACTOR = 5024183
324 PRIME_STEP = 19
325
326 def round_to_pow2(x):
327 return 2**int(math.ceil(math.log(x, 2)))
328
329 class StringIntMapEntry(object):
330 def __init__(self, string, num):
331 self.string = string
332 self.num = num
333
334 # Calculate the same hash value that we will calculate in C.
335 h = 0
336 for c in string:
337 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
338 self.hash = h
339
340 self.offset = None
341
342 class StringIntMap(object):
343 def __init__(self):
344 self.baked = False
345 self.strings = dict()
346
347 def add_string(self, string, num):
348 assert not self.baked
349 assert string not in self.strings
350 assert num >= 0 and num < 2**31
351 self.strings[string] = StringIntMapEntry(string, num)
352
353 def bake(self):
354 self.sorted_strings = \
355 sorted(self.strings.values(), key=lambda x: x.string)
356 offset = 0
357 for entry in self.sorted_strings:
358 entry.offset = offset
359 offset += len(entry.string) + 1
360
361 # Save off some values that we'll need in C
362 self.hash_size = round_to_pow2(len(self.strings) * 1.25)
363 self.hash_mask = self.hash_size - 1
364 self.prime_factor = PRIME_FACTOR
365 self.prime_step = PRIME_STEP
366
367 self.mapping = [-1] * self.hash_size
368 self.collisions = [0] * 10
369 for idx, s in enumerate(self.sorted_strings):
370 level = 0
371 h = s.hash
372 while self.mapping[h & self.hash_mask] >= 0:
373 h = h + PRIME_STEP
374 level = level + 1
375 self.collisions[min(level, 9)] += 1
376 self.mapping[h & self.hash_mask] = idx
377
378 EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
379
380 class EntrypointBase(object):
381 def __init__(self, name):
382 self.name = name
383 self.alias = None
384 self.guard = None
385 self.enabled = False
386 self.num = None
387 # Extensions which require this entrypoint
388 self.core_version = None
389 self.extensions = []
390
391 class Entrypoint(EntrypointBase):
392 def __init__(self, name, return_type, params, guard = None):
393 super(Entrypoint, self).__init__(name)
394 self.return_type = return_type
395 self.params = params
396 self.guard = guard
397 self.device_command = len(params) > 0 and (params[0].type == 'VkDevice' or params[0].type == 'VkQueue' or params[0].type == 'VkCommandBuffer')
398 self.physical_device_command = len(params) > 0 and params[0].type == 'VkPhysicalDevice'
399
400 def prefixed_name(self, prefix):
401 assert self.name.startswith('vk')
402 return prefix + '_' + self.name[2:]
403
404 def decl_params(self):
405 return ', '.join(p.decl for p in self.params)
406
407 def call_params(self):
408 return ', '.join(p.name for p in self.params)
409
410 class EntrypointAlias(EntrypointBase):
411 def __init__(self, name, entrypoint):
412 super(EntrypointAlias, self).__init__(name)
413 self.alias = entrypoint
414 self.device_command = entrypoint.device_command
415 self.physical_device_command = entrypoint.physical_device_command
416
417 def prefixed_name(self, prefix):
418 return self.alias.prefixed_name(prefix)
419
420 def get_entrypoints(doc, entrypoints_to_defines, start_index):
421 """Extract the entry points from the registry."""
422 entrypoints = OrderedDict()
423
424 for command in doc.findall('./commands/command'):
425 if 'alias' in command.attrib:
426 alias = command.attrib['name']
427 target = command.attrib['alias']
428 entrypoints[alias] = EntrypointAlias(alias, entrypoints[target])
429 else:
430 name = command.find('./proto/name').text
431 ret_type = command.find('./proto/type').text
432 params = [EntrypointParam(
433 type = p.find('./type').text,
434 name = p.find('./name').text,
435 decl = ''.join(p.itertext())
436 ) for p in command.findall('./param')]
437 guard = entrypoints_to_defines.get(name)
438 # They really need to be unique
439 assert name not in entrypoints
440 entrypoints[name] = Entrypoint(name, ret_type, params, guard)
441
442 for feature in doc.findall('./feature'):
443 assert feature.attrib['api'] == 'vulkan'
444 version = VkVersion(feature.attrib['number'])
445 if version > MAX_API_VERSION:
446 continue
447
448 for command in feature.findall('./require/command'):
449 e = entrypoints[command.attrib['name']]
450 e.enabled = True
451 assert e.core_version is None
452 e.core_version = version
453
454 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
455 for extension in doc.findall('.extensions/extension'):
456 ext_name = extension.attrib['name']
457 if ext_name not in supported_exts:
458 continue
459
460 ext = supported_exts[ext_name]
461 ext.type = extension.attrib['type']
462
463 for command in extension.findall('./require/command'):
464 e = entrypoints[command.attrib['name']]
465 e.enabled = True
466 assert e.core_version is None
467 e.extensions.append(ext)
468
469 # if the base command is not supported by the driver yet, don't alias aliases
470 for e in entrypoints.values():
471 if e.alias and not e.alias.enabled:
472 e_clone = copy.deepcopy(e.alias)
473 e_clone.enabled = True
474 e_clone.name = e.name
475 entrypoints[e.name] = e_clone
476
477 return [e for e in entrypoints.values() if e.enabled]
478
479
480 def get_entrypoints_defines(doc):
481 """Maps entry points to extension defines."""
482 entrypoints_to_defines = {}
483
484 for extension in doc.findall('./extensions/extension[@protect]'):
485 define = extension.attrib['protect']
486
487 for entrypoint in extension.findall('./require/command'):
488 fullname = entrypoint.attrib['name']
489 entrypoints_to_defines[fullname] = define
490
491 platform_define = {}
492 for platform in doc.findall('./platforms/platform'):
493 name = platform.attrib['name']
494 define = platform.attrib['protect']
495 platform_define[name] = define
496
497 for extension in doc.findall('./extensions/extension[@platform]'):
498 platform = extension.attrib['platform']
499 define = platform_define[platform]
500
501 for entrypoint in extension.findall('./require/command'):
502 fullname = entrypoint.attrib['name']
503 entrypoints_to_defines[fullname] = define
504
505 return entrypoints_to_defines
506
507
508 def gen_code(entrypoints):
509 """Generate the C code."""
510 strmap = StringIntMap()
511 for e in entrypoints:
512 strmap.add_string(e.name, e.num)
513 strmap.bake()
514
515 return TEMPLATE_C.render(entrypoints=entrypoints,
516 LAYERS=LAYERS,
517 strmap=strmap,
518 filename=os.path.basename(__file__))
519
520
521 def main():
522 parser = argparse.ArgumentParser()
523 parser.add_argument('--outdir', help='Where to write the files.',
524 required=True)
525 parser.add_argument('--xml',
526 help='Vulkan API XML file.',
527 required=True,
528 action='append',
529 dest='xml_files')
530 args = parser.parse_args()
531
532 entrypoints = []
533
534 for filename in args.xml_files:
535 doc = et.parse(filename)
536 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
537 start_index=len(entrypoints))
538
539 for num, e in enumerate(entrypoints):
540 e.num = num
541
542 # For outputting entrypoints.h we generate a radv_EntryPoint() prototype
543 # per entry point.
544 with open(os.path.join(args.outdir, 'radv_entrypoints.h'), 'wb') as f:
545 f.write(TEMPLATE_H.render(entrypoints=entrypoints,
546 LAYERS=LAYERS,
547 filename=os.path.basename(__file__)))
548 with open(os.path.join(args.outdir, 'radv_entrypoints.c'), 'wb') as f:
549 f.write(gen_code(entrypoints))
550
551
552 if __name__ == '__main__':
553 main()