anv: Update "do not edit" comments with proper filename
[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 os
27 import textwrap
28 import xml.etree.ElementTree as et
29
30 MAX_API_VERSION = 1.0
31
32 SUPPORTED_EXTENSIONS = [
33 'VK_KHR_descriptor_update_template',
34 'VK_KHR_get_physical_device_properties2',
35 'VK_KHR_maintenance1',
36 'VK_KHR_push_descriptor',
37 'VK_KHR_sampler_mirror_clamp_to_edge',
38 'VK_KHR_shader_draw_parameters',
39 'VK_KHR_surface',
40 'VK_KHR_swapchain',
41 'VK_KHR_wayland_surface',
42 'VK_KHR_xcb_surface',
43 'VK_KHR_xlib_surface',
44 ]
45
46 # We generate a static hash table for entry point lookup
47 # (vkGetProcAddress). We use a linear congruential generator for our hash
48 # function and a power-of-two size table. The prime numbers are determined
49 # experimentally.
50
51 NONE = 0xffff
52 HASH_SIZE = 256
53 U32_MASK = 2**32 - 1
54 HASH_MASK = HASH_SIZE - 1
55
56 PRIME_FACTOR = 5024183
57 PRIME_STEP = 19
58
59 opt_header = False
60 opt_code = False
61
62
63 def hash(name):
64 h = 0
65 for c in name:
66 h = (h * PRIME_FACTOR + ord(c)) & U32_MASK
67
68 return h
69
70
71 def print_guard_start(guard):
72 if guard is not None:
73 print "#ifdef {0}".format(guard)
74
75
76 def print_guard_end(guard):
77 if guard is not None:
78 print "#endif // {0}".format(guard)
79
80
81 def get_entrypoints(doc, entrypoints_to_defines):
82 """Extract the entry points from the registry."""
83 entrypoints = []
84
85 enabled_commands = set()
86 for feature in doc.findall('./feature'):
87 assert feature.attrib['api'] == 'vulkan'
88 if float(feature.attrib['number']) > MAX_API_VERSION:
89 continue
90
91 for command in feature.findall('./require/command'):
92 enabled_commands.add(command.attrib['name'])
93
94 for extension in doc.findall('.extensions/extension'):
95 if extension.attrib['name'] not in SUPPORTED_EXTENSIONS:
96 continue
97
98 assert extension.attrib['supported'] == 'vulkan'
99 for command in extension.findall('./require/command'):
100 enabled_commands.add(command.attrib['name'])
101
102 index = 0
103 for command in doc.findall('./commands/command'):
104 type = command.find('./proto/type').text
105 fullname = command.find('./proto/name').text
106
107 if fullname not in enabled_commands:
108 continue
109
110 shortname = fullname[2:]
111 params = (''.join(p.itertext()) for p in command.findall('./param'))
112 params = ', '.join(params)
113 if fullname in entrypoints_to_defines:
114 guard = entrypoints_to_defines[fullname]
115 else:
116 guard = None
117 entrypoints.append((type, shortname, params, index, hash(fullname), guard))
118 index += 1
119
120 return entrypoints
121
122
123 def get_entrypoints_defines(doc):
124 """Maps entry points to extension defines."""
125 entrypoints_to_defines = {}
126 extensions = doc.findall('./extensions/extension')
127 for extension in extensions:
128 define = extension.get('protect')
129 entrypoints = extension.findall('./require/command')
130 for entrypoint in entrypoints:
131 fullname = entrypoint.get('name')
132 entrypoints_to_defines[fullname] = define
133 return entrypoints_to_defines
134
135
136 def gen_header(entrypoints):
137 print "/* This file generated from {}, don't edit directly. */\n".format(
138 os.path.basename(__file__))
139
140 print "struct anv_dispatch_table {"
141 print " union {"
142 print " void *entrypoints[%d];" % len(entrypoints)
143 print " struct {"
144
145 for type, name, args, num, h, guard in entrypoints:
146 if guard is not None:
147 print "#ifdef {0}".format(guard)
148 print " PFN_vk{0} {0};".format(name)
149 print "#else"
150 print " void *{0};".format(name)
151 print "#endif"
152 else:
153 print " PFN_vk{0} {0};".format(name)
154 print " };\n"
155 print " };\n"
156 print "};\n"
157
158 print "void anv_set_dispatch_devinfo(const struct gen_device_info *info);\n"
159
160 for type, name, args, num, h, guard in entrypoints:
161 print_guard_start(guard)
162 print "%s anv_%s(%s);" % (type, name, args)
163 print "%s gen7_%s(%s);" % (type, name, args)
164 print "%s gen75_%s(%s);" % (type, name, args)
165 print "%s gen8_%s(%s);" % (type, name, args)
166 print "%s gen9_%s(%s);" % (type, name, args)
167 print_guard_end(guard)
168
169
170 def gen_code(entrypoints):
171 print textwrap.dedent("""\
172 /*
173 * Copyright © 2015 Intel Corporation
174 *
175 * Permission is hereby granted, free of charge, to any person obtaining a
176 * copy of this software and associated documentation files (the "Software"),
177 * to deal in the Software without restriction, including without limitation
178 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
179 * and/or sell copies of the Software, and to permit persons to whom the
180 * Software is furnished to do so, subject to the following conditions:
181 *
182 * The above copyright notice and this permission notice (including the next
183 * paragraph) shall be included in all copies or substantial portions of the
184 * Software.
185 *
186 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
189 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
190 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
191 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
192 * IN THE SOFTWARE.
193 */
194
195 /* This file generated from {}, don't edit directly. */
196
197 #include "anv_private.h"
198
199 struct anv_entrypoint {{
200 uint32_t name;
201 uint32_t hash;
202 }};
203
204 /* We use a big string constant to avoid lots of reloctions from the entry
205 * point table to lots of little strings. The entries in the entry point table
206 * store the index into this big string.
207 */
208
209 static const char strings[] =""".format(os.path.basename(__file__)))
210
211 offsets = []
212 i = 0
213 for type, name, args, num, h, guard in entrypoints:
214 print " \"vk%s\\0\"" % name
215 offsets.append(i)
216 i += 2 + len(name) + 1
217 print " ;"
218
219 # Now generate the table of all entry points
220
221 print "\nstatic const struct anv_entrypoint entrypoints[] = {"
222 for type, name, args, num, h, guard in entrypoints:
223 print " { %5d, 0x%08x }," % (offsets[num], h)
224 print "};\n"
225
226 print textwrap.dedent("""
227
228 /* Weak aliases for all potential implementations. These will resolve to
229 * NULL if they're not defined, which lets the resolve_entrypoint() function
230 * either pick the correct entry point.
231 */
232 """)
233
234 for layer in ["anv", "gen7", "gen75", "gen8", "gen9"]:
235 for type, name, args, num, h, guard in entrypoints:
236 print_guard_start(guard)
237 print "%s %s_%s(%s) __attribute__ ((weak));" % (type, layer, name, args)
238 print_guard_end(guard)
239 print "\nconst struct anv_dispatch_table %s_layer = {" % layer
240 for type, name, args, num, h, guard in entrypoints:
241 print_guard_start(guard)
242 print " .%s = %s_%s," % (name, layer, name)
243 print_guard_end(guard)
244 print "};\n"
245
246 print textwrap.dedent("""
247 static void * __attribute__ ((noinline))
248 anv_resolve_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
249 {
250 if (devinfo == NULL) {
251 return anv_layer.entrypoints[index];
252 }
253
254 switch (devinfo->gen) {
255 case 9:
256 if (gen9_layer.entrypoints[index])
257 return gen9_layer.entrypoints[index];
258 /* fall through */
259 case 8:
260 if (gen8_layer.entrypoints[index])
261 return gen8_layer.entrypoints[index];
262 /* fall through */
263 case 7:
264 if (devinfo->is_haswell && gen75_layer.entrypoints[index])
265 return gen75_layer.entrypoints[index];
266
267 if (gen7_layer.entrypoints[index])
268 return gen7_layer.entrypoints[index];
269 /* fall through */
270 case 0:
271 return anv_layer.entrypoints[index];
272 default:
273 unreachable("unsupported gen\\n");
274 }
275 }
276 """)
277
278 # Now generate the hash table used for entry point look up. This is a
279 # uint16_t table of entry point indices. We use 0xffff to indicate an entry
280 # in the hash table is empty.
281
282 map = [NONE] * HASH_SIZE
283 collisions = [0] * 10
284 for type, name, args, num, h, guard in entrypoints:
285 level = 0
286 while map[h & HASH_MASK] != NONE:
287 h = h + PRIME_STEP
288 level = level + 1
289 if level > 9:
290 collisions[9] += 1
291 else:
292 collisions[level] += 1
293 map[h & HASH_MASK] = num
294
295 print "/* Hash table stats:"
296 print " * size %d entries" % HASH_SIZE
297 print " * collisions entries"
298 for i in xrange(10):
299 if i == 9:
300 plus = "+"
301 else:
302 plus = " "
303
304 print " * %2d%s %4d" % (i, plus, collisions[i])
305 print " */\n"
306
307 print "#define none 0x%04x\n" % NONE
308
309 print "static const uint16_t map[] = {"
310 for i in xrange(0, HASH_SIZE, 8):
311 print " ",
312 for j in xrange(i, i + 8):
313 if map[j] & 0xffff == 0xffff:
314 print " none,",
315 else:
316 print "0x%04x," % (map[j] & 0xffff),
317 print
318
319 print "};"
320
321 # Finally we generate the hash table lookup function. The hash function and
322 # linear probing algorithm matches the hash table generated above.
323
324 print textwrap.dedent("""
325 void *
326 anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)
327 {
328 static const uint32_t prime_factor = %d;
329 static const uint32_t prime_step = %d;
330 const struct anv_entrypoint *e;
331 uint32_t hash, h, i;
332 const char *p;
333
334 hash = 0;
335 for (p = name; *p; p++)
336 hash = hash * prime_factor + *p;
337
338 h = hash;
339 do {
340 i = map[h & %d];
341 if (i == none)
342 return NULL;
343 e = &entrypoints[i];
344 h += prime_step;
345 } while (e->hash != hash);
346
347 if (strcmp(name, strings + e->name) != 0)
348 return NULL;
349
350 return anv_resolve_entrypoint(devinfo, i);
351 }
352 """) % (PRIME_FACTOR, PRIME_STEP, HASH_MASK)
353
354
355 def main():
356 parser = argparse.ArgumentParser()
357 parser.add_argument('target', choices=['header', 'code'],
358 help='Which file to generate.')
359 parser.add_argument('--xml', help='Vulkan API XML file.')
360 args = parser.parse_args()
361
362 doc = et.parse(args.xml)
363 entrypoints = get_entrypoints(doc, get_entrypoints_defines(doc))
364
365 # Manually add CreateDmaBufImageINTEL for which we don't have an extension
366 # defined.
367 entrypoints.append(('VkResult', 'CreateDmaBufImageINTEL',
368 'VkDevice device, ' +
369 'const VkDmaBufImageCreateInfo* pCreateInfo, ' +
370 'const VkAllocationCallbacks* pAllocator,' +
371 'VkDeviceMemory* pMem,' +
372 'VkImage* pImage', len(entrypoints),
373 hash('vkCreateDmaBufImageINTEL'), None))
374
375 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
376 # per entry point.
377 if args.target == 'header':
378 gen_header(entrypoints)
379 else:
380 gen_code(entrypoints)
381
382
383 if __name__ == '__main__':
384 main()