1COPYRIGHT = """\ 2/* 3 * Copyright 2020 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 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25""" 26 27import argparse 28import math 29import os 30 31from mako.template import Template 32 33# Mesa-local imports must be declared in meson variable 34# '{file_without_suffix}_depend_files'. 35from vk_entrypoints import get_entrypoints_from_xml 36 37# We generate a static hash table for entry point lookup 38# (vkGetProcAddress). We use a linear congruential generator for our hash 39# function and a power-of-two size table. The prime numbers are determined 40# experimentally. 41 42TEMPLATE_H = Template(COPYRIGHT + """\ 43/* This file generated from ${filename}, don't edit directly. */ 44 45#ifndef VK_DISPATCH_TABLE_H 46#define VK_DISPATCH_TABLE_H 47 48#include "vulkan/vulkan.h" 49#include "vulkan/vk_android_native_buffer.h" 50 51#include "vk_extensions.h" 52 53/* Windows api conflict */ 54#ifdef _WIN32 55#include <windows.h> 56#ifdef CreateSemaphore 57#undef CreateSemaphore 58#endif 59#ifdef CreateEvent 60#undef CreateEvent 61#endif 62#endif 63 64#ifdef __cplusplus 65extern "C" { 66#endif 67 68#ifdef _MSC_VER 69VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void); 70#endif 71 72<%def name="dispatch_table(entrypoints)"> 73% for e in entrypoints: 74 % if e.alias: 75 <% continue %> 76 % endif 77 % if e.guard is not None: 78#ifdef ${e.guard} 79 % endif 80 % if e.aliases: 81 union { 82 PFN_vk${e.name} ${e.name}; 83 % for a in e.aliases: 84 PFN_vk${a.name} ${a.name}; 85 % endfor 86 }; 87 % else: 88 PFN_vk${e.name} ${e.name}; 89 % endif 90 % if e.guard is not None: 91#else 92 % if e.aliases: 93 union { 94 PFN_vkVoidFunction ${e.name}; 95 % for a in e.aliases: 96 PFN_vkVoidFunction ${a.name}; 97 % endfor 98 }; 99 % else: 100 PFN_vkVoidFunction ${e.name}; 101 % endif 102#endif 103 % endif 104% endfor 105</%def> 106 107<%def name="entrypoint_table(type, entrypoints)"> 108struct vk_${type}_entrypoint_table { 109% for e in entrypoints: 110 % if e.guard is not None: 111#ifdef ${e.guard} 112 % endif 113 PFN_vk${e.name} ${e.name}; 114 % if e.guard is not None: 115#else 116 PFN_vkVoidFunction ${e.name}; 117# endif 118 % endif 119% endfor 120}; 121</%def> 122 123struct vk_instance_dispatch_table { 124 ${dispatch_table(instance_entrypoints)} 125}; 126 127struct vk_physical_device_dispatch_table { 128 ${dispatch_table(physical_device_entrypoints)} 129}; 130 131struct vk_device_dispatch_table { 132 ${dispatch_table(device_entrypoints)} 133}; 134 135struct vk_dispatch_table { 136 union { 137 struct { 138 struct vk_instance_dispatch_table instance; 139 struct vk_physical_device_dispatch_table physical_device; 140 struct vk_device_dispatch_table device; 141 }; 142 143 struct { 144 ${dispatch_table(instance_entrypoints)} 145 ${dispatch_table(physical_device_entrypoints)} 146 ${dispatch_table(device_entrypoints)} 147 }; 148 }; 149}; 150 151${entrypoint_table('instance', instance_entrypoints)} 152${entrypoint_table('physical_device', physical_device_entrypoints)} 153${entrypoint_table('device', device_entrypoints)} 154 155void 156vk_instance_dispatch_table_load(struct vk_instance_dispatch_table *table, 157 PFN_vkGetInstanceProcAddr gpa, 158 VkInstance instance); 159void 160vk_physical_device_dispatch_table_load(struct vk_physical_device_dispatch_table *table, 161 PFN_vkGetInstanceProcAddr gpa, 162 VkInstance instance); 163void 164vk_device_dispatch_table_load(struct vk_device_dispatch_table *table, 165 PFN_vkGetDeviceProcAddr gpa, 166 VkDevice device); 167 168void vk_instance_dispatch_table_from_entrypoints( 169 struct vk_instance_dispatch_table *dispatch_table, 170 const struct vk_instance_entrypoint_table *entrypoint_table, 171 bool overwrite); 172 173void vk_physical_device_dispatch_table_from_entrypoints( 174 struct vk_physical_device_dispatch_table *dispatch_table, 175 const struct vk_physical_device_entrypoint_table *entrypoint_table, 176 bool overwrite); 177 178void vk_device_dispatch_table_from_entrypoints( 179 struct vk_device_dispatch_table *dispatch_table, 180 const struct vk_device_entrypoint_table *entrypoint_table, 181 bool overwrite); 182 183PFN_vkVoidFunction 184vk_instance_dispatch_table_get(const struct vk_instance_dispatch_table *table, 185 const char *name); 186 187PFN_vkVoidFunction 188vk_physical_device_dispatch_table_get(const struct vk_physical_device_dispatch_table *table, 189 const char *name); 190 191PFN_vkVoidFunction 192vk_device_dispatch_table_get(const struct vk_device_dispatch_table *table, 193 const char *name); 194 195PFN_vkVoidFunction 196vk_instance_dispatch_table_get_if_supported( 197 const struct vk_instance_dispatch_table *table, 198 const char *name, 199 uint32_t core_version, 200 const struct vk_instance_extension_table *instance_exts); 201 202PFN_vkVoidFunction 203vk_physical_device_dispatch_table_get_if_supported( 204 const struct vk_physical_device_dispatch_table *table, 205 const char *name, 206 uint32_t core_version, 207 const struct vk_instance_extension_table *instance_exts); 208 209PFN_vkVoidFunction 210vk_device_dispatch_table_get_if_supported( 211 const struct vk_device_dispatch_table *table, 212 const char *name, 213 uint32_t core_version, 214 const struct vk_instance_extension_table *instance_exts, 215 const struct vk_device_extension_table *device_exts); 216 217#ifdef __cplusplus 218} 219#endif 220 221#endif /* VK_DISPATCH_TABLE_H */ 222""") 223 224TEMPLATE_C = Template(COPYRIGHT + """\ 225/* This file generated from ${filename}, don't edit directly. */ 226 227#include "vk_dispatch_table.h" 228 229#include "util/macros.h" 230#include "string.h" 231 232<%def name="load_dispatch_table(type, VkType, ProcAddr, entrypoints)"> 233void 234vk_${type}_dispatch_table_load(struct vk_${type}_dispatch_table *table, 235 PFN_vk${ProcAddr} gpa, 236 ${VkType} obj) 237{ 238% if type != 'physical_device': 239 table->${ProcAddr} = gpa; 240% endif 241% for e in entrypoints: 242 % if e.alias or e.name == '${ProcAddr}': 243 <% continue %> 244 % endif 245 % if e.guard is not None: 246#ifdef ${e.guard} 247 % endif 248 table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${e.name}"); 249 % for a in e.aliases: 250 if (table->${e.name} == NULL) { 251 table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${a.name}"); 252 } 253 % endfor 254 % if e.guard is not None: 255#endif 256 % endif 257% endfor 258} 259</%def> 260 261${load_dispatch_table('instance', 'VkInstance', 'GetInstanceProcAddr', 262 instance_entrypoints)} 263 264${load_dispatch_table('physical_device', 'VkInstance', 'GetInstanceProcAddr', 265 physical_device_entrypoints)} 266 267${load_dispatch_table('device', 'VkDevice', 'GetDeviceProcAddr', 268 device_entrypoints)} 269 270 271struct string_map_entry { 272 uint32_t name; 273 uint32_t hash; 274 uint32_t num; 275}; 276 277/* We use a big string constant to avoid lots of reloctions from the entry 278 * point table to lots of little strings. The entries in the entry point table 279 * store the index into this big string. 280 */ 281 282<%def name="strmap(strmap, prefix)"> 283static const char ${prefix}_strings[] = 284% for s in strmap.sorted_strings: 285 "${s.string}\\0" 286% endfor 287; 288 289static const struct string_map_entry ${prefix}_string_map_entries[] = { 290% for s in strmap.sorted_strings: 291 { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */ 292% endfor 293}; 294 295/* Hash table stats: 296 * size ${len(strmap.sorted_strings)} entries 297 * collisions entries: 298% for i in range(10): 299 * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]} 300% endfor 301 */ 302 303#define none 0xffff 304static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = { 305% for e in strmap.mapping: 306 ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' }, 307% endfor 308}; 309 310static int 311${prefix}_string_map_lookup(const char *str) 312{ 313 static const uint32_t prime_factor = ${strmap.prime_factor}; 314 static const uint32_t prime_step = ${strmap.prime_step}; 315 const struct string_map_entry *e; 316 uint32_t hash, h; 317 uint16_t i; 318 const char *p; 319 320 hash = 0; 321 for (p = str; *p; p++) 322 hash = hash * prime_factor + *p; 323 324 h = hash; 325 while (1) { 326 i = ${prefix}_string_map[h & ${strmap.hash_mask}]; 327 if (i == none) 328 return -1; 329 e = &${prefix}_string_map_entries[i]; 330 if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0) 331 return e->num; 332 h += prime_step; 333 } 334 335 return -1; 336} 337</%def> 338 339${strmap(instance_strmap, 'instance')} 340${strmap(physical_device_strmap, 'physical_device')} 341${strmap(device_strmap, 'device')} 342 343<% assert len(instance_entrypoints) < 2**8 %> 344static const uint8_t instance_compaction_table[] = { 345% for e in instance_entrypoints: 346 ${e.disp_table_index}, 347% endfor 348}; 349 350<% assert len(physical_device_entrypoints) < 2**8 %> 351static const uint8_t physical_device_compaction_table[] = { 352% for e in physical_device_entrypoints: 353 ${e.disp_table_index}, 354% endfor 355}; 356 357<% assert len(device_entrypoints) < 2**16 %> 358static const uint16_t device_compaction_table[] = { 359% for e in device_entrypoints: 360 ${e.disp_table_index}, 361% endfor 362}; 363 364static bool 365vk_instance_entrypoint_is_enabled(int index, uint32_t core_version, 366 const struct vk_instance_extension_table *instance) 367{ 368 switch (index) { 369% for e in instance_entrypoints: 370 case ${e.entry_table_index}: 371 /* ${e.name} */ 372 % if e.core_version: 373 return ${e.core_version.c_vk_version()} <= core_version; 374 % elif e.extensions: 375 % for ext in e.extensions: 376 % if ext.type == 'instance': 377 if (instance->${ext.name[3:]}) return true; 378 % else: 379 /* All device extensions are considered enabled at the instance level */ 380 return true; 381 % endif 382 % endfor 383 return false; 384 % else: 385 return true; 386 % endif 387% endfor 388 default: 389 return false; 390 } 391} 392 393/** Return true if the core version or extension in which the given entrypoint 394 * is defined is enabled. 395 * 396 * If device is NULL, all device extensions are considered enabled. 397 */ 398static bool 399vk_physical_device_entrypoint_is_enabled(int index, uint32_t core_version, 400 const struct vk_instance_extension_table *instance) 401{ 402 switch (index) { 403% for e in physical_device_entrypoints: 404 case ${e.entry_table_index}: 405 /* ${e.name} */ 406 % if e.core_version: 407 return ${e.core_version.c_vk_version()} <= core_version; 408 % elif e.extensions: 409 % for ext in e.extensions: 410 % if ext.type == 'instance': 411 if (instance->${ext.name[3:]}) return true; 412 % else: 413 /* All device extensions are considered enabled at the instance level */ 414 return true; 415 % endif 416 % endfor 417 return false; 418 % else: 419 return true; 420 % endif 421% endfor 422 default: 423 return false; 424 } 425} 426 427/** Return true if the core version or extension in which the given entrypoint 428 * is defined is enabled. 429 * 430 * If device is NULL, all device extensions are considered enabled. 431 */ 432static bool 433vk_device_entrypoint_is_enabled(int index, uint32_t core_version, 434 const struct vk_instance_extension_table *instance, 435 const struct vk_device_extension_table *device) 436{ 437 switch (index) { 438% for e in device_entrypoints: 439 case ${e.entry_table_index}: 440 /* ${e.name} */ 441 % if e.core_version: 442 return ${e.core_version.c_vk_version()} <= core_version; 443 % elif e.extensions: 444 % for ext in e.extensions: 445 % if ext.type == 'instance': 446 if (instance->${ext.name[3:]}) return true; 447 % else: 448 if (!device || device->${ext.name[3:]}) return true; 449 % endif 450 % endfor 451 return false; 452 % else: 453 return true; 454 % endif 455% endfor 456 default: 457 return false; 458 } 459} 460 461#ifdef _MSC_VER 462VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void) 463{ 464 unreachable(!"Entrypoint not implemented"); 465} 466#endif 467 468<%def name="dispatch_table_from_entrypoints(type)"> 469void vk_${type}_dispatch_table_from_entrypoints( 470 struct vk_${type}_dispatch_table *dispatch_table, 471 const struct vk_${type}_entrypoint_table *entrypoint_table, 472 bool overwrite) 473{ 474 PFN_vkVoidFunction *disp = (PFN_vkVoidFunction *)dispatch_table; 475 PFN_vkVoidFunction *entry = (PFN_vkVoidFunction *)entrypoint_table; 476 477 if (overwrite) { 478 memset(dispatch_table, 0, sizeof(*dispatch_table)); 479 for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) { 480#ifdef _MSC_VER 481 assert(entry[i] != NULL); 482 if (entry[i] == vk_entrypoint_stub) 483#else 484 if (entry[i] == NULL) 485#endif 486 continue; 487 unsigned disp_index = ${type}_compaction_table[i]; 488 assert(disp[disp_index] == NULL); 489 disp[disp_index] = entry[i]; 490 } 491 } else { 492 for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) { 493 unsigned disp_index = ${type}_compaction_table[i]; 494#ifdef _MSC_VER 495 assert(entry[i] != NULL); 496 if (disp[disp_index] == NULL && entry[i] != vk_entrypoint_stub) 497#else 498 if (disp[disp_index] == NULL) 499#endif 500 disp[disp_index] = entry[i]; 501 } 502 } 503} 504</%def> 505 506${dispatch_table_from_entrypoints('instance')} 507${dispatch_table_from_entrypoints('physical_device')} 508${dispatch_table_from_entrypoints('device')} 509 510<%def name="lookup_funcs(type)"> 511static PFN_vkVoidFunction 512vk_${type}_dispatch_table_get_for_entry_index( 513 const struct vk_${type}_dispatch_table *table, int entry_index) 514{ 515 assert(entry_index < ARRAY_SIZE(${type}_compaction_table)); 516 int disp_index = ${type}_compaction_table[entry_index]; 517 return ((PFN_vkVoidFunction *)table)[disp_index]; 518} 519 520PFN_vkVoidFunction 521vk_${type}_dispatch_table_get( 522 const struct vk_${type}_dispatch_table *table, const char *name) 523{ 524 int entry_index = ${type}_string_map_lookup(name); 525 if (entry_index < 0) 526 return NULL; 527 528 return vk_${type}_dispatch_table_get_for_entry_index(table, entry_index); 529} 530</%def> 531 532${lookup_funcs('instance')} 533${lookup_funcs('physical_device')} 534${lookup_funcs('device')} 535 536PFN_vkVoidFunction 537vk_instance_dispatch_table_get_if_supported( 538 const struct vk_instance_dispatch_table *table, 539 const char *name, 540 uint32_t core_version, 541 const struct vk_instance_extension_table *instance_exts) 542{ 543 int entry_index = instance_string_map_lookup(name); 544 if (entry_index < 0) 545 return NULL; 546 547 if (!vk_instance_entrypoint_is_enabled(entry_index, core_version, 548 instance_exts)) 549 return NULL; 550 551 return vk_instance_dispatch_table_get_for_entry_index(table, entry_index); 552} 553 554PFN_vkVoidFunction 555vk_physical_device_dispatch_table_get_if_supported( 556 const struct vk_physical_device_dispatch_table *table, 557 const char *name, 558 uint32_t core_version, 559 const struct vk_instance_extension_table *instance_exts) 560{ 561 int entry_index = physical_device_string_map_lookup(name); 562 if (entry_index < 0) 563 return NULL; 564 565 if (!vk_physical_device_entrypoint_is_enabled(entry_index, core_version, 566 instance_exts)) 567 return NULL; 568 569 return vk_physical_device_dispatch_table_get_for_entry_index(table, entry_index); 570} 571 572PFN_vkVoidFunction 573vk_device_dispatch_table_get_if_supported( 574 const struct vk_device_dispatch_table *table, 575 const char *name, 576 uint32_t core_version, 577 const struct vk_instance_extension_table *instance_exts, 578 const struct vk_device_extension_table *device_exts) 579{ 580 int entry_index = device_string_map_lookup(name); 581 if (entry_index < 0) 582 return NULL; 583 584 if (!vk_device_entrypoint_is_enabled(entry_index, core_version, 585 instance_exts, device_exts)) 586 return NULL; 587 588 return vk_device_dispatch_table_get_for_entry_index(table, entry_index); 589} 590""") 591 592U32_MASK = 2**32 - 1 593 594PRIME_FACTOR = 5024183 595PRIME_STEP = 19 596 597class StringIntMapEntry: 598 def __init__(self, string, num): 599 self.string = string 600 self.num = num 601 602 # Calculate the same hash value that we will calculate in C. 603 h = 0 604 for c in string: 605 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK 606 self.hash = h 607 608 self.offset = None 609 610def round_to_pow2(x): 611 return 2**int(math.ceil(math.log(x, 2))) 612 613class StringIntMap: 614 def __init__(self): 615 self.baked = False 616 self.strings = {} 617 618 def add_string(self, string, num): 619 assert not self.baked 620 assert string not in self.strings 621 assert 0 <= num < 2**31 622 self.strings[string] = StringIntMapEntry(string, num) 623 624 def bake(self): 625 self.sorted_strings = \ 626 sorted(self.strings.values(), key=lambda x: x.string) 627 offset = 0 628 for entry in self.sorted_strings: 629 entry.offset = offset 630 offset += len(entry.string) + 1 631 632 # Save off some values that we'll need in C 633 self.hash_size = round_to_pow2(len(self.strings) * 1.25) 634 self.hash_mask = self.hash_size - 1 635 self.prime_factor = PRIME_FACTOR 636 self.prime_step = PRIME_STEP 637 638 self.mapping = [-1] * self.hash_size 639 self.collisions = [0] * 10 640 for idx, s in enumerate(self.sorted_strings): 641 level = 0 642 h = s.hash 643 while self.mapping[h & self.hash_mask] >= 0: 644 h = h + PRIME_STEP 645 level = level + 1 646 self.collisions[min(level, 9)] += 1 647 self.mapping[h & self.hash_mask] = idx 648 649def main(): 650 parser = argparse.ArgumentParser() 651 parser.add_argument('--out-c', help='Output C file.') 652 parser.add_argument('--out-h', help='Output H file.') 653 parser.add_argument('--xml', 654 help='Vulkan API XML file.', 655 required=True, 656 action='append', 657 dest='xml_files') 658 args = parser.parse_args() 659 660 entrypoints = get_entrypoints_from_xml(args.xml_files) 661 662 device_entrypoints = [] 663 physical_device_entrypoints = [] 664 instance_entrypoints = [] 665 for e in entrypoints: 666 if e.is_device_entrypoint(): 667 device_entrypoints.append(e) 668 elif e.is_physical_device_entrypoint(): 669 physical_device_entrypoints.append(e) 670 else: 671 instance_entrypoints.append(e) 672 673 for i, e in enumerate(e for e in device_entrypoints if not e.alias): 674 e.disp_table_index = i 675 676 device_strmap = StringIntMap() 677 for i, e in enumerate(device_entrypoints): 678 e.entry_table_index = i 679 device_strmap.add_string("vk" + e.name, e.entry_table_index) 680 device_strmap.bake() 681 682 for i, e in enumerate(e for e in physical_device_entrypoints if not e.alias): 683 e.disp_table_index = i 684 685 physical_device_strmap = StringIntMap() 686 for i, e in enumerate(physical_device_entrypoints): 687 e.entry_table_index = i 688 physical_device_strmap.add_string("vk" + e.name, e.entry_table_index) 689 physical_device_strmap.bake() 690 691 for i, e in enumerate(e for e in instance_entrypoints if not e.alias): 692 e.disp_table_index = i 693 694 instance_strmap = StringIntMap() 695 for i, e in enumerate(instance_entrypoints): 696 e.entry_table_index = i 697 instance_strmap.add_string("vk" + e.name, e.entry_table_index) 698 instance_strmap.bake() 699 700 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype 701 # per entry point. 702 try: 703 if args.out_h: 704 with open(args.out_h, 'w') as f: 705 f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints, 706 physical_device_entrypoints=physical_device_entrypoints, 707 device_entrypoints=device_entrypoints, 708 filename=os.path.basename(__file__))) 709 if args.out_c: 710 with open(args.out_c, 'w') as f: 711 f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints, 712 physical_device_entrypoints=physical_device_entrypoints, 713 device_entrypoints=device_entrypoints, 714 instance_strmap=instance_strmap, 715 physical_device_strmap=physical_device_strmap, 716 device_strmap=device_strmap, 717 filename=os.path.basename(__file__))) 718 except Exception: 719 # In the event there's an error, this imports some helpers from mako 720 # to print a useful stack trace and prints it, then exits with 721 # status 1, if python is run with debug; otherwise it just raises 722 # the exception 723 import sys 724 from mako import exceptions 725 print(exceptions.text_error_template().render(), file=sys.stderr) 726 sys.exit(1) 727 728 729if __name__ == '__main__': 730 main() 731