blob: 143af61f1b453170cff97a61eccc99fca14c0ed1 [file] [log] [blame]
Jon Ashburnaef65882015-05-04 09:16:41 -06001#!/usr/bin/env python3
2#
Courtney Goeltzenleuchter8a17da52015-10-29 13:50:34 -06003# Copyright (C) 2015 Valve Corporation
Jon Ashburnaef65882015-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 Goeltzenleuchter96cd7952015-10-30 11:14:30 -060023# Author: Jon Ashburn <jon@lunarg.com>
24#
Jon Ashburnaef65882015-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 Ashburn9bba6dc2015-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 Elliott338dedb2015-08-21 15:09:33 -060051 wsi_creates_dispatchable_object = ["CreateSwapchainKHR"]
Chia-I Wu1f851912015-10-27 18:04:07 +080052 creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "AllocateCommandBuffers"] + wsi_creates_dispatchable_object
Jon Ashburn9bba6dc2015-05-05 09:37:01 -060053 if name in creates_dispatchable_object:
54 return True
55 else:
56 return False
Jon Ashburnaef65882015-05-04 09:16:41 -060057
Jon Ashburn9bba6dc2015-05-05 09:37:01 -060058 def _is_loader_non_trampoline_entrypoint(self, proto):
Jon Ashburn1245cec2015-05-18 13:20:15 -060059 if proto.name in ["GetDeviceProcAddr", "EnumeratePhysicalDevices", "EnumerateLayers", "DbgRegisterMsgCallback", "DbgUnregisterMsgCallback", "DbgSetGlobalOption", "DestroyInstance"]:
Jon Ashburnaef65882015-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 Ashburnaef65882015-05-04 09:16:41 -060092 *
Courtney Goeltzenleuchter8a17da52015-10-29 13:50:34 -060093 * Copyright (C) 2015 Valve Corporation
Jon Ashburnaef65882015-05-04 09:16:41 -060094 *
95 * Permission is hereby granted, free of charge, to any person obtaining a
96 * copy of this software and associated documentation files (the "Software"),
97 * to deal in the Software without restriction, including without limitation
98 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
99 * and/or sell copies of the Software, and to permit persons to whom the
100 * Software is furnished to do so, subject to the following conditions:
101 *
102 * The above copyright notice and this permission notice shall be included
103 * in all copies or substantial portions of the Software.
104 *
105 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
108 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
110 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
111 * DEALINGS IN THE SOFTWARE.
Courtney Goeltzenleuchter96cd7952015-10-30 11:14:30 -0600112 *
113 * Author: Jon Ashburn <jon@lunarg.com>
Jon Ashburnaef65882015-05-04 09:16:41 -0600114 */"""
115
116 def generate_header(self):
117 return "\n".join(["#include <" + h + ">" for h in self.headers])
118
119 def generate_body(self):
120 pass
121
122 def generate_footer(self):
123 pass
124
125class LoaderEntrypointsSubcommand(Subcommand):
126 def generate_header(self):
127 return "#include \"loader.h\""
128
129 def _generate_object_setup(self, proto):
130 method = "loader_init_dispatch"
131 cond = "res == VK_SUCCESS"
Jon Ashburn9bba6dc2015-05-05 09:37:01 -0600132 setup = []
133
134 if not self._requires_special_trampoline_code(proto.name):
135 return setup
Jon Ashburnaef65882015-05-04 09:16:41 -0600136
137 if "Get" in proto.name:
138 method = "loader_set_dispatch"
139
Ian Elliott338dedb2015-08-21 15:09:33 -0600140 if proto.name == "GetSwapchainInfoKHR":
Jon Ashburnaef65882015-05-04 09:16:41 -0600141 ptype = proto.params[-3].name
142 psize = proto.params[-2].name
143 pdata = proto.params[-1].name
Ian Elliott338dedb2015-08-21 15:09:33 -0600144 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_KHR && "
Jon Ashburnaef65882015-05-04 09:16:41 -0600145 "%s && %s" % (ptype, pdata, cond))
Ian Elliott338dedb2015-08-21 15:09:33 -0600146 setup.append("VkSwapchainImageInfoKHR *info = %s;" % pdata)
Jon Ashburnaef65882015-05-04 09:16:41 -0600147 setup.append("size_t count = *%s / sizeof(*info), i;" % psize)
148 setup.append("for (i = 0; i < count; i++) {")
149 setup.append(" %s(info[i].image, disp);" % method)
150 setup.append(" %s(info[i].memory, disp);" % method)
151 setup.append("}")
Jon Ashburn9bba6dc2015-05-05 09:37:01 -0600152 else:
Jon Ashburnaef65882015-05-04 09:16:41 -0600153 obj_params = proto.object_out_params()
154 for param in obj_params:
155 setup.append("%s(*%s, disp);" % (method, param.name))
156
157 if setup:
158 joined = "\n ".join(setup)
159 setup = []
160 setup.append(" if (%s) {" % cond)
161 setup.append(" " + joined)
162 setup.append(" }")
163
164 return "\n".join(setup)
165
166 def _generate_loader_dispatch_entrypoints(self, qual=""):
167 if qual:
168 qual += " "
169
170 funcs = []
171 for proto in self.protos:
Jon Ashburn9bba6dc2015-05-05 09:37:01 -0600172 if self._is_loader_non_trampoline_entrypoint(proto):
Jon Ashburnaef65882015-05-04 09:16:41 -0600173 continue
174 func = []
175
176 obj_setup = self._generate_object_setup(proto)
177
178 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI"))
179 func.append("{")
180
181 # declare local variables
182 func.append(" const VkLayerDispatchTable *disp;")
183 if proto.ret != 'void' and obj_setup:
184 func.append(" VkResult res;")
185 func.append("")
186
187 # get dispatch table
188 func.append(" disp = loader_get_dispatch(%s);" %
189 proto.params[0].name)
190 func.append("")
191
192 # dispatch!
193 dispatch = "disp->%s;" % proto.c_call()
194 if proto.ret == 'void':
195 func.append(" " + dispatch)
196 elif not obj_setup:
197 func.append(" return " + dispatch)
198 else:
199 func.append(" res = " + dispatch)
200 func.append(obj_setup)
201 func.append("")
202 func.append(" return res;")
203
204 func.append("}")
205
206 funcs.append("\n".join(func))
207
208 return "\n\n".join(funcs)
209
210 def generate_body(self):
211 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")]
212
213 return "\n\n".join(body)
214
215class DispatchTableOpsSubcommand(Subcommand):
216 def run(self):
217 if len(self.argv) != 1:
218 print("DispatchTableOpsSubcommand: <prefix> unspecified")
219 return
220
221 self.prefix = self.argv[0]
222 super().run()
223
224 def generate_header(self):
David Pinedo329ca9e2015-11-06 12:54:48 -0700225 return "\n".join(["#include <vulkan/vulkan.h>",
Jon Ashburnaef65882015-05-04 09:16:41 -0600226 "#include <vkLayer.h>",
227 "#include <string.h>",
228 "#include \"loader_platform.h\""])
229
Jon Ashburn9a9bb642015-05-04 16:27:53 -0600230 def _generate_init(self, type):
Jon Ashburnaef65882015-05-04 09:16:41 -0600231 stmts = []
Jon Ashburnaef65882015-05-04 09:16:41 -0600232 func = []
Jon Ashburn9a9bb642015-05-04 16:27:53 -0600233 if type == "device":
234 for proto in self.protos:
235 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
236 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
237 (proto.name, proto.name, proto.name))
238 else:
239 stmts.append("table->%s = vk%s; /* non-dispatchable */" %
240 (proto.name, proto.name))
241 func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table,"
Jon Ashburnaef65882015-05-04 09:16:41 -0600242 % self.prefix)
Jon Ashburn1245cec2015-05-18 13:20:15 -0600243 func.append("%s PFN_vkGetDeviceProcAddr gpa,"
Jon Ashburnaef65882015-05-04 09:16:41 -0600244 % (" " * len(self.prefix)))
Jon Ashburn9a9bb642015-05-04 16:27:53 -0600245 func.append("%s VkPhysicalDevice gpu)"
Jon Ashburnaef65882015-05-04 09:16:41 -0600246 % (" " * len(self.prefix)))
Jon Ashburn9a9bb642015-05-04 16:27:53 -0600247 else:
248 for proto in self.protos:
249 if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice":
250 continue
251 stmts.append("table->%s = vk%s;" % (proto.name, proto.name))
252 func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)"
253 % self.prefix)
Jon Ashburnaef65882015-05-04 09:16:41 -0600254 func.append("{")
255 func.append(" %s" % "\n ".join(stmts))
256 func.append("}")
257
258 return "\n".join(func)
259
260 def _generate_lookup(self):
261 lookups = []
262 for proto in self.protos:
263 if self.is_dispatchable_object_first_param(proto):
264 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
265 lookups.append(" return (void *) table->%s;"
266 % (proto.name))
267
268 func = []
269 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
270 % self.prefix)
271 func.append("%s const char *name)"
272 % (" " * len(self.prefix)))
273 func.append("{")
274 func.append(generate_get_proc_addr_check("name"))
275 func.append("")
276 func.append(" name += 2;")
277 func.append(" %s" % "\n ".join(lookups))
278 func.append("")
279 func.append(" return NULL;")
280 func.append("}")
281
282 return "\n".join(func)
283
284 def generate_body(self):
Jon Ashburn9a9bb642015-05-04 16:27:53 -0600285 body = [self._generate_init("device"),
286 self._generate_lookup(),
287 self._generate_init("instance")]
Jon Ashburnaef65882015-05-04 09:16:41 -0600288
289 return "\n\n".join(body)
290
291class WinDefFileSubcommand(Subcommand):
292 def run(self):
293 library_exports = {
294 "all": [],
295 }
296
297 if len(self.argv) != 2 or self.argv[1] not in library_exports:
298 print("WinDefFileSubcommand: <library-name> {%s}" %
299 "|".join(library_exports.keys()))
300 return
301
302 self.library = self.argv[0]
303 self.exports = library_exports[self.argv[1]]
304
305 super().run()
306
307 def generate_copyright(self):
308 return """; THIS FILE IS GENERATED. DO NOT EDIT.
309
310;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
311; Vulkan
312;
Courtney Goeltzenleuchter8a17da52015-10-29 13:50:34 -0600313; Copyright (C) 2015 Valve Corporation
Jon Ashburnaef65882015-05-04 09:16:41 -0600314;
315; Permission is hereby granted, free of charge, to any person obtaining a
316; copy of this software and associated documentation files (the "Software"),
317; to deal in the Software without restriction, including without limitation
318; the rights to use, copy, modify, merge, publish, distribute, sublicense,
319; and/or sell copies of the Software, and to permit persons to whom the
320; Software is furnished to do so, subject to the following conditions:
321;
322; The above copyright notice and this permission notice shall be included
323; in all copies or substantial portions of the Software.
324;
325; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
326; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
327; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
328; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
329; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
330; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
331; DEALINGS IN THE SOFTWARE.
Courtney Goeltzenleuchter96cd7952015-10-30 11:14:30 -0600332 *
333 * Author: Jon Ashburn <jon@lunarg.com>
Jon Ashburnaef65882015-05-04 09:16:41 -0600334;;;; End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"""
335
336 def generate_header(self):
337 return "; The following is required on Windows, for exporting symbols from the DLL"
338
339 def generate_body(self):
340 body = []
341
342 body.append("LIBRARY " + self.library)
343 body.append("EXPORTS")
344
345 for proto in self.protos:
346 if self.exports and proto.name not in self.exports:
347 continue
Ian Elliott338dedb2015-08-21 15:09:33 -0600348 if proto.name.endswith("KHR"):
Tony Barbour69698512015-06-18 16:29:32 -0600349 continue
Jon Ashburnaef65882015-05-04 09:16:41 -0600350 body.append(" vk" + proto.name)
351
352 return "\n".join(body)
353
354class LoaderGetProcAddrSubcommand(Subcommand):
355 def run(self):
356 self.prefix = "vk"
357
358 # we could get the list from argv if wanted
359 self.intercepted = [proto.name for proto in self.protos]
360
361 for proto in self.protos:
Jon Ashburn1245cec2015-05-18 13:20:15 -0600362 if proto.name == "GetDeviceProcAddr":
Jon Ashburnaef65882015-05-04 09:16:41 -0600363 self.gpa = proto
364
365 super().run()
366
367 def generate_header(self):
368 return "\n".join(["#include <string.h>"])
369
370 def generate_body(self):
371 lookups = []
372 for proto in self.protos:
373 if proto.name not in self.intercepted:
374 lookups.append("/* no %s%s */" % (self.prefix, proto.name))
375 continue
376
377 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
378 lookups.append(" return (%s) %s%s;" %
379 (self.gpa.ret, self.prefix, proto.name))
380
381 special_lookups = []
Jon Ashburnaef65882015-05-04 09:16:41 -0600382 for proto in self.protos:
Jon Ashburn9bba6dc2015-05-05 09:37:01 -0600383 if self._is_loader_non_trampoline_entrypoint(proto) or self._requires_special_trampoline_code(proto.name):
Jon Ashburnaef65882015-05-04 09:16:41 -0600384 special_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
385 special_lookups.append(" return (%s) %s%s;" %
386 (self.gpa.ret, self.prefix, proto.name))
387 else:
388 continue
389 body = []
390 body.append("static inline %s globalGetProcAddr(const char *name)" %
391 self.gpa.ret)
392 body.append("{")
393 body.append(generate_get_proc_addr_check("name"))
394 body.append("")
395 body.append(" name += 2;")
396 body.append(" %s" % "\n ".join(lookups))
397 body.append("")
398 body.append(" return NULL;")
399 body.append("}")
400 body.append("")
401 body.append("static inline void *loader_non_passthrough_gpa(const char *name)")
402 body.append("{")
403 body.append(generate_get_proc_addr_check("name"))
404 body.append("")
405 body.append(" name += 2;")
406 body.append(" %s" % "\n ".join(special_lookups))
407 body.append("")
408 body.append(" return NULL;")
409 body.append("}")
410
411 return "\n".join(body)
412
413def main():
414 subcommands = {
415 "loader-entrypoints": LoaderEntrypointsSubcommand,
416 "dispatch-table-ops": DispatchTableOpsSubcommand,
417 "win-def-file": WinDefFileSubcommand,
418 "loader-get-proc-addr": LoaderGetProcAddrSubcommand,
419 }
420
421 if len(sys.argv) < 2 or sys.argv[1] not in subcommands:
422 print("Usage: %s <subcommand> [options]" % sys.argv[0])
423 print
424 print("Available sucommands are: %s" % " ".join(subcommands))
425 exit(1)
426
427 subcmd = subcommands[sys.argv[1]](sys.argv[2:])
428 subcmd.run()
429
430if __name__ == "__main__":
431 main()