SDL_cocoavulkan.m (8646B)
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 33 #include "SDL_loadso.h" 34 #include "SDL_cocoametalview.h" 35 #include "SDL_cocoavulkan.h" 36 #include "SDL_syswm.h" 37 38 #include <dlfcn.h> 39 40 const char* defaultPaths[] = { 41 "vulkan.framework/vulkan", 42 "libvulkan.1.dylib", 43 "libvulkan.dylib", 44 "MoltenVK.framework/MoltenVK", 45 "libMoltenVK.dylib" 46 }; 47 48 /* Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF. */ 49 #define DEFAULT_HANDLE RTLD_DEFAULT 50 51 int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) 52 { 53 VkExtensionProperties *extensions = NULL; 54 Uint32 extensionCount = 0; 55 SDL_bool hasSurfaceExtension = SDL_FALSE; 56 SDL_bool hasMacOSSurfaceExtension = SDL_FALSE; 57 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; 58 59 if (_this->vulkan_config.loader_handle) { 60 return SDL_SetError("Vulkan Portability library is already loaded."); 61 } 62 63 /* Load the Vulkan loader library */ 64 if (!path) { 65 path = SDL_getenv("SDL_VULKAN_LIBRARY"); 66 } 67 68 if (!path) { 69 /* Handle the case where Vulkan Portability is linked statically. */ 70 vkGetInstanceProcAddr = 71 (PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE, 72 "vkGetInstanceProcAddr"); 73 } 74 75 if (vkGetInstanceProcAddr) { 76 _this->vulkan_config.loader_handle = DEFAULT_HANDLE; 77 } else { 78 const char** paths; 79 const char *foundPath = NULL; 80 int numPaths; 81 int i; 82 83 if (path) { 84 paths = &path; 85 numPaths = 1; 86 } else { 87 /* Look for framework or .dylib packaged with the application 88 * instead. */ 89 paths = defaultPaths; 90 numPaths = SDL_arraysize(defaultPaths); 91 } 92 93 for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) { 94 foundPath = paths[i]; 95 _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath); 96 } 97 98 if (_this->vulkan_config.loader_handle == NULL) { 99 return SDL_SetError("Failed to load Vulkan Portability library"); 100 } 101 102 SDL_strlcpy(_this->vulkan_config.loader_path, foundPath, 103 SDL_arraysize(_this->vulkan_config.loader_path)); 104 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( 105 _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr"); 106 } 107 108 if (!vkGetInstanceProcAddr) { 109 SDL_SetError("Failed to find %s in either executable or %s: %s", 110 "vkGetInstanceProcAddr", 111 _this->vulkan_config.loader_path, 112 (const char *) dlerror()); 113 goto fail; 114 } 115 116 _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; 117 _this->vulkan_config.vkEnumerateInstanceExtensionProperties = 118 (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( 119 VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"); 120 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { 121 goto fail; 122 } 123 extensions = SDL_Vulkan_CreateInstanceExtensionsList( 124 (PFN_vkEnumerateInstanceExtensionProperties) 125 _this->vulkan_config.vkEnumerateInstanceExtensionProperties, 126 &extensionCount); 127 if (!extensions) { 128 goto fail; 129 } 130 for (Uint32 i = 0; i < extensionCount; i++) { 131 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 132 hasSurfaceExtension = SDL_TRUE; 133 } else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 134 hasMacOSSurfaceExtension = SDL_TRUE; 135 } 136 } 137 SDL_free(extensions); 138 if (!hasSurfaceExtension) { 139 SDL_SetError("Installed Vulkan Portability library doesn't implement the " 140 VK_KHR_SURFACE_EXTENSION_NAME " extension"); 141 goto fail; 142 } else if (!hasMacOSSurfaceExtension) { 143 SDL_SetError("Installed Vulkan Portability library doesn't implement the " 144 VK_MVK_MACOS_SURFACE_EXTENSION_NAME "extension"); 145 goto fail; 146 } 147 return 0; 148 149 fail: 150 SDL_UnloadObject(_this->vulkan_config.loader_handle); 151 _this->vulkan_config.loader_handle = NULL; 152 return -1; 153 } 154 155 void Cocoa_Vulkan_UnloadLibrary(_THIS) 156 { 157 if (_this->vulkan_config.loader_handle) { 158 if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) { 159 SDL_UnloadObject(_this->vulkan_config.loader_handle); 160 } 161 _this->vulkan_config.loader_handle = NULL; 162 } 163 } 164 165 SDL_bool Cocoa_Vulkan_GetInstanceExtensions(_THIS, 166 SDL_Window *window, 167 unsigned *count, 168 const char **names) 169 { 170 static const char *const extensionsForCocoa[] = { 171 VK_KHR_SURFACE_EXTENSION_NAME, VK_MVK_MACOS_SURFACE_EXTENSION_NAME 172 }; 173 if (!_this->vulkan_config.loader_handle) { 174 SDL_SetError("Vulkan is not loaded"); 175 return SDL_FALSE; 176 } 177 return SDL_Vulkan_GetInstanceExtensions_Helper( 178 count, names, SDL_arraysize(extensionsForCocoa), 179 extensionsForCocoa); 180 } 181 182 SDL_bool Cocoa_Vulkan_CreateSurface(_THIS, 183 SDL_Window *window, 184 VkInstance instance, 185 VkSurfaceKHR *surface) 186 { 187 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 188 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; 189 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = 190 (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( 191 (VkInstance)instance, 192 "vkCreateMacOSSurfaceMVK"); 193 VkMacOSSurfaceCreateInfoMVK createInfo = {}; 194 VkResult result; 195 SDL_MetalView metalview; 196 197 if (!_this->vulkan_config.loader_handle) { 198 SDL_SetError("Vulkan is not loaded"); 199 return SDL_FALSE; 200 } 201 202 if (!vkCreateMacOSSurfaceMVK) { 203 SDL_SetError(VK_MVK_MACOS_SURFACE_EXTENSION_NAME 204 " extension is not enabled in the Vulkan instance."); 205 return SDL_FALSE; 206 } 207 208 metalview = Cocoa_Metal_CreateView(_this, window); 209 if (metalview == NULL) { 210 return SDL_FALSE; 211 } 212 213 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; 214 createInfo.pNext = NULL; 215 createInfo.flags = 0; 216 createInfo.pView = (const void *)metalview; 217 result = vkCreateMacOSSurfaceMVK(instance, &createInfo, 218 NULL, surface); 219 if (result != VK_SUCCESS) { 220 Cocoa_Metal_DestroyView(_this, metalview); 221 SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", 222 SDL_Vulkan_GetResultString(result)); 223 return SDL_FALSE; 224 } 225 226 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call 227 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one 228 * from returning a new view object in CreateView, and one because it's 229 * a subview of the window.) If we release the view here to make it +1, it 230 * will be destroyed when the window is destroyed. */ 231 CFBridgingRelease(metalview); 232 233 return SDL_TRUE; 234 } 235 236 void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) 237 { 238 Cocoa_Metal_GetDrawableSize(_this, window, w, h); 239 } 240 241 #endif 242 243 /* vim: set ts=4 sw=4 expandtab: */