1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22/* 23 * @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's 24 * SDL_x11vulkan.c. 25 */ 26#include "../../SDL_internal.h" 27 28#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_COCOA 29 30#include "SDL_cocoavideo.h" 31#include "SDL_cocoawindow.h" 32#include "SDL_assert.h" 33 34#include "SDL_loadso.h" 35#include "SDL_cocoametalview.h" 36#include "SDL_cocoavulkan.h" 37#include "SDL_syswm.h" 38 39#include <dlfcn.h> 40 41const char* defaultPaths[] = { 42 "vulkan.framework/vulkan", 43 "libvulkan.1.dylib", 44 "libvulkan.dylib", 45 "MoltenVK.framework/MoltenVK", 46 "libMoltenVK.dylib" 47}; 48 49/* Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF. */ 50#define DEFAULT_HANDLE RTLD_DEFAULT 51 52int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) 53{ 54 VkExtensionProperties *extensions = NULL; 55 Uint32 extensionCount = 0; 56 SDL_bool hasSurfaceExtension = SDL_FALSE; 57 SDL_bool hasMacOSSurfaceExtension = SDL_FALSE; 58 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; 59 60 if (_this->vulkan_config.loader_handle) { 61 return SDL_SetError("Vulkan Portability library is already loaded."); 62 } 63 64 /* Load the Vulkan loader library */ 65 if (!path) { 66 path = SDL_getenv("SDL_VULKAN_LIBRARY"); 67 } 68 69 if (!path) { 70 /* Handle the case where Vulkan Portability is linked statically. */ 71 vkGetInstanceProcAddr = 72 (PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE, 73 "vkGetInstanceProcAddr"); 74 } 75 76 if (vkGetInstanceProcAddr) { 77 _this->vulkan_config.loader_handle = DEFAULT_HANDLE; 78 } else { 79 const char** paths; 80 const char *foundPath = NULL; 81 int numPaths; 82 int i; 83 84 if (path) { 85 paths = &path; 86 numPaths = 1; 87 } else { 88 /* Look for framework or .dylib packaged with the application 89 * instead. */ 90 paths = defaultPaths; 91 numPaths = SDL_arraysize(defaultPaths); 92 } 93 94 for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) { 95 foundPath = paths[i]; 96 _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath); 97 } 98 99 if (_this->vulkan_config.loader_handle == NULL) { 100 return SDL_SetError("Failed to load Vulkan Portability library"); 101 } 102 103 SDL_strlcpy(_this->vulkan_config.loader_path, foundPath, 104 SDL_arraysize(_this->vulkan_config.loader_path)); 105 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( 106 _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr"); 107 } 108 109 if (!vkGetInstanceProcAddr) { 110 SDL_SetError("Failed to find %s in either executable or %s: %s", 111 "vkGetInstanceProcAddr", 112 _this->vulkan_config.loader_path, 113 (const char *) dlerror()); 114 goto fail; 115 } 116 117 _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; 118 _this->vulkan_config.vkEnumerateInstanceExtensionProperties = 119 (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( 120 VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"); 121 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { 122 goto fail; 123 } 124 extensions = SDL_Vulkan_CreateInstanceExtensionsList( 125 (PFN_vkEnumerateInstanceExtensionProperties) 126 _this->vulkan_config.vkEnumerateInstanceExtensionProperties, 127 &extensionCount); 128 if (!extensions) { 129 goto fail; 130 } 131 for (Uint32 i = 0; i < extensionCount; i++) { 132 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 133 hasSurfaceExtension = SDL_TRUE; 134 } else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 135 hasMacOSSurfaceExtension = SDL_TRUE; 136 } 137 } 138 SDL_free(extensions); 139 if (!hasSurfaceExtension) { 140 SDL_SetError("Installed Vulkan Portability library doesn't implement the " 141 VK_KHR_SURFACE_EXTENSION_NAME " extension"); 142 goto fail; 143 } else if (!hasMacOSSurfaceExtension) { 144 SDL_SetError("Installed Vulkan Portability library doesn't implement the " 145 VK_MVK_MACOS_SURFACE_EXTENSION_NAME "extension"); 146 goto fail; 147 } 148 return 0; 149 150fail: 151 SDL_UnloadObject(_this->vulkan_config.loader_handle); 152 _this->vulkan_config.loader_handle = NULL; 153 return -1; 154} 155 156void Cocoa_Vulkan_UnloadLibrary(_THIS) 157{ 158 if (_this->vulkan_config.loader_handle) { 159 if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) { 160 SDL_UnloadObject(_this->vulkan_config.loader_handle); 161 } 162 _this->vulkan_config.loader_handle = NULL; 163 } 164} 165 166SDL_bool Cocoa_Vulkan_GetInstanceExtensions(_THIS, 167 SDL_Window *window, 168 unsigned *count, 169 const char **names) 170{ 171 static const char *const extensionsForCocoa[] = { 172 VK_KHR_SURFACE_EXTENSION_NAME, VK_MVK_MACOS_SURFACE_EXTENSION_NAME 173 }; 174 if (!_this->vulkan_config.loader_handle) { 175 SDL_SetError("Vulkan is not loaded"); 176 return SDL_FALSE; 177 } 178 return SDL_Vulkan_GetInstanceExtensions_Helper( 179 count, names, SDL_arraysize(extensionsForCocoa), 180 extensionsForCocoa); 181} 182 183SDL_bool Cocoa_Vulkan_CreateSurface(_THIS, 184 SDL_Window *window, 185 VkInstance instance, 186 VkSurfaceKHR *surface) 187{ 188 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 189 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; 190 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = 191 (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( 192 (VkInstance)instance, 193 "vkCreateMacOSSurfaceMVK"); 194 VkMacOSSurfaceCreateInfoMVK createInfo = {}; 195 VkResult result; 196 SDL_MetalView metalview; 197 198 if (!_this->vulkan_config.loader_handle) { 199 SDL_SetError("Vulkan is not loaded"); 200 return SDL_FALSE; 201 } 202 203 if (!vkCreateMacOSSurfaceMVK) { 204 SDL_SetError(VK_MVK_MACOS_SURFACE_EXTENSION_NAME 205 " extension is not enabled in the Vulkan instance."); 206 return SDL_FALSE; 207 } 208 209 metalview = Cocoa_Metal_CreateView(_this, window); 210 if (metalview == NULL) { 211 return SDL_FALSE; 212 } 213 214 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; 215 createInfo.pNext = NULL; 216 createInfo.flags = 0; 217 createInfo.pView = (const void *)metalview; 218 result = vkCreateMacOSSurfaceMVK(instance, &createInfo, 219 NULL, surface); 220 if (result != VK_SUCCESS) { 221 Cocoa_Metal_DestroyView(_this, metalview); 222 SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", 223 SDL_Vulkan_GetResultString(result)); 224 return SDL_FALSE; 225 } 226 227 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call 228 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one 229 * from returning a new view object in CreateView, and one because it's 230 * a subview of the window.) If we release the view here to make it +1, it 231 * will be destroyed when the window is destroyed. */ 232 CFBridgingRelease(metalview); 233 234 return SDL_TRUE; 235} 236 237void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) 238{ 239 Cocoa_Metal_GetDrawableSize(_this, window, w, h); 240} 241 242#endif 243 244/* vim: set ts=4 sw=4 expandtab: */ 245