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