anv: Only advertise enabled entrypoints
[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
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.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.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 class Entrypoint(object):
305 def __init__(self, name, return_type, params, guard = None):
306 self.name = name
307 self.return_type = return_type
308 self.params = ', '.join(params)
309 self.guard = guard
310 self.enabled = False
311 self.num = None
312 # Extensions which require this entrypoint
313 self.core_version = None
314 self.extension = None
315
316 def prefixed_name(self, prefix):
317 assert self.name.startswith('vk')
318 return prefix + '_' + self.name[2:]
319
320 def get_c_hash(self):
321 return cal_hash(self.name)
322
323 def get_entrypoints(doc, entrypoints_to_defines, start_index):
324 """Extract the entry points from the registry."""
325 entrypoints = OrderedDict()
326
327 for command in doc.findall('./commands/command'):
328 ret_type = command.find('./proto/type').text
329 fullname = command.find('./proto/name').text
330 params = (''.join(p.itertext()) for p in command.findall('./param'))
331 guard = entrypoints_to_defines.get(fullname)
332 # They really need to be unique
333 assert fullname not in entrypoints
334 entrypoints[fullname] = Entrypoint(fullname, ret_type, params, guard)
335
336 enabled_commands = set()
337 for feature in doc.findall('./feature'):
338 assert feature.attrib['api'] == 'vulkan'
339 version = VkVersion(feature.attrib['number'])
340 if version > MAX_API_VERSION:
341 continue
342
343 for command in feature.findall('./require/command'):
344 e = entrypoints[command.attrib['name']]
345 e.enabled = True
346 assert e.core_version is None
347 e.core_version = version
348
349 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS)
350 for extension in doc.findall('.extensions/extension'):
351 ext_name = extension.attrib['name']
352 if ext_name not in supported_exts:
353 continue
354
355 if extension.attrib['supported'] != 'vulkan':
356 continue
357
358 ext = supported_exts[ext_name]
359 ext.type = extension.attrib['type']
360
361 for command in extension.findall('./require/command'):
362 e = entrypoints[command.attrib['name']]
363 e.enabled = True
364 assert e.core_version is None
365 assert e.extension is None
366 e.extension = ext
367
368 return [e for e in entrypoints.itervalues() if e.enabled]
369
370
371 def get_entrypoints_defines(doc):
372 """Maps entry points to extension defines."""
373 entrypoints_to_defines = {}
374
375 for extension in doc.findall('./extensions/extension[@protect]'):
376 define = extension.attrib['protect']
377
378 for entrypoint in extension.findall('./require/command'):
379 fullname = entrypoint.attrib['name']
380 entrypoints_to_defines[fullname] = define
381
382 return entrypoints_to_defines
383
384
385 def gen_code(entrypoints):
386 """Generate the C code."""
387 i = 0
388 offsets = []
389 for e in entrypoints:
390 offsets.append(i)
391 i += len(e.name) + 1
392
393 mapping = [NONE] * HASH_SIZE
394 collisions = [0] * 10
395 for e in entrypoints:
396 level = 0
397 h = e.get_c_hash()
398 while mapping[h & HASH_MASK] != NONE:
399 h = h + PRIME_STEP
400 level = level + 1
401 if level > 9:
402 collisions[9] += 1
403 else:
404 collisions[level] += 1
405 mapping[h & HASH_MASK] = e.num
406
407 return TEMPLATE_C.render(entrypoints=entrypoints,
408 LAYERS=LAYERS,
409 offsets=offsets,
410 collisions=collisions,
411 mapping=mapping,
412 hash_mask=HASH_MASK,
413 prime_step=PRIME_STEP,
414 prime_factor=PRIME_FACTOR,
415 none=NONE,
416 hash_size=HASH_SIZE,
417 filename=os.path.basename(__file__))
418
419
420 def main():
421 parser = argparse.ArgumentParser()
422 parser.add_argument('--outdir', help='Where to write the files.',
423 required=True)
424 parser.add_argument('--xml',
425 help='Vulkan API XML file.',
426 required=True,
427 action='append',
428 dest='xml_files')
429 args = parser.parse_args()
430
431 entrypoints = []
432
433 for filename in args.xml_files:
434 doc = et.parse(filename)
435 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc),
436 start_index=len(entrypoints))
437
438 # Manually add CreateDmaBufImageINTEL for which we don't have an extension
439 # defined.
440 entrypoints.append(Entrypoint('vkCreateDmaBufImageINTEL', 'VkResult',
441 ['VkDevice device',
442 'const VkDmaBufImageCreateInfo* pCreateInfo',
443 'const VkAllocationCallbacks* pAllocator',
444 'VkDeviceMemory* pMem',
445 'VkImage* pImage']))
446
447 for num, e in enumerate(entrypoints):
448 e.num = num
449
450 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
451 # per entry point.
452 try:
453 with open(os.path.join(args.outdir, 'anv_entrypoints.h'), 'wb') as f:
454 f.write(TEMPLATE_H.render(entrypoints=entrypoints,
455 LAYERS=LAYERS,
456 filename=os.path.basename(__file__)))
457 with open(os.path.join(args.outdir, 'anv_entrypoints.c'), 'wb') as f:
458 f.write(gen_code(entrypoints))
459 except Exception:
460 # In the even there's an error this imports some helpers from mako
461 # to print a useful stack trace and prints it, then exits with
462 # status 1, if python is run with debug; otherwise it just raises
463 # the exception
464 if __debug__:
465 import sys
466 from mako import exceptions
467 sys.stderr.write(exceptions.text_error_template().render() + '\n')
468 sys.exit(1)
469 raise
470
471
472 if __name__ == '__main__':
473 main()