blob: 2930cd6ba29489b7fe46d58db4a08ff15a2ca00d [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#
23
24import os, sys
25
26# add main repo directory so vulkan.py can be imported. This needs to be a complete path.
27ld_path = os.path.dirname(os.path.abspath(__file__))
28main_path = os.path.abspath(ld_path + "/../")
29sys.path.append(main_path)
30
31import vulkan
32
33def generate_get_proc_addr_check(name):
34 return " if (!%s || %s[0] != 'v' || %s[1] != 'k')\n" \
35 " return NULL;" % ((name,) * 3)
36
37class Subcommand(object):
38 def __init__(self, argv):
39 self.argv = argv
40 self.headers = vulkan.headers
41 self.protos = vulkan.protos
42
43 def run(self):
44 print(self.generate())
45
Jon Ashburn82974f42015-05-05 09:37:01 -060046 def _requires_special_trampoline_code(self, name):
47 # Dont be cute trying to use a general rule to programmatically populate this list
48 # it just obsfucates what is going on!
Ian Elliott7e40db92015-08-21 15:09:33 -060049 wsi_creates_dispatchable_object = ["CreateSwapchainKHR"]
Chia-I Wu3432a0c2015-10-27 18:04:07 +080050 creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "AllocateCommandBuffers"] + wsi_creates_dispatchable_object
Jon Ashburn82974f42015-05-05 09:37:01 -060051 if name in creates_dispatchable_object:
52 return True
53 else:
54 return False
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060055
Jon Ashburn82974f42015-05-05 09:37:01 -060056 def _is_loader_non_trampoline_entrypoint(self, proto):
Jon Ashburn8d1b0b52015-05-18 13:20:15 -060057 if proto.name in ["GetDeviceProcAddr", "EnumeratePhysicalDevices", "EnumerateLayers", "DbgRegisterMsgCallback", "DbgUnregisterMsgCallback", "DbgSetGlobalOption", "DestroyInstance"]:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060058 return True
59 return not self.is_dispatchable_object_first_param(proto)
60
61
62 def is_dispatchable_object_first_param(self, proto):
63 in_objs = proto.object_in_params()
64 non_dispatch_objs = []
65 param0 = proto.params[0]
66 return (len(in_objs) > 0) and (in_objs[0].ty == param0.ty) and (param0.ty not in non_dispatch_objs)
67
68 def generate(self):
69 copyright = self.generate_copyright()
70 header = self.generate_header()
71 body = self.generate_body()
72 footer = self.generate_footer()
73
74 contents = []
75 if copyright:
76 contents.append(copyright)
77 if header:
78 contents.append(header)
79 if body:
80 contents.append(body)
81 if footer:
82 contents.append(footer)
83
84 return "\n\n".join(contents)
85
86 def generate_copyright(self):
87 return """/* THIS FILE IS GENERATED. DO NOT EDIT. */
88
89/*
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060090 *
Courtney Goeltzenleuchterfcbe16f2015-10-29 13:50:34 -060091 * Copyright (C) 2015 Valve Corporation
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060092 *
93 * Permission is hereby granted, free of charge, to any person obtaining a
94 * copy of this software and associated documentation files (the "Software"),
95 * to deal in the Software without restriction, including without limitation
96 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
97 * and/or sell copies of the Software, and to permit persons to whom the
98 * Software is furnished to do so, subject to the following conditions:
99 *
100 * The above copyright notice and this permission notice shall be included
101 * in all copies or substantial portions of the Software.
102 *
103 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
104 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
105 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
106 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
107 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
108 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
109 * DEALINGS IN THE SOFTWARE.
110 */"""
111
112 def generate_header(self):
113 return "\n".join(["#include <" + h + ">" for h in self.headers])
114
115 def generate_body(self):
116 pass
117
118 def generate_footer(self):
119 pass
120
121class LoaderEntrypointsSubcommand(Subcommand):
122 def generate_header(self):
123 return "#include \"loader.h\""
124
125 def _generate_object_setup(self, proto):
126 method = "loader_init_dispatch"
127 cond = "res == VK_SUCCESS"
Jon Ashburn82974f42015-05-05 09:37:01 -0600128 setup = []
129
130 if not self._requires_special_trampoline_code(proto.name):
131 return setup
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600132
133 if "Get" in proto.name:
134 method = "loader_set_dispatch"
135
Ian Elliott7e40db92015-08-21 15:09:33 -0600136 if proto.name == "GetSwapchainInfoKHR":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600137 ptype = proto.params[-3].name
138 psize = proto.params[-2].name
139 pdata = proto.params[-1].name
Ian Elliott7e40db92015-08-21 15:09:33 -0600140 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_KHR && "
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600141 "%s && %s" % (ptype, pdata, cond))
Ian Elliott7e40db92015-08-21 15:09:33 -0600142 setup.append("VkSwapchainImageInfoKHR *info = %s;" % pdata)
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600143 setup.append("size_t count = *%s / sizeof(*info), i;" % psize)
144 setup.append("for (i = 0; i < count; i++) {")
145 setup.append(" %s(info[i].image, disp);" % method)
146 setup.append(" %s(info[i].memory, disp);" % method)
147 setup.append("}")
Jon Ashburn82974f42015-05-05 09:37:01 -0600148 else:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600149 obj_params = proto.object_out_params()
150 for param in obj_params:
151 setup.append("%s(*%s, disp);" % (method, param.name))
152
153 if setup:
154 joined = "\n ".join(setup)
155 setup = []
156 setup.append(" if (%s) {" % cond)
157 setup.append(" " + joined)
158 setup.append(" }")
159
160 return "\n".join(setup)
161
162 def _generate_loader_dispatch_entrypoints(self, qual=""):
163 if qual:
164 qual += " "
165
166 funcs = []
167 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600168 if self._is_loader_non_trampoline_entrypoint(proto):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600169 continue
170 func = []
171
172 obj_setup = self._generate_object_setup(proto)
173
174 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI"))
175 func.append("{")
176
177 # declare local variables
178 func.append(" const VkLayerDispatchTable *disp;")
179 if proto.ret != 'void' and obj_setup:
180 func.append(" VkResult res;")
181 func.append("")
182
183 # get dispatch table
184 func.append(" disp = loader_get_dispatch(%s);" %
185 proto.params[0].name)
186 func.append("")
187
188 # dispatch!
189 dispatch = "disp->%s;" % proto.c_call()
190 if proto.ret == 'void':
191 func.append(" " + dispatch)
192 elif not obj_setup:
193 func.append(" return " + dispatch)
194 else:
195 func.append(" res = " + dispatch)
196 func.append(obj_setup)
197 func.append("")
198 func.append(" return res;")
199
200 func.append("}")
201
202 funcs.append("\n".join(func))
203
204 return "\n\n".join(funcs)
205
206 def generate_body(self):
207 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")]
208
209 return "\n\n".join(body)
210
211class DispatchTableOpsSubcommand(Subcommand):
212 def run(self):
213 if len(self.argv) != 1:
214 print("DispatchTableOpsSubcommand: <prefix> unspecified")
215 return
216
217 self.prefix = self.argv[0]
218 super().run()
219
220 def generate_header(self):
221 return "\n".join(["#include <vulkan.h>",
222 "#include <vkLayer.h>",
223 "#include <string.h>",
224 "#include \"loader_platform.h\""])
225
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600226 def _generate_init(self, type):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600227 stmts = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600228 func = []
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600229 if type == "device":
230 for proto in self.protos:
231 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
232 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
233 (proto.name, proto.name, proto.name))
234 else:
235 stmts.append("table->%s = vk%s; /* non-dispatchable */" %
236 (proto.name, proto.name))
237 func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600238 % self.prefix)
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600239 func.append("%s PFN_vkGetDeviceProcAddr gpa,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600240 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600241 func.append("%s VkPhysicalDevice gpu)"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600242 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600243 else:
244 for proto in self.protos:
245 if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice":
246 continue
247 stmts.append("table->%s = vk%s;" % (proto.name, proto.name))
248 func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)"
249 % self.prefix)
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600250 func.append("{")
251 func.append(" %s" % "\n ".join(stmts))
252 func.append("}")
253
254 return "\n".join(func)
255
256 def _generate_lookup(self):
257 lookups = []
258 for proto in self.protos:
259 if self.is_dispatchable_object_first_param(proto):
260 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
261 lookups.append(" return (void *) table->%s;"
262 % (proto.name))
263
264 func = []
265 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
266 % self.prefix)
267 func.append("%s const char *name)"
268 % (" " * len(self.prefix)))
269 func.append("{")
270 func.append(generate_get_proc_addr_check("name"))
271 func.append("")
272 func.append(" name += 2;")
273 func.append(" %s" % "\n ".join(lookups))
274 func.append("")
275 func.append(" return NULL;")
276 func.append("}")
277
278 return "\n".join(func)
279
280 def generate_body(self):
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600281 body = [self._generate_init("device"),
282 self._generate_lookup(),
283 self._generate_init("instance")]
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600284
285 return "\n\n".join(body)
286
287class WinDefFileSubcommand(Subcommand):
288 def run(self):
289 library_exports = {
290 "all": [],
291 }
292
293 if len(self.argv) != 2 or self.argv[1] not in library_exports:
294 print("WinDefFileSubcommand: <library-name> {%s}" %
295 "|".join(library_exports.keys()))
296 return
297
298 self.library = self.argv[0]
299 self.exports = library_exports[self.argv[1]]
300
301 super().run()
302
303 def generate_copyright(self):
304 return """; THIS FILE IS GENERATED. DO NOT EDIT.
305
306;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
307; Vulkan
308;
Courtney Goeltzenleuchterfcbe16f2015-10-29 13:50:34 -0600309; Copyright (C) 2015 Valve Corporation
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600310;
311; Permission is hereby granted, free of charge, to any person obtaining a
312; copy of this software and associated documentation files (the "Software"),
313; to deal in the Software without restriction, including without limitation
314; the rights to use, copy, modify, merge, publish, distribute, sublicense,
315; and/or sell copies of the Software, and to permit persons to whom the
316; Software is furnished to do so, subject to the following conditions:
317;
318; The above copyright notice and this permission notice shall be included
319; in all copies or substantial portions of the Software.
320;
321; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
322; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
323; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
324; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
325; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
326; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
327; DEALINGS IN THE SOFTWARE.
328;;;; End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"""
329
330 def generate_header(self):
331 return "; The following is required on Windows, for exporting symbols from the DLL"
332
333 def generate_body(self):
334 body = []
335
336 body.append("LIBRARY " + self.library)
337 body.append("EXPORTS")
338
339 for proto in self.protos:
340 if self.exports and proto.name not in self.exports:
341 continue
Ian Elliott7e40db92015-08-21 15:09:33 -0600342 if proto.name.endswith("KHR"):
Tony Barbour1d825c72015-06-18 16:29:32 -0600343 continue
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600344 body.append(" vk" + proto.name)
345
346 return "\n".join(body)
347
348class LoaderGetProcAddrSubcommand(Subcommand):
349 def run(self):
350 self.prefix = "vk"
351
352 # we could get the list from argv if wanted
353 self.intercepted = [proto.name for proto in self.protos]
354
355 for proto in self.protos:
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600356 if proto.name == "GetDeviceProcAddr":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600357 self.gpa = proto
358
359 super().run()
360
361 def generate_header(self):
362 return "\n".join(["#include <string.h>"])
363
364 def generate_body(self):
365 lookups = []
366 for proto in self.protos:
367 if proto.name not in self.intercepted:
368 lookups.append("/* no %s%s */" % (self.prefix, proto.name))
369 continue
370
371 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
372 lookups.append(" return (%s) %s%s;" %
373 (self.gpa.ret, self.prefix, proto.name))
374
375 special_lookups = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600376 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600377 if self._is_loader_non_trampoline_entrypoint(proto) or self._requires_special_trampoline_code(proto.name):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600378 special_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
379 special_lookups.append(" return (%s) %s%s;" %
380 (self.gpa.ret, self.prefix, proto.name))
381 else:
382 continue
383 body = []
384 body.append("static inline %s globalGetProcAddr(const char *name)" %
385 self.gpa.ret)
386 body.append("{")
387 body.append(generate_get_proc_addr_check("name"))
388 body.append("")
389 body.append(" name += 2;")
390 body.append(" %s" % "\n ".join(lookups))
391 body.append("")
392 body.append(" return NULL;")
393 body.append("}")
394 body.append("")
395 body.append("static inline void *loader_non_passthrough_gpa(const char *name)")
396 body.append("{")
397 body.append(generate_get_proc_addr_check("name"))
398 body.append("")
399 body.append(" name += 2;")
400 body.append(" %s" % "\n ".join(special_lookups))
401 body.append("")
402 body.append(" return NULL;")
403 body.append("}")
404
405 return "\n".join(body)
406
407def main():
408 subcommands = {
409 "loader-entrypoints": LoaderEntrypointsSubcommand,
410 "dispatch-table-ops": DispatchTableOpsSubcommand,
411 "win-def-file": WinDefFileSubcommand,
412 "loader-get-proc-addr": LoaderGetProcAddrSubcommand,
413 }
414
415 if len(sys.argv) < 2 or sys.argv[1] not in subcommands:
416 print("Usage: %s <subcommand> [options]" % sys.argv[0])
417 print
418 print("Available sucommands are: %s" % " ".join(subcommands))
419 exit(1)
420
421 subcmd = subcommands[sys.argv[1]](sys.argv[2:])
422 subcmd.run()
423
424if __name__ == "__main__":
425 main()