summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-08-13 22:57:50 +1000
committerStenzek <stenzek@gmail.com>2016-10-01 02:40:01 +1000
commit77a128ab8707659a531029a21a2e874108eedf38 (patch)
tree7b4a52ee61208eec2bf44a7745f3dac3bed1bafb /Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp
parentfdd954e7e757b4601eb6d2636e12623aef63c9b4 (diff)
Implement experimental Vulkan backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp317
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