From f146661a76ad4a433d17730307e637ab2d0a9996 Mon Sep 17 00:00:00 2001 From: Alexandre Janniaux Date: Sun, 20 Mar 2022 20:12:40 +0100 Subject: [PATCH] VulkanRenderer: implement metal view The metal view backing the MoltenVK compatibility wrapper is written in Obj-C. It would have been possible to use the Objective-C runtime in C++ but the code is less performant (the symbol binding is done at first use instead of the beginning of the program) and actually harder to get right. Note that MoltenVK is not linked to the loader, so the libMoltenVK.dylib object must be available for loading. --- .../VulkanRenderer/Wrapper/Instance.hpp | 4 +++ .../Wrapper/InstanceFunctions.hpp | 6 +++++ .../Nazara/VulkanRenderer/Wrapper/Surface.hpp | 10 ++++++++ .../Nazara/VulkanRenderer/Wrapper/Surface.inl | 25 +++++++++++++++++++ src/Nazara/VulkanRenderer/Vulkan.cpp | 4 +++ src/Nazara/VulkanRenderer/VulkanSurface.cpp | 14 +++++++++++ .../VulkanRenderer/VulkanSurfaceMetal.mm | 16 ++++++++++++ xmake.lua | 2 ++ 8 files changed, 81 insertions(+) create mode 100644 src/Nazara/VulkanRenderer/VulkanSurfaceMetal.mm diff --git a/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp b/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp index b63252bcd..13c31180e 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp @@ -34,6 +34,10 @@ #include #endif +#ifdef VK_USE_PLATFORM_METAL_EXT +#include +#endif + #ifdef VK_USE_PLATFORM_XLIB_KHR typedef struct _XDisplay Display; typedef unsigned long XID; diff --git a/include/Nazara/VulkanRenderer/Wrapper/InstanceFunctions.hpp b/include/Nazara/VulkanRenderer/Wrapper/InstanceFunctions.hpp index b59818039..42805549e 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/InstanceFunctions.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/InstanceFunctions.hpp @@ -97,6 +97,12 @@ NAZARA_VULKANRENDERER_INSTANCE_EXT_BEGIN(VK_KHR_win32_surface) NAZARA_VULKANRENDERER_INSTANCE_EXT_END() #endif +#ifdef VK_USE_PLATFORM_METAL_EXT +NAZARA_VULKANRENDERER_INSTANCE_EXT_BEGIN(VK_EXT_metal_surface) + NAZARA_VULKANRENDERER_INSTANCE_FUNCTION(vkCreateMetalSurfaceEXT) +NAZARA_VULKANRENDERER_INSTANCE_EXT_END() +#endif + #undef NAZARA_VULKANRENDERER_INSTANCE_CORE_EXT_FUNCTION #undef NAZARA_VULKANRENDERER_INSTANCE_EXT_BEGIN #undef NAZARA_VULKANRENDERER_INSTANCE_EXT_END diff --git a/include/Nazara/VulkanRenderer/Wrapper/Surface.hpp b/include/Nazara/VulkanRenderer/Wrapper/Surface.hpp index 7ea5831b5..95157b889 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Surface.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Surface.hpp @@ -12,6 +12,11 @@ #include #include +#ifdef VK_USE_PLATFORM_METAL_EXT +#include +#include +#endif + namespace Nz { namespace Vk @@ -54,6 +59,11 @@ namespace Nz inline bool Create(HINSTANCE instance, HWND handle, VkWin32SurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); #endif + #ifdef VK_USE_PLATFORM_METAL_EXT + inline bool Create(const VkMetalSurfaceCreateInfoEXT& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(id layer, const VkAllocationCallbacks* allocator = nullptr); + #endif + inline void Destroy(); bool GetCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceCapabilitiesKHR* surfaceCapabilities) const; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Surface.inl b/include/Nazara/VulkanRenderer/Wrapper/Surface.inl index a9beb4bca..5e6d6b4e8 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Surface.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/Surface.inl @@ -140,6 +140,27 @@ namespace Nz } #endif + #ifdef VK_USE_PLATFORM_METAL_EXT + inline bool Surface::Create(const VkMetalSurfaceCreateInfoEXT& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateMetalSurfaceEXT(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(id layer, const VkAllocationCallbacks* allocator) + { + VkMetalSurfaceCreateInfoEXT createInfo = + { + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, + nullptr, + 0, + layer + }; + + return Create(createInfo, allocator); + } + #endif + inline void Surface::Destroy() { if (m_surface != VK_NULL_HANDLE) @@ -262,6 +283,10 @@ namespace Nz return true; #endif +#ifdef VK_USE_PLATFORM_METAL_EXT + if (instance.IsExtensionLoaded(VK_EXT_METAL_SURFACE_EXTENSION_NAME)) + return true; +#endif return false; } diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index 48407f42b..661d17e30 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -244,6 +244,10 @@ namespace Nz enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #endif + #ifdef VK_USE_PLATFORM_METAL_EXT + enabledExtensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); + #endif + if (availableExtensions.count(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) enabledExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); diff --git a/src/Nazara/VulkanRenderer/VulkanSurface.cpp b/src/Nazara/VulkanRenderer/VulkanSurface.cpp index 80b186e56..3663e3c17 100644 --- a/src/Nazara/VulkanRenderer/VulkanSurface.cpp +++ b/src/Nazara/VulkanRenderer/VulkanSurface.cpp @@ -7,8 +7,17 @@ #include #include +#ifdef VK_USE_PLATFORM_METAL_EXT +#include +#include +#endif + namespace Nz { + #ifdef VK_USE_PLATFORM_METAL_EXT + id CreateAndAttachMetalLayer(void* window); + #endif + VulkanSurface::VulkanSurface() : m_surface(Vulkan::GetInstance()) { @@ -60,6 +69,11 @@ namespace Nz } } #elif defined(NAZARA_PLATFORM_MACOS) + { + NazaraAssert(handle.type == WindowBackend::Cocoa, "expected cocoa window"); + id layer = CreateAndAttachMetalLayer(handle.cocoa.window); + success = m_surface.Create(layer); + } #else #error This OS is not supported by Vulkan #endif diff --git a/src/Nazara/VulkanRenderer/VulkanSurfaceMetal.mm b/src/Nazara/VulkanRenderer/VulkanSurfaceMetal.mm new file mode 100644 index 000000000..3ea392a61 --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanSurfaceMetal.mm @@ -0,0 +1,16 @@ +#import +#import +#import + +namespace Nz +{ + id CreateAndAttachMetalLayer(void* window) { + NSWindow* obj = (__bridge NSWindow*)window; + NSView* view = [[NSView alloc] initWithFrame:obj.frame]; + [view setLayer:[CAMetalLayer layer]]; + [view setWantsLayer:YES]; + view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + [obj.contentView addSubview:view]; + return view.layer; + } +} diff --git a/xmake.lua b/xmake.lua index d50f51962..1567a9b7b 100644 --- a/xmake.lua +++ b/xmake.lua @@ -103,6 +103,8 @@ local modules = { add_defines("VK_USE_PLATFORM_WAYLAND_KHR") elseif is_plat("macosx") then add_defines("VK_USE_PLATFORM_METAL_EXT") + add_files("src/Nazara/VulkanRenderer/**.mm") + add_frameworks("quartzcore", "AppKit") end end },