blob: eed42dfaba2a038775adb897811abff86f258d2f [file] [log] [blame]
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -06001#!/usr/bin/env python3
2#
3# VK
4#
5# Copyright (C) 2014 LunarG, Inc.
6#
7# Permission is hereby granted, free of charge, to any person obtaining a
8# copy of this software and associated documentation files (the "Software"),
9# to deal in the Software without restriction, including without limitation
10# the rights to use, copy, modify, merge, publish, distribute, sublicense,
11# and/or sell copies of the Software, and to permit persons to whom the
12# Software is furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included
15# in all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS 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. IN NO EVENT SHALL
20# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23# DEALINGS IN THE SOFTWARE.
24#
25
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!
Tony Barbour59a47322015-06-24 16:06:58 -060051 wsi_creates_dispatchable_object = ["CreateSwapChainWSI"]
Jon Ashburn82974f42015-05-05 09:37:01 -060052 creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "CreateCommandBuffer"] + wsi_creates_dispatchable_object
53 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/*
92 * Vulkan
93 *
94 * Copyright (C) 2014 LunarG, Inc.
95 *
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.
113 */"""
114
115 def generate_header(self):
116 return "\n".join(["#include <" + h + ">" for h in self.headers])
117
118 def generate_body(self):
119 pass
120
121 def generate_footer(self):
122 pass
123
124class LoaderEntrypointsSubcommand(Subcommand):
125 def generate_header(self):
126 return "#include \"loader.h\""
127
128 def _generate_object_setup(self, proto):
129 method = "loader_init_dispatch"
130 cond = "res == VK_SUCCESS"
Jon Ashburn82974f42015-05-05 09:37:01 -0600131 setup = []
132
133 if not self._requires_special_trampoline_code(proto.name):
134 return setup
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600135
136 if "Get" in proto.name:
137 method = "loader_set_dispatch"
138
Tony Barbour59a47322015-06-24 16:06:58 -0600139 if proto.name == "GetSwapChainInfoWSI":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600140 ptype = proto.params[-3].name
141 psize = proto.params[-2].name
142 pdata = proto.params[-1].name
143 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_WSI && "
144 "%s && %s" % (ptype, pdata, cond))
145 setup.append("VkSwapChainImageInfoWSI *info = %s;" % pdata)
146 setup.append("size_t count = *%s / sizeof(*info), i;" % psize)
147 setup.append("for (i = 0; i < count; i++) {")
148 setup.append(" %s(info[i].image, disp);" % method)
149 setup.append(" %s(info[i].memory, disp);" % method)
150 setup.append("}")
Jon Ashburn82974f42015-05-05 09:37:01 -0600151 else:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600152 obj_params = proto.object_out_params()
153 for param in obj_params:
154 setup.append("%s(*%s, disp);" % (method, param.name))
155
156 if setup:
157 joined = "\n ".join(setup)
158 setup = []
159 setup.append(" if (%s) {" % cond)
160 setup.append(" " + joined)
161 setup.append(" }")
162
163 return "\n".join(setup)
164
165 def _generate_loader_dispatch_entrypoints(self, qual=""):
166 if qual:
167 qual += " "
168
169 funcs = []
170 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600171 if self._is_loader_non_trampoline_entrypoint(proto):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600172 continue
173 func = []
174
175 obj_setup = self._generate_object_setup(proto)
176
177 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI"))
178 func.append("{")
179
180 # declare local variables
181 func.append(" const VkLayerDispatchTable *disp;")
182 if proto.ret != 'void' and obj_setup:
183 func.append(" VkResult res;")
184 func.append("")
185
186 # get dispatch table
187 func.append(" disp = loader_get_dispatch(%s);" %
188 proto.params[0].name)
189 func.append("")
190
191 # dispatch!
192 dispatch = "disp->%s;" % proto.c_call()
193 if proto.ret == 'void':
194 func.append(" " + dispatch)
195 elif not obj_setup:
196 func.append(" return " + dispatch)
197 else:
198 func.append(" res = " + dispatch)
199 func.append(obj_setup)
200 func.append("")
201 func.append(" return res;")
202
203 func.append("}")
204
205 funcs.append("\n".join(func))
206
207 return "\n\n".join(funcs)
208
209 def generate_body(self):
210 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")]
211
212 return "\n\n".join(body)
213
214class DispatchTableOpsSubcommand(Subcommand):
215 def run(self):
216 if len(self.argv) != 1:
217 print("DispatchTableOpsSubcommand: <prefix> unspecified")
218 return
219
220 self.prefix = self.argv[0]
221 super().run()
222
223 def generate_header(self):
224 return "\n".join(["#include <vulkan.h>",
225 "#include <vkLayer.h>",
226 "#include <string.h>",
227 "#include \"loader_platform.h\""])
228
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600229 def _generate_init(self, type):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600230 stmts = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600231 func = []
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600232 if type == "device":
233 for proto in self.protos:
234 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
235 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
236 (proto.name, proto.name, proto.name))
237 else:
238 stmts.append("table->%s = vk%s; /* non-dispatchable */" %
239 (proto.name, proto.name))
240 func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600241 % self.prefix)
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600242 func.append("%s PFN_vkGetDeviceProcAddr gpa,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600243 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600244 func.append("%s VkPhysicalDevice gpu)"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600245 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600246 else:
247 for proto in self.protos:
248 if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice":
249 continue
250 stmts.append("table->%s = vk%s;" % (proto.name, proto.name))
251 func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)"
252 % self.prefix)
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600253 func.append("{")
254 func.append(" %s" % "\n ".join(stmts))
255 func.append("}")
256
257 return "\n".join(func)
258
259 def _generate_lookup(self):
260 lookups = []
261 for proto in self.protos:
262 if self.is_dispatchable_object_first_param(proto):
263 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
264 lookups.append(" return (void *) table->%s;"
265 % (proto.name))
266
267 func = []
268 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
269 % self.prefix)
270 func.append("%s const char *name)"
271 % (" " * len(self.prefix)))
272 func.append("{")
273 func.append(generate_get_proc_addr_check("name"))
274 func.append("")
275 func.append(" name += 2;")
276 func.append(" %s" % "\n ".join(lookups))
277 func.append("")
278 func.append(" return NULL;")
279 func.append("}")
280
281 return "\n".join(func)
282
283 def generate_body(self):
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600284 body = [self._generate_init("device"),
285 self._generate_lookup(),
286 self._generate_init("instance")]
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600287
288 return "\n\n".join(body)
289
290class WinDefFileSubcommand(Subcommand):
291 def run(self):
292 library_exports = {
293 "all": [],
294 }
295
296 if len(self.argv) != 2 or self.argv[1] not in library_exports:
297 print("WinDefFileSubcommand: <library-name> {%s}" %
298 "|".join(library_exports.keys()))
299 return
300
301 self.library = self.argv[0]
302 self.exports = library_exports[self.argv[1]]
303
304 super().run()
305
306 def generate_copyright(self):
307 return """; THIS FILE IS GENERATED. DO NOT EDIT.
308
309;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
310; Vulkan
311;
312; Copyright (C) 2015 LunarG, Inc.
313;
314; Permission is hereby granted, free of charge, to any person obtaining a
315; copy of this software and associated documentation files (the "Software"),
316; to deal in the Software without restriction, including without limitation
317; the rights to use, copy, modify, merge, publish, distribute, sublicense,
318; and/or sell copies of the Software, and to permit persons to whom the
319; Software is furnished to do so, subject to the following conditions:
320;
321; The above copyright notice and this permission notice shall be included
322; in all copies or substantial portions of the Software.
323;
324; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
325; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
326; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
327; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
328; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
329; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
330; DEALINGS IN THE SOFTWARE.
331;;;; End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"""
332
333 def generate_header(self):
334 return "; The following is required on Windows, for exporting symbols from the DLL"
335
336 def generate_body(self):
337 body = []
338
339 body.append("LIBRARY " + self.library)
340 body.append("EXPORTS")
341
342 for proto in self.protos:
343 if self.exports and proto.name not in self.exports:
344 continue
Tony Barbour1d825c72015-06-18 16:29:32 -0600345 if proto.name.endswith("WSI"):
346 continue
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600347 body.append(" vk" + proto.name)
348
349 return "\n".join(body)
350
351class LoaderGetProcAddrSubcommand(Subcommand):
352 def run(self):
353 self.prefix = "vk"
354
355 # we could get the list from argv if wanted
356 self.intercepted = [proto.name for proto in self.protos]
357
358 for proto in self.protos:
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600359 if proto.name == "GetDeviceProcAddr":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600360 self.gpa = proto
361
362 super().run()
363
364 def generate_header(self):
365 return "\n".join(["#include <string.h>"])
366
367 def generate_body(self):
368 lookups = []
369 for proto in self.protos:
370 if proto.name not in self.intercepted:
371 lookups.append("/* no %s%s */" % (self.prefix, proto.name))
372 continue
373
374 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
375 lookups.append(" return (%s) %s%s;" %
376 (self.gpa.ret, self.prefix, proto.name))
377
378 special_lookups = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600379 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600380 if self._is_loader_non_trampoline_entrypoint(proto) or self._requires_special_trampoline_code(proto.name):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600381 special_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
382 special_lookups.append(" return (%s) %s%s;" %
383 (self.gpa.ret, self.prefix, proto.name))
384 else:
385 continue
386 body = []
387 body.append("static inline %s globalGetProcAddr(const char *name)" %
388 self.gpa.ret)
389 body.append("{")
390 body.append(generate_get_proc_addr_check("name"))
391 body.append("")
392 body.append(" name += 2;")
393 body.append(" %s" % "\n ".join(lookups))
394 body.append("")
395 body.append(" return NULL;")
396 body.append("}")
397 body.append("")
398 body.append("static inline void *loader_non_passthrough_gpa(const char *name)")
399 body.append("{")
400 body.append(generate_get_proc_addr_check("name"))
401 body.append("")
402 body.append(" name += 2;")
403 body.append(" %s" % "\n ".join(special_lookups))
404 body.append("")
405 body.append(" return NULL;")
406 body.append("}")
407
408 return "\n".join(body)
409
410def main():
411 subcommands = {
412 "loader-entrypoints": LoaderEntrypointsSubcommand,
413 "dispatch-table-ops": DispatchTableOpsSubcommand,
414 "win-def-file": WinDefFileSubcommand,
415 "loader-get-proc-addr": LoaderGetProcAddrSubcommand,
416 }
417
418 if len(sys.argv) < 2 or sys.argv[1] not in subcommands:
419 print("Usage: %s <subcommand> [options]" % sys.argv[0])
420 print
421 print("Available sucommands are: %s" % " ".join(subcommands))
422 exit(1)
423
424 subcmd = subcommands[sys.argv[1]](sys.argv[2:])
425 subcmd.run()
426
427if __name__ == "__main__":
428 main()