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 copy 27import functools 28import math 29import os 30import xml.etree.ElementTree as et 31 32from collections import OrderedDict, namedtuple 33from mako.template import Template 34 35from tu_extensions import VkVersion, MAX_API_VERSION, EXTENSIONS 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 42# We currently don't use layers in tu, but keeping the ability for anv 43# anyways, so we can use it for device groups. 44LAYERS = [ 45 'tu' 46] 47 48TEMPLATE_H = Template("""\ 49/* This file generated from ${filename}, don't edit directly. */ 50 51struct tu_dispatch_table { 52 union { 53 void *entrypoints[${len(entrypoints)}]; 54 struct { 55 % for e in entrypoints: 56 % if e.guard is not None: 57#ifdef ${e.guard} 58 PFN_${e.name} ${e.name}; 59#else 60 void *${e.name}; 61# endif 62 % else: 63 PFN_${e.name} ${e.name}; 64 % endif 65 % endfor 66 }; 67 }; 68}; 69 70% for e in entrypoints: 71 % if e.alias: 72 <% continue %> 73 % endif 74 % if e.guard is not None: 75#ifdef ${e.guard} 76 % endif 77 % for layer in LAYERS: 78 VKAPI_ATTR ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}); 79 % endfor 80 % if e.guard is not None: 81#endif // ${e.guard} 82 % endif 83% endfor 84""", output_encoding='utf-8') 85 86TEMPLATE_C = Template(u"""\ 87/* 88 * Copyright © 2015 Intel Corporation 89 * 90 * Permission is hereby granted, free of charge, to any person obtaining a 91 * copy of this software and associated documentation files (the "Software"), 92 * to deal in the Software without restriction, including without limitation 93 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 94 * and/or sell copies of the Software, and to permit persons to whom the 95 * Software is furnished to do so, subject to the following conditions: 96 * 97 * The above copyright notice and this permission notice (including the next 98 * paragraph) shall be included in all copies or substantial portions of the 99 * Software. 100 * 101 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 102 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 103 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 104 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 105 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 106 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 107 * IN THE SOFTWARE. 108 */ 109 110/* This file generated from ${filename}, don't edit directly. */ 111 112#include "tu_private.h" 113 114struct string_map_entry { 115 uint32_t name; 116 uint32_t hash; 117 uint32_t num; 118}; 119 120/* We use a big string constant to avoid lots of relocations from the entry 121 * point table to lots of little strings. The entries in the entry point table 122 * store the index into this big string. 123 */ 124 125static const char strings[] = 126% for s in strmap.sorted_strings: 127 "${s.string}\\0" 128% endfor 129; 130 131static const struct string_map_entry string_map_entries[] = { 132% for s in strmap.sorted_strings: 133 { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */ 134% endfor 135}; 136 137/* Hash table stats: 138 * size ${len(strmap.sorted_strings)} entries 139 * collisions entries: 140% for i in range(10): 141 * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]} 142% endfor 143 */ 144 145#define none 0xffff 146static const uint16_t string_map[${strmap.hash_size}] = { 147% for e in strmap.mapping: 148 ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' }, 149% endfor 150}; 151 152/* Weak aliases for all potential implementations. These will resolve to 153 * NULL if they're not defined, which lets the resolve_entrypoint() function 154 * either pick the correct entry point. 155 */ 156 157% for layer in LAYERS: 158 % for e in entrypoints: 159 % if e.alias: 160 <% continue %> 161 % endif 162 % if e.guard is not None: 163#ifdef ${e.guard} 164 % endif 165 ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}) __attribute__ ((weak)); 166 % if e.guard is not None: 167#endif // ${e.guard} 168 % endif 169 % endfor 170 171 const struct tu_dispatch_table ${layer}_layer = { 172 % for e in entrypoints: 173 % if e.guard is not None: 174#ifdef ${e.guard} 175 % endif 176 .${e.name} = ${e.prefixed_name(layer)}, 177 % if e.guard is not None: 178#endif // ${e.guard} 179 % endif 180 % endfor 181 }; 182% endfor 183 184static void * __attribute__ ((noinline)) 185tu_resolve_entrypoint(uint32_t index) 186{ 187 return tu_layer.entrypoints[index]; 188} 189 190/** Return true if the core version or extension in which the given entrypoint 191 * is defined is enabled. 192 * 193 * If instance is NULL, we only allow the 3 commands explicitly allowed by the vk 194 * spec. 195 * 196 * If device is NULL, all device extensions are considered enabled. 197 */ 198static bool 199tu_entrypoint_is_enabled(int index, uint32_t core_version, 200 const struct tu_instance_extension_table *instance, 201 const struct tu_device_extension_table *device) 202{ 203 switch (index) { 204% for e in entrypoints: 205 case ${e.num}: 206 % if not e.device_command: 207 if (device) return false; 208 % endif 209 % if e.name == 'vkGetInstanceProcAddr' or e.name == 'vkCreateInstance' or e.name == 'vkEnumerateInstanceExtensionProperties' or e.name == 'vkEnumerateInstanceLayerProperties' or e.name == 'vkEnumerateInstanceVersion': 210 return !device; 211 % elif e.core_version: 212 return instance && ${e.core_version.c_vk_version()} <= core_version; 213 % elif e.extensions: 214 % for ext in e.extensions: 215 % if ext.type == 'instance': 216 if (instance && instance->${ext.name[3:]}) return true; 217 % else: 218 if (instance && (!device || device->${ext.name[3:]})) return true; 219 % endif 220 %endfor 221 return false; 222 % else: 223 return instance; 224 % endif 225% endfor 226 default: 227 return false; 228 } 229} 230 231static int 232tu_lookup_entrypoint(const char *name) 233{ 234 static const uint32_t prime_factor = ${strmap.prime_factor}; 235 static const uint32_t prime_step = ${strmap.prime_step}; 236 const struct string_map_entry *e; 237 uint32_t hash, h; 238 uint16_t i; 239 const char *p; 240 241 hash = 0; 242 for (p = name; *p; p++) 243 hash = hash * prime_factor + *p; 244 245 h = hash; 246 while (1) { 247 i = string_map[h & ${strmap.hash_mask}]; 248 if (i == none) 249 return -1; 250 e = &string_map_entries[i]; 251 if (e->hash == hash && strcmp(name, strings + e->name) == 0) 252 return e->num; 253 h += prime_step; 254 } 255 256 return -1; 257} 258 259void * 260tu_lookup_entrypoint_unchecked(const char *name) 261{ 262 int index = tu_lookup_entrypoint(name); 263 if (index < 0) 264 return NULL; 265 return tu_resolve_entrypoint(index); 266} 267 268void * 269tu_lookup_entrypoint_checked(const char *name, 270 uint32_t core_version, 271 const struct tu_instance_extension_table *instance, 272 const struct tu_device_extension_table *device) 273{ 274 int index = tu_lookup_entrypoint(name); 275 if (index < 0 || !tu_entrypoint_is_enabled(index, core_version, instance, device)) 276 return NULL; 277 return tu_resolve_entrypoint(index); 278}""", output_encoding='utf-8') 279 280U32_MASK = 2**32 - 1 281 282PRIME_FACTOR = 5024183 283PRIME_STEP = 19 284 285def round_to_pow2(x): 286 return 2**int(math.ceil(math.log(x, 2))) 287 288class StringIntMapEntry(object): 289 def __init__(self, string, num): 290 self.string = string 291 self.num = num 292 293 # Calculate the same hash value that we will calculate in C. 294 h = 0 295 for c in string: 296 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK 297 self.hash = h 298 299 self.offset = None 300 301class StringIntMap(object): 302 def __init__(self): 303 self.baked = False 304 self.strings = dict() 305 306 def add_string(self, string, num): 307 assert not self.baked 308 assert string not in self.strings 309 assert num >= 0 and num < 2**31 310 self.strings[string] = StringIntMapEntry(string, num) 311 312 def bake(self): 313 self.sorted_strings = \ 314 sorted(self.strings.values(), key=lambda x: x.string) 315 offset = 0 316 for entry in self.sorted_strings: 317 entry.offset = offset 318 offset += len(entry.string) + 1 319 320 # Save off some values that we'll need in C 321 self.hash_size = round_to_pow2(len(self.strings) * 1.25) 322 self.hash_mask = self.hash_size - 1 323 self.prime_factor = PRIME_FACTOR 324 self.prime_step = PRIME_STEP 325 326 self.mapping = [-1] * self.hash_size 327 self.collisions = [0] * 10 328 for idx, s in enumerate(self.sorted_strings): 329 level = 0 330 h = s.hash 331 while self.mapping[h & self.hash_mask] >= 0: 332 h = h + PRIME_STEP 333 level = level + 1 334 self.collisions[min(level, 9)] += 1 335 self.mapping[h & self.hash_mask] = idx 336 337EntrypointParam = namedtuple('EntrypointParam', 'type name decl') 338 339class EntrypointBase(object): 340 def __init__(self, name): 341 self.name = name 342 self.alias = None 343 self.guard = None 344 self.enabled = False 345 self.num = None 346 # Extensions which require this entrypoint 347 self.core_version = None 348 self.extensions = [] 349 350class Entrypoint(EntrypointBase): 351 def __init__(self, name, return_type, params, guard = None): 352 super(Entrypoint, self).__init__(name) 353 self.return_type = return_type 354 self.params = params 355 self.guard = guard 356 self.device_command = len(params) > 0 and (params[0].type == 'VkDevice' or params[0].type == 'VkQueue' or params[0].type == 'VkCommandBuffer') 357 358 def prefixed_name(self, prefix): 359 assert self.name.startswith('vk') 360 return prefix + '_' + self.name[2:] 361 362 def decl_params(self): 363 return ', '.join(p.decl for p in self.params) 364 365 def call_params(self): 366 return ', '.join(p.name for p in self.params) 367 368class EntrypointAlias(EntrypointBase): 369 def __init__(self, name, entrypoint): 370 super(EntrypointAlias, self).__init__(name) 371 self.alias = entrypoint 372 self.device_command = entrypoint.device_command 373 374 def prefixed_name(self, prefix): 375 return self.alias.prefixed_name(prefix) 376 377def get_entrypoints(doc, entrypoints_to_defines, start_index): 378 """Extract the entry points from the registry.""" 379 entrypoints = OrderedDict() 380 381 for command in doc.findall('./commands/command'): 382 if 'alias' in command.attrib: 383 alias = command.attrib['name'] 384 target = command.attrib['alias'] 385 entrypoints[alias] = EntrypointAlias(alias, entrypoints[target]) 386 else: 387 name = command.find('./proto/name').text 388 ret_type = command.find('./proto/type').text 389 params = [EntrypointParam( 390 type = p.find('./type').text, 391 name = p.find('./name').text, 392 decl = ''.join(p.itertext()) 393 ) for p in command.findall('./param')] 394 guard = entrypoints_to_defines.get(name) 395 # They really need to be unique 396 assert name not in entrypoints 397 entrypoints[name] = Entrypoint(name, ret_type, params, guard) 398 399 for feature in doc.findall('./feature'): 400 assert feature.attrib['api'] == 'vulkan' 401 version = VkVersion(feature.attrib['number']) 402 if version > MAX_API_VERSION: 403 continue 404 405 for command in feature.findall('./require/command'): 406 e = entrypoints[command.attrib['name']] 407 e.enabled = True 408 assert e.core_version is None 409 e.core_version = version 410 411 supported_exts = dict((ext.name, ext) for ext in EXTENSIONS) 412 for extension in doc.findall('.extensions/extension'): 413 ext_name = extension.attrib['name'] 414 if ext_name not in supported_exts: 415 continue 416 417 ext = supported_exts[ext_name] 418 ext.type = extension.attrib['type'] 419 420 for command in extension.findall('./require/command'): 421 e = entrypoints[command.attrib['name']] 422 e.enabled = True 423 assert e.core_version is None 424 e.extensions.append(ext) 425 426 # if the base command is not supported by the driver yet, don't alias aliases 427 for e in entrypoints.values(): 428 if e.alias and not e.alias.enabled: 429 e_clone = copy.deepcopy(e.alias) 430 e_clone.enabled = True 431 e_clone.name = e.name 432 entrypoints[e.name] = e_clone 433 434 return [e for e in entrypoints.values() if e.enabled] 435 436 437def get_entrypoints_defines(doc): 438 """Maps entry points to extension defines.""" 439 entrypoints_to_defines = {} 440 441 for extension in doc.findall('./extensions/extension[@protect]'): 442 define = extension.attrib['protect'] 443 444 for entrypoint in extension.findall('./require/command'): 445 fullname = entrypoint.attrib['name'] 446 entrypoints_to_defines[fullname] = define 447 448 platform_define = {} 449 for platform in doc.findall('./platforms/platform'): 450 name = platform.attrib['name'] 451 define = platform.attrib['protect'] 452 platform_define[name] = define 453 454 for extension in doc.findall('./extensions/extension[@platform]'): 455 platform = extension.attrib['platform'] 456 define = platform_define[platform] 457 458 for entrypoint in extension.findall('./require/command'): 459 fullname = entrypoint.attrib['name'] 460 entrypoints_to_defines[fullname] = define 461 462 return entrypoints_to_defines 463 464 465def gen_code(entrypoints): 466 """Generate the C code.""" 467 strmap = StringIntMap() 468 for e in entrypoints: 469 strmap.add_string(e.name, e.num) 470 strmap.bake() 471 472 return TEMPLATE_C.render(entrypoints=entrypoints, 473 LAYERS=LAYERS, 474 strmap=strmap, 475 filename=os.path.basename(__file__)) 476 477 478def main(): 479 parser = argparse.ArgumentParser() 480 parser.add_argument('--outdir', help='Where to write the files.', 481 required=True) 482 parser.add_argument('--xml', 483 help='Vulkan API XML file.', 484 required=True, 485 action='append', 486 dest='xml_files') 487 args = parser.parse_args() 488 489 entrypoints = [] 490 491 for filename in args.xml_files: 492 doc = et.parse(filename) 493 entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc), 494 start_index=len(entrypoints)) 495 496 for num, e in enumerate(entrypoints): 497 e.num = num 498 499 # For outputting entrypoints.h we generate a tu_EntryPoint() prototype 500 # per entry point. 501 with open(os.path.join(args.outdir, 'tu_entrypoints.h'), 'wb') as f: 502 f.write(TEMPLATE_H.render(entrypoints=entrypoints, 503 LAYERS=LAYERS, 504 filename=os.path.basename(__file__))) 505 with open(os.path.join(args.outdir, 'tu_entrypoints.c'), 'wb') as f: 506 f.write(gen_code(entrypoints)) 507 508 509if __name__ == '__main__': 510 main() 511