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