1#!/usr/bin/env python3 2# 3# Copyright (c) 2015-2016 The Khronos Group Inc. 4# Copyright (c) 2015-2016 Valve Corporation 5# Copyright (c) 2015-2016 LunarG, Inc. 6# 7# Permission is hereby granted, free of charge, to any person obtaining a copy 8# of this software and/or associated documentation files (the "Materials"), to 9# deal in the Materials without restriction, including without limitation the 10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11# sell copies of the Materials, and to permit persons to whom the Materials are 12# furnished to do so, subject to the following conditions: 13# 14# The above copyright notice(s) and this permission notice shall be included in 15# all copies or substantial portions of the Materials. 16# 17# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20# 21# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 24# USE OR OTHER DEALINGS IN THE MATERIALS.Copyright (C) 2015 Valve Corporation 25# 26# Author: Jon Ashburn <jon@lunarg.com> 27# 28 29import os, sys 30 31# add main repo directory so vulkan.py can be imported. This needs to be a complete path. 32ld_path = os.path.dirname(os.path.abspath(__file__)) 33main_path = os.path.abspath(ld_path + "/../") 34sys.path.append(main_path) 35 36import vulkan 37 38def generate_get_proc_addr_check(name): 39 return " if (!%s || %s[0] != 'v' || %s[1] != 'k')\n" \ 40 " return NULL;" % ((name,) * 3) 41 42class Subcommand(object): 43 def __init__(self, argv): 44 self.argv = argv 45 self.headers = vulkan.headers 46 self.protos = vulkan.protos 47 48 def run(self): 49 print(self.generate()) 50 51 def _requires_special_trampoline_code(self, name): 52 # Dont be cute trying to use a general rule to programmatically populate this list 53 # it just obsfucates what is going on! 54 wsi_creates_dispatchable_object = ["CreateSwapchainKHR"] 55 creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "AllocateCommandBuffers"] + wsi_creates_dispatchable_object 56 if name in creates_dispatchable_object: 57 return True 58 else: 59 return False 60 61 def _is_loader_non_trampoline_entrypoint(self, proto): 62 if proto.name in ["GetDeviceProcAddr", "EnumeratePhysicalDevices", "EnumerateLayers", "DbgRegisterMsgCallback", "DbgUnregisterMsgCallback", "DbgSetGlobalOption", "DestroyInstance"]: 63 return True 64 return not self.is_dispatchable_object_first_param(proto) 65 66 67 def is_dispatchable_object_first_param(self, proto): 68 in_objs = proto.object_in_params() 69 non_dispatch_objs = [] 70 param0 = proto.params[0] 71 return (len(in_objs) > 0) and (in_objs[0].ty == param0.ty) and (param0.ty not in non_dispatch_objs) 72 73 def generate(self): 74 copyright = self.generate_copyright() 75 header = self.generate_header() 76 body = self.generate_body() 77 footer = self.generate_footer() 78 79 contents = [] 80 if copyright: 81 contents.append(copyright) 82 if header: 83 contents.append(header) 84 if body: 85 contents.append(body) 86 if footer: 87 contents.append(footer) 88 89 return "\n\n".join(contents) 90 91 def generate_copyright(self): 92 return """/* THIS FILE IS GENERATED. DO NOT EDIT. */ 93 94/* 95 * Copyright (c) 2015-2016 The Khronos Group Inc. 96 * Copyright (c) 2015-2016 Valve Corporation 97 * Copyright (c) 2015-2016 LunarG, Inc. 98 * 99 * Permission is hereby granted, free of charge, to any person obtaining a copy 100 * of this software and/or associated documentation files (the "Materials"), to 101 * deal in the Materials without restriction, including without limitation the 102 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 103 * sell copies of the Materials, and to permit persons to whom the Materials are 104 * furnished to do so, subject to the following conditions: 105 * 106 * The above copyright notice(s) and this permission notice shall be included in 107 * all copies or substantial portions of the Materials. 108 * 109 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 110 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 111 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 112 * 113 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 114 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 115 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 116 * USE OR OTHER DEALINGS IN THE MATERIALS. 117 * 118 * Author: Jon Ashburn <jon@lunarg.com> 119 * Author: Chia-I Wu <olv@lunarg.com> 120 * Author: Courtney Goeltzenleuchter <courtney@lunarg.com> 121 */""" 122 123 def generate_header(self): 124 return "\n".join(["#include <" + h + ">" for h in self.headers]) 125 126 def generate_body(self): 127 pass 128 129 def generate_footer(self): 130 pass 131 132class DevExtTrampolineSubcommand(Subcommand): 133 def generate_header(self): 134 lines = [] 135 lines.append("#include \"vk_loader_platform.h\"") 136 lines.append("#include \"loader.h\"") 137 lines.append("#if defined(__linux__)") 138 lines.append("#pragma GCC optimize(3) // force gcc to use tail-calls") 139 lines.append("#endif") 140 return "\n".join(lines) 141 142 def generate_body(self): 143 lines = [] 144 for i in range(250): 145 lines.append('\nVKAPI_ATTR void VKAPI_CALL vkDevExt%s(VkDevice device)' % i) 146 lines.append('{') 147 lines.append(' const struct loader_dev_dispatch_table *disp;') 148 lines.append(' disp = loader_get_dev_dispatch(device);') 149 lines.append(' disp->ext_dispatch.DevExt[%s](device);' % i) 150 lines.append('}') 151 lines.append('') 152 lines.append('void *loader_get_dev_ext_trampoline(uint32_t index)') 153 lines.append('{') 154 lines.append(' switch (index) {') 155 for i in range(250): 156 lines.append(' case %s:' % i) 157 lines.append(' return vkDevExt%s;' % i) 158 lines.append(' }') 159 lines.append(' return NULL;') 160 lines.append('}') 161 return "\n".join(lines) 162 163class LoaderEntrypointsSubcommand(Subcommand): 164 def generate_header(self): 165 return "#include \"loader.h\"" 166 167 def _generate_object_setup(self, proto): 168 method = "loader_init_dispatch" 169 cond = "res == VK_SUCCESS" 170 setup = [] 171 172 if not self._requires_special_trampoline_code(proto.name): 173 return setup 174 175 if "Get" in proto.name: 176 method = "loader_set_dispatch" 177 178 if proto.name == "GetSwapchainInfoKHR": 179 ptype = proto.params[-3].name 180 psize = proto.params[-2].name 181 pdata = proto.params[-1].name 182 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_KHR && " 183 "%s && %s" % (ptype, pdata, cond)) 184 setup.append("VkSwapchainImageInfoKHR *info = %s;" % pdata) 185 setup.append("size_t count = *%s / sizeof(*info), i;" % psize) 186 setup.append("for (i = 0; i < count; i++) {") 187 setup.append(" %s(info[i].image, disp);" % method) 188 setup.append(" %s(info[i].memory, disp);" % method) 189 setup.append("}") 190 else: 191 obj_params = proto.object_out_params() 192 for param in obj_params: 193 setup.append("%s(*%s, disp);" % (method, param.name)) 194 195 if setup: 196 joined = "\n ".join(setup) 197 setup = [] 198 setup.append(" if (%s) {" % cond) 199 setup.append(" " + joined) 200 setup.append(" }") 201 202 return "\n".join(setup) 203 204 def _generate_loader_dispatch_entrypoints(self, qual=""): 205 if qual: 206 qual += " " 207 208 funcs = [] 209 for proto in self.protos: 210 if self._is_loader_non_trampoline_entrypoint(proto): 211 continue 212 func = [] 213 214 obj_setup = self._generate_object_setup(proto) 215 216 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI")) 217 func.append("{") 218 219 # declare local variables 220 func.append(" const VkLayerDispatchTable *disp;") 221 if proto.ret != 'void' and obj_setup: 222 func.append(" VkResult res;") 223 func.append("") 224 225 # get dispatch table 226 func.append(" disp = loader_get_dispatch(%s);" % 227 proto.params[0].name) 228 func.append("") 229 230 # dispatch! 231 dispatch = "disp->%s;" % proto.c_call() 232 if proto.ret == 'void': 233 func.append(" " + dispatch) 234 elif not obj_setup: 235 func.append(" return " + dispatch) 236 else: 237 func.append(" res = " + dispatch) 238 func.append(obj_setup) 239 func.append("") 240 func.append(" return res;") 241 242 func.append("}") 243 244 funcs.append("\n".join(func)) 245 246 return "\n\n".join(funcs) 247 248 def generate_body(self): 249 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")] 250 251 return "\n\n".join(body) 252 253class DispatchTableOpsSubcommand(Subcommand): 254 def run(self): 255 if len(self.argv) != 1: 256 print("DispatchTableOpsSubcommand: <prefix> unspecified") 257 return 258 259 self.prefix = self.argv[0] 260 super().run() 261 262 def generate_header(self): 263 return "\n".join(["#include <vulkan/vulkan.h>", 264 "#include <vkLayer.h>", 265 "#include <string.h>", 266 "#include \"loader_platform.h\""]) 267 268 def _generate_init(self, type): 269 stmts = [] 270 func = [] 271 if type == "device": 272 for proto in self.protos: 273 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance": 274 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" % 275 (proto.name, proto.name, proto.name)) 276 else: 277 stmts.append("table->%s = vk%s; /* non-dispatchable */" % 278 (proto.name, proto.name)) 279 func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table," 280 % self.prefix) 281 func.append("%s PFN_vkGetDeviceProcAddr gpa," 282 % (" " * len(self.prefix))) 283 func.append("%s VkPhysicalDevice gpu)" 284 % (" " * len(self.prefix))) 285 else: 286 for proto in self.protos: 287 if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice": 288 continue 289 stmts.append("table->%s = vk%s;" % (proto.name, proto.name)) 290 func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)" 291 % self.prefix) 292 func.append("{") 293 func.append(" %s" % "\n ".join(stmts)) 294 func.append("}") 295 296 return "\n".join(func) 297 298 def _generate_lookup(self): 299 lookups = [] 300 for proto in self.protos: 301 if self.is_dispatchable_object_first_param(proto): 302 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name)) 303 lookups.append(" return (void *) table->%s;" 304 % (proto.name)) 305 306 func = [] 307 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table," 308 % self.prefix) 309 func.append("%s const char *name)" 310 % (" " * len(self.prefix))) 311 func.append("{") 312 func.append(generate_get_proc_addr_check("name")) 313 func.append("") 314 func.append(" name += 2;") 315 func.append(" %s" % "\n ".join(lookups)) 316 func.append("") 317 func.append(" return NULL;") 318 func.append("}") 319 320 return "\n".join(func) 321 322 def generate_body(self): 323 body = [self._generate_init("device"), 324 self._generate_lookup(), 325 self._generate_init("instance")] 326 327 return "\n\n".join(body) 328 329class WinDefFileSubcommand(Subcommand): 330 def run(self): 331 library_exports = { 332 "all": [], 333 } 334 335 if len(self.argv) != 2 or self.argv[1] not in library_exports: 336 print("WinDefFileSubcommand: <library-name> {%s}" % 337 "|".join(library_exports.keys())) 338 return 339 340 self.library = self.argv[0] 341 self.exports = library_exports[self.argv[1]] 342 343 super().run() 344 345 def generate_copyright(self): 346 return """; THIS FILE IS GENERATED. DO NOT EDIT. 347 348;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 349; Copyright (c) 2015-2016 The Khronos Group Inc. 350; Copyright (c) 2015-2016 Valve Corporation 351; Copyright (c) 2015-2016 LunarG, Inc. 352; 353; Permission is hereby granted, free of charge, to any person obtaining a copy 354; of this software and/or associated documentation files (the "Materials"), to 355; deal in the Materials without restriction, including without limitation the 356; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 357; sell copies of the Materials, and to permit persons to whom the Materials are 358; furnished to do so, subject to the following conditions: 359; 360; The above copyright notice(s) and this permission notice shall be included in 361; all copies or substantial portions of the Materials. 362; 363; THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 364; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 365; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 366; 367; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 368; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 369; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 370; USE OR OTHER DEALINGS IN THE MATERIALS. 371; 372; 373; Author: Jon Ashburn <jon@lunarg.com> 374;;;; End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;""" 375 376 def generate_header(self): 377 return "; The following is required on Windows, for exporting symbols from the DLL" 378 379 def generate_body(self): 380 body = [] 381 382 body.append("LIBRARY " + self.library) 383 body.append("EXPORTS") 384 385 for proto in self.protos: 386 if self.exports and proto.name not in self.exports: 387 continue 388# This was intended to reject WSI calls, but actually rejects ALL extensions 389# TODO: Make this WSI-extension specific 390# if proto.name.endswith("KHR"): 391# continue 392 body.append(" vk" + proto.name) 393 394 return "\n".join(body) 395 396class LoaderGetProcAddrSubcommand(Subcommand): 397 def run(self): 398 self.prefix = "vk" 399 400 # we could get the list from argv if wanted 401 self.intercepted = [proto.name for proto in self.protos] 402 403 for proto in self.protos: 404 if proto.name == "GetDeviceProcAddr": 405 self.gpa = proto 406 407 super().run() 408 409 def generate_header(self): 410 return "\n".join(["#include <string.h>"]) 411 412 def generate_body(self): 413 lookups = [] 414 for proto in self.protos: 415 if proto.name not in self.intercepted: 416 lookups.append("/* no %s%s */" % (self.prefix, proto.name)) 417 continue 418 419 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name) 420 lookups.append(" return (%s) %s%s;" % 421 (self.gpa.ret, self.prefix, proto.name)) 422 423 special_lookups = [] 424 for proto in self.protos: 425 if self._is_loader_non_trampoline_entrypoint(proto) or self._requires_special_trampoline_code(proto.name): 426 special_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name) 427 special_lookups.append(" return (%s) %s%s;" % 428 (self.gpa.ret, self.prefix, proto.name)) 429 else: 430 continue 431 body = [] 432 body.append("static inline %s globalGetProcAddr(const char *name)" % 433 self.gpa.ret) 434 body.append("{") 435 body.append(generate_get_proc_addr_check("name")) 436 body.append("") 437 body.append(" name += 2;") 438 body.append(" %s" % "\n ".join(lookups)) 439 body.append("") 440 body.append(" return NULL;") 441 body.append("}") 442 body.append("") 443 body.append("static inline void *loader_non_passthrough_gpa(const char *name)") 444 body.append("{") 445 body.append(generate_get_proc_addr_check("name")) 446 body.append("") 447 body.append(" name += 2;") 448 body.append(" %s" % "\n ".join(special_lookups)) 449 body.append("") 450 body.append(" return NULL;") 451 body.append("}") 452 453 return "\n".join(body) 454 455def main(): 456 457 wsi = { 458 "Win32", 459 "Android", 460 "Xcb", 461 "Xlib", 462 "Wayland", 463 "Mir" 464 } 465 466 subcommands = { 467 "dev-ext-trampoline": DevExtTrampolineSubcommand, 468 "loader-entrypoints": LoaderEntrypointsSubcommand, 469 "dispatch-table-ops": DispatchTableOpsSubcommand, 470 "win-def-file": WinDefFileSubcommand, 471 "loader-get-proc-addr": LoaderGetProcAddrSubcommand, 472 } 473 474 if len(sys.argv) < 3 or sys.argv[1] not in wsi or sys.argv[2] not in subcommands: 475 print("Usage: %s <wsi> <subcommand> [options]" % sys.argv[0]) 476 print 477 print("Available wsi (displayservers) are: %s" % " ".join(wsi)) 478 print("Available subcommands are: %s" % " ".join(subcommands)) 479 exit(1) 480 481 subcmd = subcommands[sys.argv[2]](sys.argv[3:]) 482 subcmd.run() 483 484if __name__ == "__main__": 485 main() 486