mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Implement experimental Vulkan backend
This commit is contained in:
317
Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp
Normal file
317
Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp
Normal file
@ -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
|
Reference in New Issue
Block a user