Permalink
Browse files

Merge pull request #7039 from stenzek/moltenvk

Vulkan: macOS support via MoltenVK
  • Loading branch information...
stenzek committed Nov 30, 2018
2 parents d9cacf6 + 0c0d668 commit 6388992f62098c5d0c6af5bf2f091330ed29de94
@@ -30,7 +30,31 @@ class Semaphore
};
} // namespace Common

#else // _WIN32
#elif defined(__APPLE__)

#include <dispatch/dispatch.h>

namespace Common
{
class Semaphore
{
public:
Semaphore(int initial_count, int maximum_count)
{
m_handle = dispatch_semaphore_create(0);
for (int i = 0; i < initial_count; i++)
dispatch_semaphore_signal(m_handle);
}
~Semaphore() { dispatch_release(m_handle); }
void Wait() { dispatch_semaphore_wait(m_handle, DISPATCH_TIME_FOREVER); }
void Post() { dispatch_semaphore_signal(m_handle); }

private:
dispatch_semaphore_t m_handle;
};
} // namespace Common

#else

#include <semaphore.h>

@@ -293,6 +293,7 @@ PUBLIC
videonull
videoogl
videosoftware
videovulkan

PRIVATE
${LZO}
@@ -322,10 +323,6 @@ if(LIBUSB_FOUND)
)
endif()

if(NOT APPLE)
target_link_libraries(core PUBLIC videovulkan)
endif()

if(WIN32)
target_sources(core PRIVATE
HW/EXI/BBA-TAP/TAP_Win32.cpp
@@ -214,6 +214,9 @@ bool Init(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)

Host_UpdateMainFrame(); // Disable any menus or buttons at boot

// Issue any API calls which must occur on the main thread for the graphics backend.
g_video_backend->PrepareWindow(wsi);

// Start the emu thread
s_emu_thread = std::thread(EmuThread, std::move(boot), wsi);
return true;
@@ -92,15 +92,17 @@ void AdvancedWidget::CreateWidgets()

m_enable_cropping = new GraphicsBool(tr("Crop"), Config::GFX_CROP);
m_enable_prog_scan = new QCheckBox(tr("Enable Progressive Scan"));
m_backend_multithreading =
new GraphicsBool(tr("Backend Multi-threading"), Config::GFX_BACKEND_MULTITHREADING);

misc_layout->addWidget(m_enable_cropping, 0, 0);
misc_layout->addWidget(m_enable_prog_scan, 0, 1);

misc_layout->addWidget(m_backend_multithreading, 1, 0);
#ifdef _WIN32
m_borderless_fullscreen =
new GraphicsBool(tr("Borderless Fullscreen"), Config::GFX_BORDERLESS_FULLSCREEN);

misc_layout->addWidget(m_borderless_fullscreen, 1, 0);
misc_layout->addWidget(m_borderless_fullscreen, 1, 1);
#endif

main_layout->addWidget(debugging_box);
@@ -191,6 +193,11 @@ void AdvancedWidget::AddDescriptions()
static const char TR_PROGRESSIVE_SCAN_DESCRIPTION[] = QT_TR_NOOP(
"Enables progressive scan if supported by the emulated software.\nMost games don't "
"care about this.\n\nIf unsure, leave this unchecked.");
static const char TR_BACKEND_MULTITHREADING_DESCRIPTION[] =
QT_TR_NOOP("Enables multi-threaded command submission in backends where supported. Enabling "
"this option may result in a performance improvement on systems with more than "
"two CPU cores. Currently, this is limited to the Vulkan backend.\n\nIf unsure, "
"leave this checked.");
#ifdef _WIN32
static const char TR_BORDERLESS_FULLSCREEN_DESCRIPTION[] = QT_TR_NOOP(
"Implement fullscreen mode with a borderless window spanning the whole screen instead of "
@@ -217,6 +224,7 @@ void AdvancedWidget::AddDescriptions()
AddDescription(m_enable_cropping, TR_CROPPING_DESCRIPTION);
AddDescription(m_enable_prog_scan, TR_PROGRESSIVE_SCAN_DESCRIPTION);
AddDescription(m_enable_freelook, TR_FREE_LOOK_DESCRIPTION);
AddDescription(m_backend_multithreading, TR_BACKEND_MULTITHREADING_DESCRIPTION);
#ifdef _WIN32
AddDescription(m_borderless_fullscreen, TR_BORDERLESS_FULLSCREEN_DESCRIPTION);
#endif
@@ -44,5 +44,6 @@ class AdvancedWidget final : public GraphicsWidget
// Misc
QCheckBox* m_enable_cropping;
QCheckBox* m_enable_prog_scan;
QCheckBox* m_backend_multithreading;
QCheckBox* m_borderless_fullscreen;
};
@@ -1,11 +1,9 @@
add_subdirectory(OGL)
add_subdirectory(Null)
add_subdirectory(Software)
add_subdirectory(Vulkan)

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_subdirectory(D3D)
endif()

if(NOT APPLE)
add_subdirectory(Vulkan)
endif()
@@ -118,6 +118,19 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, void* display_h

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
return VK_NULL_HANDLE;
#endif
@@ -18,5 +18,6 @@ class VideoBackend : public VideoBackendBase
std::string GetName() const override { return "Vulkan"; }
std::string GetDisplayName() const override { return _trans("Vulkan"); }
void InitBackendInfo() override;
void PrepareWindow(const WindowSystemInfo& wsi) override;
};
} // namespace Vulkan
@@ -186,6 +186,9 @@ bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, bool
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
if (enable_surface && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
return false;
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
if (enable_surface && !SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true))
return false;
#endif

// VK_EXT_debug_report
@@ -833,6 +836,13 @@ void VulkanContext::InitDriverDetails()
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,
static_cast<double>(m_device_properties.driverVersion),
DriverDetails::Family::UNKNOWN);
@@ -16,7 +16,7 @@ VULKAN_MODULE_ENTRY_POINT(vkGetDeviceProcAddr, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceExtensionProperties, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceLayerProperties, true)

#endif // VULKAN_MODULE_ENTRY_POINT
#endif // VULKAN_MODULE_ENTRY_POINT

#ifdef VULKAN_INSTANCE_ENTRY_POINT

@@ -178,13 +178,17 @@ VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXcbPresentationSupportKHR, false)

VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false)

#elif defined(VK_USE_PLATFORM_MACOS_MVK)

VULKAN_INSTANCE_ENTRY_POINT(vkCreateMacOSSurfaceMVK, false)

#endif

VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false)

#endif // VULKAN_INSTANCE_ENTRY_POINT
#endif // VULKAN_INSTANCE_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(vkQueuePresentKHR, false)

#endif // VULKAN_DEVICE_ENTRY_POINT
#endif // VULKAN_DEVICE_ENTRY_POINT
@@ -4,8 +4,10 @@

#include <atomic>
#include <cstdarg>
#include <cstdlib>

#include "Common/CommonFuncs.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"

@@ -14,7 +16,8 @@
#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) || defined(USE_HEADLESS)
defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \
defined(USE_HEADLESS)
#include <dlfcn.h>
#endif

@@ -98,7 +101,8 @@ void UnloadVulkanLibrary()
}

#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 std::atomic_int vulkan_module_ref_count = {0};
@@ -112,15 +116,27 @@ bool LoadVulkanLibrary()
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.
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;
}
#endif

if (!vulkan_module)
{
@@ -164,6 +180,7 @@ void UnloadVulkanLibrary()
dlclose(vulkan_module);
vulkan_module = nullptr;
}

#else

//#warning Unknown platform, not compiling loader.
@@ -15,6 +15,8 @@
//#define VK_USE_PLATFORM_XCB_KHR
#elif defined(ANDROID)
#define VK_USE_PLATFORM_ANDROID_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_MACOS_MVK
#else
//#warning Unknown platform
#endif
@@ -24,6 +24,10 @@
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"

#if defined(VK_USE_PLATFORM_MACOS_MVK)
#include <objc/message.h>
#endif

namespace Vulkan
{
void VideoBackend::InitBackendInfo()
@@ -272,4 +276,33 @@ void VideoBackend::Shutdown()
ShutdownShared();
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
@@ -49,19 +49,20 @@ enum Vendor
enum Driver
{
DRIVER_ALL = 0,
DRIVER_NVIDIA, // Official Nvidia, including mobile GPU
DRIVER_NOUVEAU, // OSS nouveau
DRIVER_ATI, // Official ATI
DRIVER_R600, // OSS Radeon
DRIVER_INTEL, // Official Intel
DRIVER_I965, // OSS Intel
DRIVER_ARM, // Official Mali driver
DRIVER_LIMA, // OSS Mali driver
DRIVER_QUALCOMM, // Official Adreno driver
DRIVER_FREEDRENO, // OSS Adreno driver
DRIVER_IMGTEC, // Official PowerVR driver
DRIVER_VIVANTE, // Official Vivante driver
DRIVER_UNKNOWN // Unknown driver, default to official hardware driver
DRIVER_NVIDIA, // Official Nvidia, including mobile GPU
DRIVER_NOUVEAU, // OSS nouveau
DRIVER_ATI, // Official ATI
DRIVER_R600, // OSS Radeon
DRIVER_INTEL, // Official Intel
DRIVER_I965, // OSS Intel
DRIVER_ARM, // Official Mali driver
DRIVER_LIMA, // OSS Mali driver
DRIVER_QUALCOMM, // Official Adreno driver
DRIVER_FREEDRENO, // OSS Adreno driver
DRIVER_IMGTEC, // Official PowerVR driver
DRIVER_VIVANTE, // Official Vivante driver
DRIVER_PORTABILITY, // Vulkan via Metal on macOS
DRIVER_UNKNOWN // Unknown driver, default to official hardware driver
};

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
// it.
bool HasBug(Bug bug);
}
} // namespace DriverDetails
Oops, something went wrong.

0 comments on commit 6388992

Please sign in to comment.