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