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