blob: ab6645f8c091f142c0fad06a307d312178594b42 [file] [log] [blame]
Jon Ashburnaef65882015-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
48 def _does_function_create_object(self, proto):
49 out_objs = proto.object_out_params()
50 if proto.name == "ResetFences":
51 return False
52 return out_objs and out_objs[-1] == proto.params[-1]
53
54 def _is_loader_special_case(self, proto):
55 if proto.name in ["GetProcAddr", "EnumeratePhysicalDevices", "EnumerateLayers", "DbgRegisterMsgCallback", "DbgUnregisterMsgCallback", "DbgSetGlobalOption", "DestroyInstance"]:
56 return True
57 return not self.is_dispatchable_object_first_param(proto)
58
59
60 def is_dispatchable_object_first_param(self, proto):
61 in_objs = proto.object_in_params()
62 non_dispatch_objs = []
63 param0 = proto.params[0]
64 return (len(in_objs) > 0) and (in_objs[0].ty == param0.ty) and (param0.ty not in non_dispatch_objs)
65
66 def generate(self):
67 copyright = self.generate_copyright()
68 header = self.generate_header()
69 body = self.generate_body()
70 footer = self.generate_footer()
71
72 contents = []
73 if copyright:
74 contents.append(copyright)
75 if header:
76 contents.append(header)
77 if body:
78 contents.append(body)
79 if footer:
80 contents.append(footer)
81
82 return "\n\n".join(contents)
83
84 def generate_copyright(self):
85 return """/* THIS FILE IS GENERATED. DO NOT EDIT. */
86
87/*
88 * Vulkan
89 *
90 * Copyright (C) 2014 LunarG, Inc.
91 *
92 * Permission is hereby granted, free of charge, to any person obtaining a
93 * copy of this software and associated documentation files (the "Software"),
94 * to deal in the Software without restriction, including without limitation
95 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
96 * and/or sell copies of the Software, and to permit persons to whom the
97 * Software is furnished to do so, subject to the following conditions:
98 *
99 * The above copyright notice and this permission notice shall be included
100 * in all copies or substantial portions of the Software.
101 *
102 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
103 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
104 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
105 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
106 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
107 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
108 * DEALINGS IN THE SOFTWARE.
109 */"""
110
111 def generate_header(self):
112 return "\n".join(["#include <" + h + ">" for h in self.headers])
113
114 def generate_body(self):
115 pass
116
117 def generate_footer(self):
118 pass
119
120class LoaderEntrypointsSubcommand(Subcommand):
121 def generate_header(self):
122 return "#include \"loader.h\""
123
124 def _generate_object_setup(self, proto):
125 method = "loader_init_dispatch"
126 cond = "res == VK_SUCCESS"
127
128 if "Get" in proto.name:
129 method = "loader_set_dispatch"
130
131 setup = []
132
133 if proto.name == "AllocDescriptorSets":
134 psets = proto.params[-2].name
135 pcount = proto.params[-1].name
136 setup.append("uint32_t i;")
137 setup.append("for (i = 0; i < *%s; i++)" % pcount)
138 setup.append(" %s(%s[i], disp);" % (method, psets))
139 elif proto.name == "GetPhysicalDeviceInfo":
140 ptype = proto.params[-3].name
141 psize = proto.params[-2].name
142 pdata = proto.params[-1].name
143 cond = ("%s == VK_PHYSICAL_DEVICE_INFO_TYPE_DISPLAY_PROPERTIES_WSI && "
144 "%s && %s" % (ptype, pdata, cond))
145 setup.append("VkDisplayPropertiesWSI *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].display, disp);" % method)
149 setup.append("}")
150 elif proto.name == "GetSwapChainInfoWSI":
151 ptype = proto.params[-3].name
152 psize = proto.params[-2].name
153 pdata = proto.params[-1].name
154 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_WSI && "
155 "%s && %s" % (ptype, pdata, cond))
156 setup.append("VkSwapChainImageInfoWSI *info = %s;" % pdata)
157 setup.append("size_t count = *%s / sizeof(*info), i;" % psize)
158 setup.append("for (i = 0; i < count; i++) {")
159 setup.append(" %s(info[i].image, disp);" % method)
160 setup.append(" %s(info[i].memory, disp);" % method)
161 setup.append("}")
162 elif proto.name != "ResetFences":
163 obj_params = proto.object_out_params()
164 for param in obj_params:
165 setup.append("%s(*%s, disp);" % (method, param.name))
166
167 if setup:
168 joined = "\n ".join(setup)
169 setup = []
170 setup.append(" if (%s) {" % cond)
171 setup.append(" " + joined)
172 setup.append(" }")
173
174 return "\n".join(setup)
175
176 def _generate_loader_dispatch_entrypoints(self, qual=""):
177 if qual:
178 qual += " "
179
180 funcs = []
181 for proto in self.protos:
182 if self._is_loader_special_case(proto):
183 continue
184 func = []
185
186 obj_setup = self._generate_object_setup(proto)
187
188 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI"))
189 func.append("{")
190
191 # declare local variables
192 func.append(" const VkLayerDispatchTable *disp;")
193 if proto.ret != 'void' and obj_setup:
194 func.append(" VkResult res;")
195 func.append("")
196
197 # get dispatch table
198 func.append(" disp = loader_get_dispatch(%s);" %
199 proto.params[0].name)
200 func.append("")
201
202 # dispatch!
203 dispatch = "disp->%s;" % proto.c_call()
204 if proto.ret == 'void':
205 func.append(" " + dispatch)
206 elif not obj_setup:
207 func.append(" return " + dispatch)
208 else:
209 func.append(" res = " + dispatch)
210 func.append(obj_setup)
211 func.append("")
212 func.append(" return res;")
213
214 func.append("}")
215
216 funcs.append("\n".join(func))
217
218 return "\n\n".join(funcs)
219
220 def generate_body(self):
221 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")]
222
223 return "\n\n".join(body)
224
225class DispatchTableOpsSubcommand(Subcommand):
226 def run(self):
227 if len(self.argv) != 1:
228 print("DispatchTableOpsSubcommand: <prefix> unspecified")
229 return
230
231 self.prefix = self.argv[0]
232 super().run()
233
234 def generate_header(self):
235 return "\n".join(["#include <vulkan.h>",
236 "#include <vkLayer.h>",
237 "#include <string.h>",
238 "#include \"loader_platform.h\""])
239
240 def _generate_init(self):
241 stmts = []
242 for proto in self.protos:
243 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
244 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
245 (proto.name, proto.name, proto.name))
246 else:
247 stmts.append("table->%s = vk%s; /* non-dispatchable */" %
248 (proto.name, proto.name))
249
250 func = []
251 func.append("static inline void %s_initialize_dispatch_table(VkLayerDispatchTable *table,"
252 % self.prefix)
253 func.append("%s PFN_vkGetProcAddr gpa,"
254 % (" " * len(self.prefix)))
255 func.append("%s VkPhysicalDevice gpu)"
256 % (" " * len(self.prefix)))
257 func.append("{")
258 func.append(" %s" % "\n ".join(stmts))
259 func.append("}")
260
261 return "\n".join(func)
262
263 def _generate_lookup(self):
264 lookups = []
265 for proto in self.protos:
266 if self.is_dispatchable_object_first_param(proto):
267 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
268 lookups.append(" return (void *) table->%s;"
269 % (proto.name))
270
271 func = []
272 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
273 % self.prefix)
274 func.append("%s const char *name)"
275 % (" " * len(self.prefix)))
276 func.append("{")
277 func.append(generate_get_proc_addr_check("name"))
278 func.append("")
279 func.append(" name += 2;")
280 func.append(" %s" % "\n ".join(lookups))
281 func.append("")
282 func.append(" return NULL;")
283 func.append("}")
284
285 return "\n".join(func)
286
287 def generate_body(self):
288 body = [self._generate_init(),
289 self._generate_lookup()]
290
291 return "\n\n".join(body)
292
293class WinDefFileSubcommand(Subcommand):
294 def run(self):
295 library_exports = {
296 "all": [],
297 }
298
299 if len(self.argv) != 2 or self.argv[1] not in library_exports:
300 print("WinDefFileSubcommand: <library-name> {%s}" %
301 "|".join(library_exports.keys()))
302 return
303
304 self.library = self.argv[0]
305 self.exports = library_exports[self.argv[1]]
306
307 super().run()
308
309 def generate_copyright(self):
310 return """; THIS FILE IS GENERATED. DO NOT EDIT.
311
312;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
313; Vulkan
314;
315; Copyright (C) 2015 LunarG, Inc.
316;
317; Permission is hereby granted, free of charge, to any person obtaining a
318; copy of this software and associated documentation files (the "Software"),
319; to deal in the Software without restriction, including without limitation
320; the rights to use, copy, modify, merge, publish, distribute, sublicense,
321; and/or sell copies of the Software, and to permit persons to whom the
322; Software is furnished to do so, subject to the following conditions:
323;
324; The above copyright notice and this permission notice shall be included
325; in all copies or substantial portions of the Software.
326;
327; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
328; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
329; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
330; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
331; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
332; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
333; DEALINGS IN THE SOFTWARE.
334;;;; 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
348 body.append(" vk" + proto.name)
349
350 return "\n".join(body)
351
352class LoaderGetProcAddrSubcommand(Subcommand):
353 def run(self):
354 self.prefix = "vk"
355
356 # we could get the list from argv if wanted
357 self.intercepted = [proto.name for proto in self.protos]
358
359 for proto in self.protos:
360 if proto.name == "GetProcAddr":
361 self.gpa = proto
362
363 super().run()
364
365 def generate_header(self):
366 return "\n".join(["#include <string.h>"])
367
368 def generate_body(self):
369 lookups = []
370 for proto in self.protos:
371 if proto.name not in self.intercepted:
372 lookups.append("/* no %s%s */" % (self.prefix, proto.name))
373 continue
374
375 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
376 lookups.append(" return (%s) %s%s;" %
377 (self.gpa.ret, self.prefix, proto.name))
378
379 special_lookups = []
380 # these functions require special trampoline code beyond just the normal create object trampoline code
381 special_names = ["AllocDescriptorSets", "GetMultiDeviceCompatibility"]
382 for proto in self.protos:
383 if self._is_loader_special_case(proto) or self._does_function_create_object(proto) or proto.name in special_names:
384 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()