diff options
| author | Stenzek <stenzek@gmail.com> | 2016-08-13 22:57:50 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2016-10-01 02:40:01 +1000 |
| commit | 77a128ab8707659a531029a21a2e874108eedf38 (patch) | |
| tree | 7b4a52ee61208eec2bf44a7745f3dac3bed1bafb /Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp | |
| parent | fdd954e7e757b4601eb6d2636e12623aef63c9b4 (diff) | |
Implement experimental Vulkan backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp b/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp new file mode 100644 index 0000000000..f72d43d62e --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp @@ -0,0 +1,317 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <atomic> +#include <cstdarg> + +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Common/StringUtil.h" + +#include "VideoBackends/Vulkan/VulkanLoader.h" + +#if defined(VK_USE_PLATFORM_WIN32_KHR) +#include <Windows.h> +#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ + defined(VK_USE_PLATFORM_ANDROID_KHR) +#include <dlfcn.h> +#endif + +#define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name name; +#define VULKAN_INSTANCE_ENTRY_POINT(name, required) PFN_##name name; +#define VULKAN_DEVICE_ENTRY_POINT(name, required) PFN_##name name; +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_DEVICE_ENTRY_POINT +#undef VULKAN_INSTANCE_ENTRY_POINT +#undef VULKAN_MODULE_ENTRY_POINT + +namespace Vulkan +{ +static void ResetVulkanLibraryFunctionPointers() +{ +#define VULKAN_MODULE_ENTRY_POINT(name, required) name = nullptr; +#define VULKAN_INSTANCE_ENTRY_POINT(name, required) name = nullptr; +#define VULKAN_DEVICE_ENTRY_POINT(name, required) name = nullptr; +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_DEVICE_ENTRY_POINT +#undef VULKAN_INSTANCE_ENTRY_POINT +#undef VULKAN_MODULE_ENTRY_POINT +} + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + +static HMODULE vulkan_module; +static std::atomic_int vulkan_module_ref_count = {0}; + +bool LoadVulkanLibrary() +{ + // Not thread safe if a second thread calls the loader whilst the first is still in-progress. + if (vulkan_module) + { + vulkan_module_ref_count++; + return true; + } + + vulkan_module = LoadLibraryA("vulkan-1.dll"); + if (!vulkan_module) + { + ERROR_LOG(VIDEO, "Failed to load vulkan-1.dll"); + return false; + } + + bool required_functions_missing = false; + auto LoadFunction = [&](FARPROC* func_ptr, const char* name, bool is_required) { + *func_ptr = GetProcAddress(vulkan_module, name); + if (!(*func_ptr) && is_required) + { + ERROR_LOG(VIDEO, "Vulkan: Failed to load required module function %s", name); + required_functions_missing = true; + } + }; + +#define VULKAN_MODULE_ENTRY_POINT(name, required) \ + LoadFunction(reinterpret_cast<FARPROC*>(&name), #name, required); +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_MODULE_ENTRY_POINT + + if (required_functions_missing) + { + ResetVulkanLibraryFunctionPointers(); + FreeLibrary(vulkan_module); + vulkan_module = nullptr; + return false; + } + + vulkan_module_ref_count++; + return true; +} + +void UnloadVulkanLibrary() +{ + if ((--vulkan_module_ref_count) > 0) + return; + + ResetVulkanLibraryFunctionPointers(); + FreeLibrary(vulkan_module); + vulkan_module = nullptr; +} + +#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ + defined(VK_USE_PLATFORM_ANDROID_KHR) + +static void* vulkan_module; +static std::atomic_int vulkan_module_ref_count = {0}; + +bool LoadVulkanLibrary() +{ + // Not thread safe if a second thread calls the loader whilst the first is still in-progress. + if (vulkan_module) + { + vulkan_module_ref_count++; + return true; + } + + // Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so. + static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"}; + + for (size_t i = 0; i < ArraySize(search_lib_names); i++) + { + vulkan_module = dlopen(search_lib_names[i], RTLD_NOW); + if (vulkan_module) + break; + } + + if (!vulkan_module) + { + ERROR_LOG(VIDEO, "Failed to load or locate libvulkan.so"); + return false; + } + + bool required_functions_missing = false; + auto LoadFunction = [&](void** func_ptr, const char* name, bool is_required) { + *func_ptr = dlsym(vulkan_module, name); + if (!(*func_ptr) && is_required) + { + ERROR_LOG(VIDEO, "Vulkan: Failed to load required module function %s", name); + required_functions_missing = true; + } + }; + +#define VULKAN_MODULE_ENTRY_POINT(name, required) \ + LoadFunction(reinterpret_cast<void**>(&name), #name, required); +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_MODULE_ENTRY_POINT + + if (required_functions_missing) + { + ResetVulkanLibraryFunctionPointers(); + dlclose(vulkan_module); + vulkan_module = nullptr; + return false; + } + + vulkan_module_ref_count++; + return true; +} + +void UnloadVulkanLibrary() +{ + if ((--vulkan_module_ref_count) > 0) + return; + + ResetVulkanLibraryFunctionPointers(); + dlclose(vulkan_module); + vulkan_module = nullptr; +} +#else + +//#warning Unknown platform, not compiling loader. + +bool LoadVulkanLibrary() +{ + return false; +} + +void UnloadVulkanLibrary() +{ + ResetVulkanLibraryFunctionPointers(); +} + +#endif + +bool LoadVulkanInstanceFunctions(VkInstance instance) +{ + bool required_functions_missing = false; + auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) { + *func_ptr = vkGetInstanceProcAddr(instance, name); + if (!(*func_ptr) && is_required) + { + ERROR_LOG(VIDEO, "Vulkan: Failed to load required instance function %s", name); + required_functions_missing = true; + } + }; + +#define VULKAN_INSTANCE_ENTRY_POINT(name, required) \ + LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required); +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_INSTANCE_ENTRY_POINT + + return !required_functions_missing; +} + +bool LoadVulkanDeviceFunctions(VkDevice device) +{ + bool required_functions_missing = false; + auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) { + *func_ptr = vkGetDeviceProcAddr(device, name); + if (!(*func_ptr) && is_required) + { + ERROR_LOG(VIDEO, "Vulkan: Failed to load required device function %s", name); + required_functions_missing = true; + } + }; + +#define VULKAN_DEVICE_ENTRY_POINT(name, required) \ + LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required); +#include "VideoBackends/Vulkan/VulkanEntryPoints.inl" +#undef VULKAN_DEVICE_ENTRY_POINT + + return !required_functions_missing; +} + +const char* VkResultToString(VkResult res) +{ + switch (res) + { + case VK_SUCCESS: + return "VK_SUCCESS"; + + case VK_NOT_READY: + return "VK_NOT_READY"; + + case VK_TIMEOUT: + return "VK_TIMEOUT"; + + case VK_EVENT_SET: + return "VK_EVENT_SET"; + + case VK_EVENT_RESET: + return "VK_EVENT_RESET"; + + case VK_INCOMPLETE: + return "VK_INCOMPLETE"; + + case VK_ERROR_OUT_OF_HOST_MEMORY: + return "VK_ERROR_OUT_OF_HOST_MEMORY"; + + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; + + case VK_ERROR_INITIALIZATION_FAILED: + return "VK_ERROR_INITIALIZATION_FAILED"; + + case VK_ERROR_DEVICE_LOST: + return "VK_ERROR_DEVICE_LOST"; + + case VK_ERROR_MEMORY_MAP_FAILED: + return "VK_ERROR_MEMORY_MAP_FAILED"; + + case VK_ERROR_LAYER_NOT_PRESENT: + return "VK_ERROR_LAYER_NOT_PRESENT"; + + case VK_ERROR_EXTENSION_NOT_PRESENT: + return "VK_ERROR_EXTENSION_NOT_PRESENT"; + + case VK_ERROR_FEATURE_NOT_PRESENT: + return "VK_ERROR_FEATURE_NOT_PRESENT"; + + case VK_ERROR_INCOMPATIBLE_DRIVER: + return "VK_ERROR_INCOMPATIBLE_DRIVER"; + + case VK_ERROR_TOO_MANY_OBJECTS: + return "VK_ERROR_TOO_MANY_OBJECTS"; + + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return "VK_ERROR_FORMAT_NOT_SUPPORTED"; + + case VK_ERROR_SURFACE_LOST_KHR: + return "VK_ERROR_SURFACE_LOST_KHR"; + + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; + + case VK_SUBOPTIMAL_KHR: + return "VK_SUBOPTIMAL_KHR"; + + case VK_ERROR_OUT_OF_DATE_KHR: + return "VK_ERROR_OUT_OF_DATE_KHR"; + + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; + + case VK_ERROR_VALIDATION_FAILED_EXT: + return "VK_ERROR_VALIDATION_FAILED_EXT"; + + case VK_ERROR_INVALID_SHADER_NV: + return "VK_ERROR_INVALID_SHADER_NV"; + + default: + return "UNKNOWN_VK_RESULT"; + } +} + +void LogVulkanResult(int level, const char* func_name, VkResult res, const char* msg, ...) +{ + std::va_list ap; + va_start(ap, msg); + std::string real_msg = StringFromFormatV(msg, ap); + va_end(ap); + + real_msg = StringFromFormat("(%s) %s (%d: %s)", func_name, real_msg.c_str(), + static_cast<int>(res), VkResultToString(res)); + + GENERIC_LOG(LogTypes::VIDEO, static_cast<LogTypes::LOG_LEVELS>(level), "%s", real_msg.c_str()); +} + +} // namespace Vulkan |
