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