From 527ee3aea5fc2bc80d51cee5812673e8baff70ff Mon Sep 17 00:00:00 2001 From: Marcin Chojnacki Date: Tue, 30 Aug 2022 19:02:56 +0200 Subject: [PATCH] Initial implementation of macOS Vulkan renderer over MoltenVK (#124) --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 6 ++- src/Cafe/CMakeLists.txt | 8 +++- src/Cafe/GraphicPack/GraphicPack2.cpp | 2 + src/Cafe/HW/Latte/Core/LatteConst.h | 1 + src/Cafe/HW/Latte/Core/LatteThread.cpp | 2 + src/Cafe/HW/Latte/Renderer/Renderer.h | 1 + .../HW/Latte/Renderer/Vulkan/CocoaSurface.h | 9 ++++ .../HW/Latte/Renderer/Vulkan/CocoaSurface.mm | 45 +++++++++++++++++++ .../HW/Latte/Renderer/Vulkan/VulkanAPI.cpp | 12 +++-- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h | 4 ++ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 13 +++++- src/Common/CMakeLists.txt | 2 +- src/gui/guiWrapper.cpp | 4 ++ src/gui/guiWrapper.h | 2 + src/util/Fiber/FiberUnix.cpp | 1 - 16 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h create mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 82599f2a..12daf566 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ if (NOT TARGET glslang::SPIRV AND TARGET SPIRV) add_library(glslang::SPIRV ALIAS SPIRV) endif() -if (UNIX) +if (UNIX AND NOT APPLE) find_package(X11 REQUIRED) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e903db4c..872a5df9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,7 +20,11 @@ if(MSVC) # _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING # _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS elseif(UNIX) - if(NOT APPLE) + if(APPLE) + add_definitions(-D_XOPEN_SOURCE) + add_definitions(-DVK_USE_PLATFORM_MACOS_MVK) + add_definitions(-DVK_USE_PLATFORM_METAL_EXT) + else() add_definitions(-DVK_USE_PLATFORM_XLIB_KHR) # legacy. Do we need to support XLIB surfaces? add_definitions(-DVK_USE_PLATFORM_XCB_KHR) endif() diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index a4a60d0d..47ad5667 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -6,7 +6,13 @@ endif() file(GLOB_RECURSE CPP_FILES *.cpp) file(GLOB_RECURSE H_FILES *.h) -add_library(CemuCafe ${CPP_FILES} ${H_FILES}) + +if(APPLE) + file(GLOB_RECURSE MM_FILES *.mm) + add_library(CemuCafe ${CPP_FILES} ${MM_FILES} ${H_FILES}) +else() + add_library(CemuCafe ${CPP_FILES} ${H_FILES}) +endif() set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 0f734675..62d47388 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -182,6 +182,8 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules) m_gfx_vendor = GfxVendor::Mesa; else if (boost::iequals(*option_vendorFilter, "nvidia")) m_gfx_vendor = GfxVendor::Nvidia; + else if (boost::iequals(*option_vendorFilter, "apple")) + m_gfx_vendor = GfxVendor::Apple; else cemuLog_force("Unknown value '{}' for vendorFilter", *option_vendorFilter); } diff --git a/src/Cafe/HW/Latte/Core/LatteConst.h b/src/Cafe/HW/Latte/Core/LatteConst.h index 4cd92977..ffbead1c 100644 --- a/src/Cafe/HW/Latte/Core/LatteConst.h +++ b/src/Cafe/HW/Latte/Core/LatteConst.h @@ -79,6 +79,7 @@ #define GLVENDOR_INTEL_LEGACY (3) #define GLVENDOR_INTEL_NOLEGACY (4) #define GLVENDOR_INTEL (5) +#define GLVENDOR_APPLE (6) // decompiler diff --git a/src/Cafe/HW/Latte/Core/LatteThread.cpp b/src/Cafe/HW/Latte/Core/LatteThread.cpp index ce28c759..9b3dfe8a 100644 --- a/src/Cafe/HW/Latte/Core/LatteThread.cpp +++ b/src/Cafe/HW/Latte/Core/LatteThread.cpp @@ -161,6 +161,8 @@ int Latte_ThreadEntry() case GfxVendor::Nvidia: LatteGPUState.glVendor = GLVENDOR_NVIDIA; break; + case GfxVendor::Apple: + LatteGPUState.glVendor = GLVENDOR_APPLE; default: break; } diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.h b/src/Cafe/HW/Latte/Renderer/Renderer.h index bd57cee1..e58a4ae7 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.h +++ b/src/Cafe/HW/Latte/Renderer/Renderer.h @@ -21,6 +21,7 @@ enum class GfxVendor IntelNoLegacy, Intel, Nvidia, + Apple, Mesa, MAX diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h new file mode 100644 index 00000000..b2c0db13 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h @@ -0,0 +1,9 @@ +#pragma once + +#if BOOST_OS_MACOS + +#include + +VkSurfaceKHR CreateCocoaSurface(VkInstance instance, void* handle); + +#endif diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm new file mode 100644 index 00000000..0dc3b3ec --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm @@ -0,0 +1,45 @@ +#include "Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" + +#import +#import + +@interface MetalView : NSView +@end + +@implementation MetalView + +-(BOOL) wantsUpdateLayer { return YES; } + ++(Class) layerClass { return [CAMetalLayer class]; } + +-(CALayer*) makeBackingLayer { return [self.class.layerClass layer]; } + +@end + +VkSurfaceKHR CreateCocoaSurface(VkInstance instance, void* handle) +{ + NSView* view = (NSView*)handle; + + MetalView* childView = [[MetalView alloc] initWithFrame:view.bounds]; + childView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + childView.wantsLayer = YES; + + [view addSubview:childView]; + + VkMetalSurfaceCreateInfoEXT surface; + surface.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + surface.pNext = NULL; + surface.flags = 0; + surface.pLayer = (CAMetalLayer*)childView.layer; + + VkSurfaceKHR result; + VkResult err; + if ((err = vkCreateMetalSurfaceEXT(instance, &surface, nullptr, &result)) != VK_SUCCESS) + { + forceLog_printf("Cannot create a Metal Vulkan surface: %d", (sint32)err); + throw std::runtime_error(fmt::format("Cannot create a Metal Vulkan surface: {}", err)); + } + + return result; +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp index a60720d1..fd5beb2f 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp @@ -65,10 +65,14 @@ bool InitializeDeviceVulkan(VkDevice device) void* dlopen_vulkan_loader() { - void* vulkan_so = dlopen("libvulkan.so", RTLD_NOW); - if(!vulkan_so) - vulkan_so = dlopen("libvulkan.so.1", RTLD_NOW); - return vulkan_so; +#if BOOST_OS_LINUX + void* vulkan_so = dlopen("libvulkan.so", RTLD_NOW); + if(!vulkan_so) + vulkan_so = dlopen("libvulkan.so.1", RTLD_NOW); +#elif BOOST_OS_MACOS + void* vulkan_so = dlopen("libMoltenVK.dylib", RTLD_NOW); +#endif + return vulkan_so; } bool InitializeGlobalVulkan() diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index bf4e58d0..c1f2dee6 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -135,6 +135,10 @@ VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR); VKFUNC_INSTANCE(vkCreateWin32SurfaceKHR); #endif +#if BOOST_OS_MACOS +VKFUNC_INSTANCE(vkCreateMetalSurfaceEXT); +#endif + VKFUNC_INSTANCE(vkDestroySurfaceKHR); VKFUNC_DEVICE(vkCreateSwapchainKHR); VKFUNC_DEVICE(vkDestroySwapchainKHR); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index e35ef6fd..0c28950d 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -3,6 +3,7 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h" #include "Cafe/HW/Latte/Core/LatteBufferCache.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" @@ -107,6 +108,8 @@ std::vector VulkanRenderer::GetDevices() requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif BOOST_OS_LINUX requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + #elif BOOST_OS_MACOS + requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #endif VkApplicationInfo app_info{}; @@ -189,6 +192,9 @@ void VulkanRenderer::DetermineVendor() case 0x1002: m_vendor = GfxVendor::AMD; break; + case 0x106B: + m_vendor = GfxVendor::Apple; + break; } if (IsRunningInWine()) @@ -402,8 +408,10 @@ VulkanRenderer::VulkanRenderer() deviceFeatures.independentBlend = VK_TRUE; deviceFeatures.samplerAnisotropy = VK_TRUE; deviceFeatures.imageCubeArray = VK_TRUE; +#if !BOOST_OS_MACOS deviceFeatures.geometryShader = VK_TRUE; deviceFeatures.logicOp = VK_TRUE; +#endif deviceFeatures.occlusionQueryPrecise = VK_TRUE; deviceFeatures.depthClamp = VK_TRUE; deviceFeatures.depthBiasClamp = VK_TRUE; @@ -1165,6 +1173,8 @@ std::vector VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif BOOST_OS_LINUX requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + #elif BOOST_OS_MACOS + requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #endif if (cafeLog_isLoggingFlagEnabled(LOG_TYPE_VULKAN_VALIDATION)) requiredInstanceExtensions.emplace_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); @@ -1342,8 +1352,7 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc #elif BOOST_OS_LINUX return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window); #elif BOOST_OS_MACOS - cemu_assert_unimplemented(); - return nullptr; + return CreateCocoaSurface(instance, windowInfo.handle); #endif } diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index dd7b8f21..a1d5b8ab 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -38,7 +38,7 @@ target_link_libraries(CemuCommon PRIVATE glm::glm ) -if (UNIX) +if (UNIX AND NOT APPLE) target_link_libraries(CemuCommon PRIVATE X11::X11 X11::Xrender X11::Xutil) endif() diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index c53a3bef..17eb3bc6 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -90,6 +90,8 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) graphicMode = "[Intel GPU]"; else if (LatteGPUState.glVendor == GLVENDOR_NVIDIA) graphicMode = "[NVIDIA GPU]"; + else if (LatteGPUState.glVendor == GLVENDOR_APPLE) + graphicMode = "[Apple GPU]"; const uint64 titleId = CafeSystem::GetForegroundTitleId(); windowText.append(fmt::format(" - FPS: {:.2f} {} {} [TitleId: {:08x}-{:08x}]", (double)fps, renderer, graphicMode, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF))); @@ -197,6 +199,8 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c { cemuLog_log(LogType::Force, "Unable to get xlib display"); } +#else + handleInfoOut.handle = wxw->GetHandle(); #endif } diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index ecea2f6e..a9d3e1b3 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -20,6 +20,8 @@ struct WindowHandleInfo //xcb_window_t xcb_window{}; // Wayland // todo +#else + void* handle; #endif }; diff --git a/src/util/Fiber/FiberUnix.cpp b/src/util/Fiber/FiberUnix.cpp index 9614fc4a..c2ced28b 100644 --- a/src/util/Fiber/FiberUnix.cpp +++ b/src/util/Fiber/FiberUnix.cpp @@ -1,6 +1,5 @@ #include "Fiber.h" #if BOOST_OS_LINUX || BOOST_OS_MACOS -#define _XOPEN_SOURCE #include thread_local Fiber* sCurrentFiber{};