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