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