mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 06:25:37 +00:00
GPU/Vulkan: Use Metal layer instead NSView on macOS.
This commit is contained in:
parent
f846817848
commit
eaca5eca07
|
@ -38,9 +38,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// TODO: Switch to Metal
|
// #define VK_USE_PLATFORM_MACOS_MVK
|
||||||
#define VK_USE_PLATFORM_MACOS_MVK
|
#define VK_USE_PLATFORM_METAL_EXT
|
||||||
// #define VK_USE_PLATFORM_METAL_EXT
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
|
|
|
@ -336,7 +336,8 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||||
if (enable_surface && (surface = SwapChain::CreateVulkanSurface(instance, *wi)) == VK_NULL_HANDLE)
|
WindowInfo wi_copy(*wi);
|
||||||
|
if (enable_surface && (surface = SwapChain::CreateVulkanSurface(instance, wi_copy)) == VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
vkDestroyInstance(instance, nullptr);
|
vkDestroyInstance(instance, nullptr);
|
||||||
Vulkan::UnloadVulkanLibrary();
|
Vulkan::UnloadVulkanLibrary();
|
||||||
|
@ -352,7 +353,7 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu
|
||||||
// Attempt to create the device.
|
// Attempt to create the device.
|
||||||
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer) ||
|
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer) ||
|
||||||
!g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers() ||
|
!g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers() ||
|
||||||
(enable_surface && (*out_swap_chain = SwapChain::Create(*wi, surface, true)) == nullptr))
|
(enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, true)) == nullptr))
|
||||||
{
|
{
|
||||||
// Since we are destroying the instance, we're also responsible for destroying the surface.
|
// Since we are destroying the instance, we're also responsible for destroying the surface.
|
||||||
if (surface != VK_NULL_HANDLE)
|
if (surface != VK_NULL_HANDLE)
|
||||||
|
|
|
@ -16,6 +16,63 @@ Log_SetChannel(Vulkan::SwapChain);
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <objc/message.h>
|
||||||
|
|
||||||
|
static bool CreateMetalLayer(WindowInfo& wi)
|
||||||
|
{
|
||||||
|
id view = reinterpret_cast<id>(wi.window_handle);
|
||||||
|
|
||||||
|
Class clsCAMetalLayer = objc_getClass("CAMetalLayer");
|
||||||
|
if (!clsCAMetalLayer)
|
||||||
|
{
|
||||||
|
Log_ErrorPrint("Failed to get CAMetalLayer class.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [CAMetalLayer layer]
|
||||||
|
id layer = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(objc_getClass("CAMetalLayer"), sel_getUid("layer"));
|
||||||
|
if (!layer)
|
||||||
|
{
|
||||||
|
Log_ErrorPrint("Failed to create Metal layer.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [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);
|
||||||
|
|
||||||
|
// NSScreen* screen = [NSScreen mainScreen]
|
||||||
|
id screen = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(objc_getClass("NSScreen"), sel_getUid("mainScreen"));
|
||||||
|
|
||||||
|
// CGFloat factor = [screen backingScaleFactor]
|
||||||
|
double factor = reinterpret_cast<double (*)(id, SEL)>(objc_msgSend)(screen, sel_getUid("backingScaleFactor"));
|
||||||
|
|
||||||
|
// layer.contentsScale = factor
|
||||||
|
reinterpret_cast<void (*)(id, SEL, double)>(objc_msgSend)(layer, sel_getUid("setContentsScale:"), factor);
|
||||||
|
|
||||||
|
// Store the layer pointer, that way MoltenVK doesn't call [NSView layer] outside the main thread.
|
||||||
|
wi.surface_handle = layer;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DestroyMetalLayer(WindowInfo& wi)
|
||||||
|
{
|
||||||
|
id view = reinterpret_cast<id>(wi.window_handle);
|
||||||
|
id layer = reinterpret_cast<id>(wi.surface_handle);
|
||||||
|
if (layer == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
reinterpret_cast<void (*)(id, SEL, id)>(objc_msgSend)(view, sel_getUid("setLayer:"), nil);
|
||||||
|
reinterpret_cast<void (*)(id, SEL, BOOL)>(objc_msgSend)(view, sel_getUid("setWantsLayer:"), NO);
|
||||||
|
reinterpret_cast<void (*)(id, SEL)>(objc_msgSend)(layer, sel_getUid("release"));
|
||||||
|
wi.surface_handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
SwapChain::SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync)
|
SwapChain::SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync)
|
||||||
: m_wi(wi), m_surface(surface), m_vsync_enabled(vsync)
|
: m_wi(wi), m_surface(surface), m_vsync_enabled(vsync)
|
||||||
|
@ -30,7 +87,7 @@ SwapChain::~SwapChain()
|
||||||
DestroySurface();
|
DestroySurface();
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowInfo& wi)
|
VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, WindowInfo& wi)
|
||||||
{
|
{
|
||||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
if (wi.type == WindowInfo::Type::Win32)
|
if (wi.type == WindowInfo::Type::Win32)
|
||||||
|
@ -103,9 +160,11 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowInf
|
||||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||||
if (wi.type == WindowInfo::Type::MacOS)
|
if (wi.type == WindowInfo::Type::MacOS)
|
||||||
{
|
{
|
||||||
// TODO: Create Metal layer
|
if (!wi.surface_handle && !CreateMetalLayer(wi))
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
|
||||||
VkMetalSurfaceCreateInfoEXT surface_create_info = {VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, nullptr, 0,
|
VkMetalSurfaceCreateInfoEXT surface_create_info = {VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, nullptr, 0,
|
||||||
static_cast<const CAMetalLayer*>(wi.window_handle)};
|
static_cast<const CAMetalLayer*>(wi.surface_handle)};
|
||||||
|
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &surface);
|
VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &surface);
|
||||||
|
@ -138,6 +197,16 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowInf
|
||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo& wi, VkSurfaceKHR surface)
|
||||||
|
{
|
||||||
|
vkDestroySurfaceKHR(g_vulkan_context->GetVulkanInstance(), surface, nullptr);
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
if (wi.type == WindowInfo::Type::MacOS && wi.surface_handle)
|
||||||
|
DestroyMetalLayer(wi);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<SwapChain> SwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync)
|
std::unique_ptr<SwapChain> SwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync)
|
||||||
{
|
{
|
||||||
std::unique_ptr<SwapChain> swap_chain = std::make_unique<SwapChain>(wi, surface, vsync);
|
std::unique_ptr<SwapChain> swap_chain = std::make_unique<SwapChain>(wi, surface, vsync);
|
||||||
|
@ -495,7 +564,7 @@ bool SwapChain::RecreateSurface(const WindowInfo& new_wi)
|
||||||
|
|
||||||
void SwapChain::DestroySurface()
|
void SwapChain::DestroySurface()
|
||||||
{
|
{
|
||||||
vkDestroySurfaceKHR(g_vulkan_context->GetVulkanInstance(), m_surface, nullptr);
|
DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wi, m_surface);
|
||||||
m_surface = VK_NULL_HANDLE;
|
m_surface = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,10 @@ public:
|
||||||
~SwapChain();
|
~SwapChain();
|
||||||
|
|
||||||
// Creates a vulkan-renderable surface for the specified window handle.
|
// Creates a vulkan-renderable surface for the specified window handle.
|
||||||
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, const WindowInfo& wi);
|
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, WindowInfo& wi);
|
||||||
|
|
||||||
|
// Destroys a previously-created surface.
|
||||||
|
static void DestroyVulkanSurface(VkInstance instance, WindowInfo& wi, VkSurfaceKHR surface);
|
||||||
|
|
||||||
// Create a new swap chain from a pre-existing surface.
|
// Create a new swap chain from a pre-existing surface.
|
||||||
static std::unique_ptr<SwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync);
|
static std::unique_ptr<SwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync);
|
||||||
|
|
|
@ -29,4 +29,9 @@ struct WindowInfo
|
||||||
u32 surface_width = 0;
|
u32 surface_width = 0;
|
||||||
u32 surface_height = 0;
|
u32 surface_height = 0;
|
||||||
SurfaceFormat surface_format = SurfaceFormat::RGB8;
|
SurfaceFormat surface_format = SurfaceFormat::RGB8;
|
||||||
|
|
||||||
|
// Needed for macOS.
|
||||||
|
#ifdef __APPLE__
|
||||||
|
void* surface_handle = nullptr;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,14 +100,15 @@ bool VulkanHostDisplay::RecreateSwapChain(const WindowInfo& new_wi)
|
||||||
{
|
{
|
||||||
Assert(!m_swap_chain);
|
Assert(!m_swap_chain);
|
||||||
|
|
||||||
VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), new_wi);
|
WindowInfo wi_copy(new_wi);
|
||||||
|
VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), wi_copy);
|
||||||
if (surface == VK_NULL_HANDLE)
|
if (surface == VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to create new surface for swap chain");
|
Log_ErrorPrintf("Failed to create new surface for swap chain");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_swap_chain = Vulkan::SwapChain::Create(new_wi, surface, false);
|
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, false);
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to create swap chain");
|
Log_ErrorPrintf("Failed to create swap chain");
|
||||||
|
|
Loading…
Reference in a new issue