blob: 0e6f32b2c727b0922608c3fd115f0b7d2e7222f1 [file] [log] [blame]
#!/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))