Vulkan: Support macOS via MoltenVK

The path to the MoltenVK library can be specified by the
LIBMOLTENVK_PATH environment variable, otherwise it assumes it is
located in the application bundle's Contents/MacOS directory.
This commit is contained in:
Stenzek 2018-05-31 04:23:45 -07:00
parent c7a2b1572b
commit 673f1963a0
9 changed files with 101 additions and 24 deletions

View file

@ -118,6 +118,19 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, void* display_h
return surface; return surface;
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
VkMacOSSurfaceCreateInfoMVK surface_create_info = {
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, nullptr, 0, hwnd};
VkSurfaceKHR surface;
VkResult res = vkCreateMacOSSurfaceMVK(instance, &surface_create_info, nullptr, &surface);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateMacOSSurfaceMVK failed: ");
return VK_NULL_HANDLE;
}
return surface;
#else #else
return VK_NULL_HANDLE; return VK_NULL_HANDLE;
#endif #endif

View file

@ -18,5 +18,6 @@ public:
std::string GetName() const override { return "Vulkan"; } std::string GetName() const override { return "Vulkan"; }
std::string GetDisplayName() const override { return _trans("Vulkan"); } std::string GetDisplayName() const override { return _trans("Vulkan"); }
void InitBackendInfo() override; void InitBackendInfo() override;
void PrepareWindow(const WindowSystemInfo& wsi) override;
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -186,6 +186,9 @@ bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, bool
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
if (enable_surface && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) if (enable_surface && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
return false; return false;
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
if (enable_surface && !SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true))
return false;
#endif #endif
// VK_EXT_debug_report // VK_EXT_debug_report
@ -833,6 +836,13 @@ void VulkanContext::InitDriverDetails()
driver = DriverDetails::DRIVER_UNKNOWN; driver = DriverDetails::DRIVER_UNKNOWN;
} }
#ifdef __APPLE__
// Vulkan on macOS goes through Metal, and is not susceptible to the same bugs
// as the vendor's native Vulkan drivers. We use a different driver fields to
// differentiate MoltenVK.
driver = DriverDetails::DRIVER_PORTABILITY;
#endif
DriverDetails::Init(DriverDetails::API_VULKAN, vendor, driver, DriverDetails::Init(DriverDetails::API_VULKAN, vendor, driver,
static_cast<double>(m_device_properties.driverVersion), static_cast<double>(m_device_properties.driverVersion),
DriverDetails::Family::UNKNOWN); DriverDetails::Family::UNKNOWN);

View file

@ -16,7 +16,7 @@ VULKAN_MODULE_ENTRY_POINT(vkGetDeviceProcAddr, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceExtensionProperties, true) VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceExtensionProperties, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceLayerProperties, true) VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceLayerProperties, true)
#endif // VULKAN_MODULE_ENTRY_POINT #endif // VULKAN_MODULE_ENTRY_POINT
#ifdef VULKAN_INSTANCE_ENTRY_POINT #ifdef VULKAN_INSTANCE_ENTRY_POINT
@ -178,13 +178,17 @@ VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXcbPresentationSupportKHR, false)
VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false)
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
VULKAN_INSTANCE_ENTRY_POINT(vkCreateMacOSSurfaceMVK, false)
#endif #endif
VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false)
#endif // VULKAN_INSTANCE_ENTRY_POINT #endif // VULKAN_INSTANCE_ENTRY_POINT
#ifdef VULKAN_DEVICE_ENTRY_POINT #ifdef VULKAN_DEVICE_ENTRY_POINT
@ -194,4 +198,4 @@ VULKAN_DEVICE_ENTRY_POINT(vkGetSwapchainImagesKHR, false)
VULKAN_DEVICE_ENTRY_POINT(vkAcquireNextImageKHR, false) VULKAN_DEVICE_ENTRY_POINT(vkAcquireNextImageKHR, false)
VULKAN_DEVICE_ENTRY_POINT(vkQueuePresentKHR, false) VULKAN_DEVICE_ENTRY_POINT(vkQueuePresentKHR, false)
#endif // VULKAN_DEVICE_ENTRY_POINT #endif // VULKAN_DEVICE_ENTRY_POINT

View file

@ -4,8 +4,10 @@
#include <atomic> #include <atomic>
#include <cstdarg> #include <cstdarg>
#include <cstdlib>
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -14,7 +16,8 @@
#if defined(VK_USE_PLATFORM_WIN32_KHR) #if defined(VK_USE_PLATFORM_WIN32_KHR)
#include <Windows.h> #include <Windows.h>
#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \
defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(USE_HEADLESS) defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \
defined(USE_HEADLESS)
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
@ -98,7 +101,8 @@ void UnloadVulkanLibrary()
} }
#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \
defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(USE_HEADLESS) defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \
defined(USE_HEADLESS)
static void* vulkan_module; static void* vulkan_module;
static std::atomic_int vulkan_module_ref_count = {0}; static std::atomic_int vulkan_module_ref_count = {0};
@ -112,15 +116,27 @@ bool LoadVulkanLibrary()
return true; return true;
} }
#if defined(__APPLE__)
// Check if a path to a specific Vulkan library has been specified.
char* libvulkan_env = getenv("LIBVULKAN_PATH");
if (libvulkan_env)
vulkan_module = dlopen(libvulkan_env, RTLD_NOW);
if (!vulkan_module)
{
// Use the libvulkan.dylib from the application bundle.
std::string path = File::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
vulkan_module = dlopen(path.c_str(), RTLD_NOW);
}
#else
// Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so. // 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"}; static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"};
for (size_t i = 0; i < ArraySize(search_lib_names); i++) for (size_t i = 0; i < ArraySize(search_lib_names); i++)
{ {
vulkan_module = dlopen(search_lib_names[i], RTLD_NOW); vulkan_module = dlopen(search_lib_names[i], RTLD_NOW);
if (vulkan_module) if (vulkan_module)
break; break;
} }
#endif
if (!vulkan_module) if (!vulkan_module)
{ {
@ -164,6 +180,7 @@ void UnloadVulkanLibrary()
dlclose(vulkan_module); dlclose(vulkan_module);
vulkan_module = nullptr; vulkan_module = nullptr;
} }
#else #else
//#warning Unknown platform, not compiling loader. //#warning Unknown platform, not compiling loader.

View file

@ -15,6 +15,8 @@
//#define VK_USE_PLATFORM_XCB_KHR //#define VK_USE_PLATFORM_XCB_KHR
#elif defined(ANDROID) #elif defined(ANDROID)
#define VK_USE_PLATFORM_ANDROID_KHR #define VK_USE_PLATFORM_ANDROID_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_MACOS_MVK
#else #else
//#warning Unknown platform //#warning Unknown platform
#endif #endif

View file

@ -24,6 +24,10 @@
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#if defined(VK_USE_PLATFORM_MACOS_MVK)
#include <objc/message.h>
#endif
namespace Vulkan namespace Vulkan
{ {
void VideoBackend::InitBackendInfo() void VideoBackend::InitBackendInfo()
@ -272,4 +276,33 @@ void VideoBackend::Shutdown()
ShutdownShared(); ShutdownShared();
UnloadVulkanLibrary(); UnloadVulkanLibrary();
} }
void VideoBackend::PrepareWindow(const WindowSystemInfo& wsi)
{
#if defined(VK_USE_PLATFORM_MACOS_MVK)
// This is kinda messy, but it avoids having to write Objective C++ just to create a metal layer.
id view = reinterpret_cast<id>(wsi.render_surface);
Class clsCAMetalLayer = objc_getClass("CAMetalLayer");
if (!clsCAMetalLayer)
{
ERROR_LOG(VIDEO, "Failed to get CAMetalLayer class.");
return;
}
// [CAMetalLayer layer]
id layer = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(objc_getClass("CAMetalLayer"),
sel_getUid("layer"));
if (!layer)
{
ERROR_LOG(VIDEO, "Failed to create Metal layer.");
return;
}
// [view setWantsLayer:YES]
reinterpret_cast<void (*)(id, SEL, BOOL)>(objc_msgSend)(view, sel_getUid("setWantsLayer:"), YES);
// [view setLayer:layer]
reinterpret_cast<void (*)(id, SEL, id)>(objc_msgSend)(view, sel_getUid("setLayer:"), layer);
#endif
}
} // namespace Vulkan } // namespace Vulkan

View file

@ -49,19 +49,20 @@ enum Vendor
enum Driver enum Driver
{ {
DRIVER_ALL = 0, DRIVER_ALL = 0,
DRIVER_NVIDIA, // Official Nvidia, including mobile GPU DRIVER_NVIDIA, // Official Nvidia, including mobile GPU
DRIVER_NOUVEAU, // OSS nouveau DRIVER_NOUVEAU, // OSS nouveau
DRIVER_ATI, // Official ATI DRIVER_ATI, // Official ATI
DRIVER_R600, // OSS Radeon DRIVER_R600, // OSS Radeon
DRIVER_INTEL, // Official Intel DRIVER_INTEL, // Official Intel
DRIVER_I965, // OSS Intel DRIVER_I965, // OSS Intel
DRIVER_ARM, // Official Mali driver DRIVER_ARM, // Official Mali driver
DRIVER_LIMA, // OSS Mali driver DRIVER_LIMA, // OSS Mali driver
DRIVER_QUALCOMM, // Official Adreno driver DRIVER_QUALCOMM, // Official Adreno driver
DRIVER_FREEDRENO, // OSS Adreno driver DRIVER_FREEDRENO, // OSS Adreno driver
DRIVER_IMGTEC, // Official PowerVR driver DRIVER_IMGTEC, // Official PowerVR driver
DRIVER_VIVANTE, // Official Vivante driver DRIVER_VIVANTE, // Official Vivante driver
DRIVER_UNKNOWN // Unknown driver, default to official hardware driver DRIVER_PORTABILITY, // Vulkan via Metal on macOS
DRIVER_UNKNOWN // Unknown driver, default to official hardware driver
}; };
enum class Family enum class Family
@ -283,4 +284,4 @@ void Init(API api, Vendor vendor, Driver driver, const double version, const Fam
// Once Vendor and driver version is set, this will return if it has the applicable bug passed to // Once Vendor and driver version is set, this will return if it has the applicable bug passed to
// it. // it.
bool HasBug(Bug bug); bool HasBug(Bug bug);
} } // namespace DriverDetails

View file

@ -25,9 +25,7 @@
#include "VideoBackends/Null/VideoBackend.h" #include "VideoBackends/Null/VideoBackend.h"
#include "VideoBackends/OGL/VideoBackend.h" #include "VideoBackends/OGL/VideoBackend.h"
#include "VideoBackends/Software/VideoBackend.h" #include "VideoBackends/Software/VideoBackend.h"
#ifndef __APPLE__
#include "VideoBackends/Vulkan/VideoBackend.h" #include "VideoBackends/Vulkan/VideoBackend.h"
#endif
#include "VideoCommon/AsyncRequests.h" #include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/BPStructs.h" #include "VideoCommon/BPStructs.h"
@ -187,9 +185,7 @@ void VideoBackendBase::PopulateList()
#ifdef _WIN32 #ifdef _WIN32
g_available_video_backends.push_back(std::make_unique<DX11::VideoBackend>()); g_available_video_backends.push_back(std::make_unique<DX11::VideoBackend>());
#endif #endif
#ifndef __APPLE__
g_available_video_backends.push_back(std::make_unique<Vulkan::VideoBackend>()); g_available_video_backends.push_back(std::make_unique<Vulkan::VideoBackend>());
#endif
g_available_video_backends.push_back(std::make_unique<SW::VideoSoftware>()); g_available_video_backends.push_back(std::make_unique<SW::VideoSoftware>());
g_available_video_backends.push_back(std::make_unique<Null::VideoBackend>()); g_available_video_backends.push_back(std::make_unique<Null::VideoBackend>());