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# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11#     http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# Author: Jon Ashburn <jon@lunarg.com>
20#
21
22import os, sys
23
24# add main repo directory so vulkan.py can be imported. This needs to be a complete path.
25ld_path = os.path.dirname(os.path.abspath(__file__))
26main_path = os.path.abspath(ld_path + "/../")
27sys.path.append(main_path)
28
29import vulkan
30
31def generate_get_proc_addr_check(name):
32    return "    if (!%s || %s[0] != 'v' || %s[1] != 'k')\n" \
33           "        return NULL;" % ((name,) * 3)
34
35class Subcommand(object):
36    def __init__(self, argv):
37        self.argv = argv
38        self.headers = vulkan.headers
39        self.protos = vulkan.protos
40
41    def run(self):
42        print(self.generate())
43
44    def _requires_special_trampoline_code(self, name):
45        # Don't be cute trying to use a general rule to programmatically populate this list
46        # it just obsfucates what is going on!
47        wsi_creates_dispatchable_object = ["CreateSwapchainKHR"]
48        creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "AllocateCommandBuffers"] + wsi_creates_dispatchable_object
49        if name in creates_dispatchable_object:
50            return True
51        else:
52           return False
53
54    def _is_loader_non_trampoline_entrypoint(self, proto):
55        if proto.name in ["GetDeviceProcAddr", "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 * Copyright (c) 2015-2016 The Khronos Group Inc.
89 * Copyright (c) 2015-2016 Valve Corporation
90 * Copyright (c) 2015-2016 LunarG, Inc.
91 *
92 * Licensed under the Apache License, Version 2.0 (the "License");
93 * you may not use this file except in compliance with the License.
94 * You may obtain a copy of the License at
95 *
96 *     http://www.apache.org/licenses/LICENSE-2.0
97 *
98 * Unless required by applicable law or agreed to in writing, software
99 * distributed under the License is distributed on an "AS IS" BASIS,
100 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101 * See the License for the specific language governing permissions and
102 * limitations under the License.
103 *
104 * Author: Jon Ashburn <jon@lunarg.com>
105 * Author: Chia-I Wu <olv@lunarg.com>
106 * Author: Courtney Goeltzenleuchter <courtney@lunarg.com>
107 */"""
108
109    def generate_header(self):
110        return "\n".join(["#include <" + h + ">" for h in self.headers])
111
112    def generate_body(self):
113        pass
114
115    def generate_footer(self):
116        pass
117
118class DispatchTableOpsSubcommand(Subcommand):
119    def run(self):
120        if len(self.argv) != 1:
121            print("DispatchTableOpsSubcommand: <prefix> unspecified")
122            return
123
124        self.prefix = self.argv[0]
125        super().run()
126
127    def generate_header(self):
128        return "\n".join(["#include <vulkan/vulkan.h>",
129                          "#include <vkLayer.h>",
130                          "#include <string.h>",
131                          "#include \"loader_platform.h\""])
132
133    def _generate_init(self, type):
134        stmts = []
135        func = []
136        if type == "device":
137            for proto in self.protos:
138                if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
139                    stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
140                        (proto.name, proto.name, proto.name))
141                else:
142                    stmts.append("table->%s = vk%s; /* non-dispatchable */" %
143                             (proto.name, proto.name))
144            func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table,"
145                % self.prefix)
146            func.append("%s                                              PFN_vkGetDeviceProcAddr gpa,"
147                % (" " * len(self.prefix)))
148            func.append("%s                                              VkPhysicalDevice gpu)"
149                % (" " * len(self.prefix)))
150        else:
151            for proto in self.protos:
152                if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice":
153                    continue
154                stmts.append("table->%s = vk%s;" % (proto.name, proto.name))
155            func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)"
156                % self.prefix)
157        func.append("{")
158        func.append("    %s" % "\n    ".join(stmts))
159        func.append("}")
160
161        return "\n".join(func)
162
163    def _generate_lookup(self):
164        lookups = []
165        for proto in self.protos:
166            if self.is_dispatchable_object_first_param(proto):
167                lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
168                lookups.append("    return (void *) table->%s;"
169                    % (proto.name))
170
171        func = []
172        func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
173                % self.prefix)
174        func.append("%s                                           const char *name)"
175                % (" " * len(self.prefix)))
176        func.append("{")
177        func.append(generate_get_proc_addr_check("name"))
178        func.append("")
179        func.append("    name += 2;")
180        func.append("    %s" % "\n    ".join(lookups))
181        func.append("")
182        func.append("    return NULL;")
183        func.append("}")
184
185        return "\n".join(func)
186
187    def generate_body(self):
188        body = [self._generate_init("device"),
189                self._generate_lookup(),
190                self._generate_init("instance")]
191
192        return "\n\n".join(body)
193
194class WinDefFileSubcommand(Subcommand):
195    def run(self):
196        library_exports = {
197                "all": [],
198        }
199
200        if len(self.argv) != 2 or self.argv[1] not in library_exports:
201            print("WinDefFileSubcommand: <library-name> {%s}" %
202                    "|".join(library_exports.keys()))
203            return
204
205        self.library = self.argv[0]
206        self.exports = library_exports[self.argv[1]]
207
208        super().run()
209
210    def generate_copyright(self):
211        return """; THIS FILE IS GENERATED.  DO NOT EDIT.
212
213;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
214; Copyright (c) 2015-2016 The Khronos Group Inc.
215; Copyright (c) 2015-2016 Valve Corporation
216; Copyright (c) 2015-2016 LunarG, Inc.
217;
218; Licensed under the Apache License, Version 2.0 (the "License");
219; you may not use this file except in compliance with the License.
220; You may obtain a copy of the License at
221;
222;     http://www.apache.org/licenses/LICENSE-2.0
223;
224; Unless required by applicable law or agreed to in writing, software
225; distributed under the License is distributed on an "AS IS" BASIS,
226; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
227; See the License for the specific language governing permissions and
228; limitations under the License.
229;
230;
231;  Author: Jon Ashburn <jon@lunarg.com>
232;;;;  End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"""
233
234    def generate_header(self):
235        return "; The following is required on Windows, for exporting symbols from the DLL"
236
237    def generate_body(self):
238        body = []
239
240        body.append("LIBRARY " + self.library)
241        body.append("EXPORTS")
242
243        for proto in self.protos:
244            if self.exports and proto.name not in self.exports:
245                continue
246#           This was intended to reject WSI calls, but actually rejects ALL extensions
247#           TODO:  Make this WSI-extension specific
248#           if proto.name.endswith("KHR"):
249#               continue
250            body.append("   vk" + proto.name)
251
252        return "\n".join(body)
253
254def main():
255
256    wsi = {
257            "Win32",
258            "Android",
259            "Xcb",
260            "Xlib",
261            "Wayland",
262            "Mir"
263    }
264
265    subcommands = {
266            "dispatch-table-ops": DispatchTableOpsSubcommand,
267            "win-def-file": WinDefFileSubcommand,
268    }
269
270    if len(sys.argv) < 3 or sys.argv[1] not in wsi or sys.argv[2] not in subcommands:
271        print("Usage: %s <wsi> <subcommand> [options]" % sys.argv[0])
272        print
273        print("Available wsi (displayservers) are: %s" % " ".join(wsi))
274        print("Available subcommands are: %s" % " ".join(subcommands))
275        exit(1)
276
277    subcmd = subcommands[sys.argv[2]](sys.argv[3:])
278    subcmd.run()
279
280if __name__ == "__main__":
281    main()
282