| #!/usr/bin/python3 -i |
| # |
| # Copyright (c) 2015-2024 The Khronos Group Inc. |
| # Copyright (c) 2015-2024 Valve Corporation |
| # Copyright (c) 2015-2024 LunarG, Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| from generators.base_generator import BaseGenerator |
| from generators.generator_utils import PlatformGuardHelper |
| |
| class FunctionPointersOutputGenerator(BaseGenerator): |
| def __init__(self): |
| BaseGenerator.__init__(self) |
| |
| def generate(self): |
| self.write(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT *** |
| // See {os.path.basename(__file__)} for modifications |
| |
| /*************************************************************************** |
| * |
| * Copyright (c) 2015-2024 The Khronos Group Inc. |
| * Copyright (c) 2015-2024 Valve Corporation |
| * Copyright (c) 2015-2024 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| ****************************************************************************/\n''') |
| self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore |
| self.write('// clang-format off') |
| |
| if self.filename == 'vk_function_pointers.h': |
| self.generateHeader() |
| elif self.filename == 'vk_function_pointers.cpp': |
| self.generateSource() |
| else: |
| self.write(f'\nFile name {self.filename} has no code to generate\n') |
| |
| self.write('// clang-format on') |
| self.write('// NOLINTEND') # Wrap for clang-tidy to ignore |
| |
| def generateHeader(self): |
| out = [] |
| out.append(''' |
| #pragma once |
| #include <vulkan/vulkan.h> |
| |
| #ifdef _WIN32 |
| /* Windows-specific common code: */ |
| // WinBase.h defines CreateSemaphore and synchapi.h defines CreateEvent |
| // undefine them to avoid conflicts with VkLayerDispatchTable struct members. |
| #ifdef CreateSemaphore |
| #undef CreateSemaphore |
| #endif |
| #ifdef CreateEvent |
| #undef CreateEvent |
| #endif |
| #endif |
| |
| namespace vk { |
| ''') |
| guard_helper = PlatformGuardHelper() |
| for command in self.vk.commands.values(): |
| out.extend(guard_helper.add_guard(command.protect)) |
| out.append(f'extern PFN_{command.name} {command.name[2:]};\n') |
| out.extend(guard_helper.add_guard(None)) |
| out.append(''' |
| void InitCore(const char *api_name); |
| void InitExtensionFromCore(const char* extension_name); |
| void InitInstanceExtension(VkInstance instance, const char* extension_name); |
| void InitDeviceExtension(VkInstance instance, VkDevice device, const char* extension_name); |
| void ResetAllExtensions(); |
| |
| } // namespace vk''') |
| self.write("".join(out)) |
| |
| def generateSource(self): |
| out = [] |
| out.append(''' |
| #include "vk_function_pointers.h" |
| #include <cassert> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <functional> |
| #include <string> |
| #include "containers/custom_containers.h" |
| |
| #ifdef _WIN32 |
| // Dynamic Loading: |
| typedef HMODULE dl_handle; |
| static dl_handle open_library(const char *lib_path) { |
| // Try loading the library the original way first. |
| dl_handle lib_handle = LoadLibrary(lib_path); |
| if (lib_handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) { |
| // If that failed, then try loading it with broader search folders. |
| lib_handle = LoadLibraryEx(lib_path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); |
| } |
| return lib_handle; |
| } |
| static char *open_library_error(const char *libPath) { |
| static char errorMsg[164]; |
| (void)snprintf(errorMsg, 163, "Failed to open dynamic library \\\"%s\\\" with error %lu", libPath, GetLastError()); |
| return errorMsg; |
| } |
| static void *get_proc_address(dl_handle library, const char *name) { |
| assert(library); |
| assert(name); |
| return (void *)GetProcAddress(library, name); |
| } |
| #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__QNX__) |
| |
| #include <dlfcn.h> |
| |
| typedef void *dl_handle; |
| static inline dl_handle open_library(const char *libPath) { |
| // When loading the library, we use RTLD_LAZY so that not all symbols have to be |
| // resolved at this time (which improves performance). Note that if not all symbols |
| // can be resolved, this could cause crashes later. Use the LD_BIND_NOW environment |
| // variable to force all symbols to be resolved here. |
| return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL); |
| } |
| static inline const char *open_library_error(const char * /*libPath*/) { return dlerror(); } |
| static inline void *get_proc_address(dl_handle library, const char *name) { |
| assert(library); |
| assert(name); |
| return dlsym(library, name); |
| } |
| #else |
| #error Dynamic library functions must be defined for this OS. |
| #endif |
| |
| namespace vk { |
| ''') |
| guard_helper = PlatformGuardHelper() |
| for command in self.vk.commands.values(): |
| out.extend(guard_helper.add_guard(command.protect)) |
| out.append(f'PFN_{command.name} {command.name[2:]};\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| void InitCore(const char *api_name) { |
| |
| #if defined(WIN32) |
| std::string filename = std::string(api_name) + "-1.dll"; |
| auto lib_handle = open_library(filename.c_str()); |
| #elif(__APPLE__) |
| std::string filename = std::string("lib") + api_name + ".dylib"; |
| auto lib_handle = open_library(filename.c_str()); |
| #else |
| std::string filename = std::string("lib") + api_name + ".so"; |
| auto lib_handle = open_library(filename.c_str()); |
| if (!lib_handle) { |
| filename = std::string("lib") + api_name + ".so.1"; |
| lib_handle = open_library(filename.c_str()); |
| } |
| #endif |
| |
| if (lib_handle == nullptr) { |
| printf("%s\\n", open_library_error(filename.c_str())); |
| exit(1); |
| } |
| ''') |
| out.extend([f' {x.name[2:]} = reinterpret_cast<PFN_{x.name}>(get_proc_address(lib_handle, "{x.name}"));\n' for x in self.vk.commands.values() if not x.extensions]) |
| out.append('}') |
| |
| out.append(''' |
| void InitExtensionFromCore(const char* extension_name) { |
| static const vvl::unordered_map<std::string, std::function<void()>> initializers = { |
| ''') |
| for extension in [x for x in self.vk.extensions.values() if x.commands and x.promotedTo]: |
| out.extend(guard_helper.add_guard(extension.protect)) |
| out.append(' {\n') |
| out.append(f' "{extension.name}", []() {{\n') |
| for command in [x for x in extension.commands]: |
| if command.alias is not None and not self.vk.commands[command.alias].extensions: |
| out.append(f' {command.name[2:]} = {command.alias[2:]};\n') |
| out.append(' }\n') |
| out.append(' },\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| }; |
| |
| if (auto it = initializers.find(extension_name); it != initializers.end()) |
| (it->second)(); |
| } |
| ''') |
| |
| out.append(''' |
| void InitInstanceExtension(VkInstance instance, const char* extension_name) { |
| assert(instance); |
| static const vvl::unordered_map<std::string, std::function<void(VkInstance)>> initializers = { |
| ''') |
| for extension in [x for x in self.vk.extensions.values() if x.instance and x.commands]: |
| out.extend(guard_helper.add_guard(extension.protect)) |
| out.append(' {\n') |
| out.append(f' "{extension.name}", [](VkInstance instance) {{\n') |
| for command in [x for x in extension.commands]: |
| out.append(f' {command.name[2:]} = reinterpret_cast<PFN_{command.name}>(GetInstanceProcAddr(instance, "{command.name}"));\n') |
| out.append(' }\n') |
| out.append(' },\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| }; |
| |
| if (auto it = initializers.find(extension_name); it != initializers.end()) |
| (it->second)(instance); |
| } |
| ''') |
| |
| out.append(''' |
| void InitDeviceExtension(VkInstance instance, VkDevice device, const char* extension_name) { |
| static const vvl::unordered_map<std::string, std::function<void(VkInstance, VkDevice)>> initializers = { |
| ''') |
| for extension in [x for x in self.vk.extensions.values() if x.device and x.commands]: |
| out.extend(guard_helper.add_guard(extension.protect)) |
| out.append(' {\n') |
| instanceCommand = [x for x in extension.commands if x.instance] |
| deviceCommand = [x for x in extension.commands if x.device] |
| out.append(f' "{extension.name}", [](VkInstance {"instance" if instanceCommand else ""}, VkDevice {"device" if deviceCommand else ""}) {{\n') |
| out.extend([f' {command.name[2:]} = reinterpret_cast<PFN_{command.name}>(GetDeviceProcAddr(device, "{command.name}"));\n' for command in deviceCommand]) |
| out.extend([f' {command.name[2:]} = reinterpret_cast<PFN_{command.name}>(GetInstanceProcAddr(instance, "{command.name}"));\n' for command in instanceCommand]) |
| out.append(' }\n') |
| out.append(' },\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append(''' |
| }; |
| |
| if (auto it = initializers.find(extension_name); it != initializers.end()) |
| (it->second)(instance, device); |
| } |
| ''') |
| |
| out.append('void ResetAllExtensions() {\n') |
| for command in [x for x in self.vk.commands.values() if x.extensions]: |
| out.extend(guard_helper.add_guard(command.protect)) |
| out.append(f' {command.name[2:]} = nullptr;\n') |
| out.extend(guard_helper.add_guard(None)) |
| |
| out.append('}\n') |
| |
| out.append('} // namespace vk') |
| self.write(''.join(out)) |