Atlas - SDL_cocoavulkan.m
Home / ext / SDL / src / video / cocoa Lines: 1 | Size: 12323 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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 defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA) 29 30#include "SDL_cocoavideo.h" 31#include "SDL_cocoawindow.h" 32 33#include "SDL_cocoametalview.h" 34#include "SDL_cocoavulkan.h" 35 36#include <dlfcn.h> 37 38const char *defaultPaths[] = { 39 "@executable_path/../Frameworks/libMoltenVK.dylib", 40 "vulkan.framework/vulkan", 41 "libvulkan.1.dylib", 42 "libvulkan.dylib", 43 "MoltenVK.framework/MoltenVK", 44 "libMoltenVK.dylib" 45}; 46 47// Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF. 48#define DEFAULT_HANDLE RTLD_DEFAULT 49 50bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path) 51{ 52 VkExtensionProperties *extensions = NULL; 53 Uint32 extensionCount = 0; 54 bool hasSurfaceExtension = false; 55 bool hasMetalSurfaceExtension = false; 56 bool hasMacOSSurfaceExtension = 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_GetHint(SDL_HINT_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 = true; 133 } else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 134 hasMetalSurfaceExtension = true; 135 } else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 136 hasMacOSSurfaceExtension = true; 137 } 138 } 139 SDL_free(extensions); 140 if (!hasSurfaceExtension) { 141 SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension"); 142 goto fail; 143 } else if (!hasMetalSurfaceExtension && !hasMacOSSurfaceExtension) { 144 SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME " extensions"); 145 goto fail; 146 } 147 return true; 148 149fail: 150 SDL_UnloadObject(_this->vulkan_config.loader_handle); 151 _this->vulkan_config.loader_handle = NULL; 152 return false; 153} 154 155void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_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 165char const * const *Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count) 166{ 167 static const char *const extensionsForCocoa[] = { 168 VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME 169 }; 170 if(count) { 171 *count = SDL_arraysize(extensionsForCocoa); 172 } 173 return extensionsForCocoa; 174} 175 176static bool Cocoa_Vulkan_CreateSurfaceViaMetalView(SDL_VideoDevice *_this, 177 SDL_Window *window, 178 VkInstance instance, 179 const struct VkAllocationCallbacks *allocator, 180 VkSurfaceKHR *surface, 181 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT, 182 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK) 183{ 184 VkResult rc; 185 SDL_MetalView metalview = Cocoa_Metal_CreateView(_this, window); 186 if (metalview == NULL) { 187 return false; 188 } 189 190 if (vkCreateMetalSurfaceEXT) { 191 VkMetalSurfaceCreateInfoEXT createInfo = {}; 192 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; 193 createInfo.pNext = NULL; 194 createInfo.flags = 0; 195 createInfo.pLayer = (__bridge const CAMetalLayer *) 196 Cocoa_Metal_GetLayer(_this, metalview); 197 rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface); 198 if (rc != VK_SUCCESS) { 199 Cocoa_Metal_DestroyView(_this, metalview); 200 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc)); 201 } 202 } else { 203 VkMacOSSurfaceCreateInfoMVK createInfo = {}; 204 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; 205 createInfo.pNext = NULL; 206 createInfo.flags = 0; 207 createInfo.pView = (const void *)metalview; 208 rc = vkCreateMacOSSurfaceMVK(instance, &createInfo, 209 NULL, surface); 210 if (rc != VK_SUCCESS) { 211 Cocoa_Metal_DestroyView(_this, metalview); 212 return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc)); 213 } 214 } 215 216 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call 217 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one 218 * from returning a new view object in CreateView, and one because it's 219 * a subview of the window.) If we release the view here to make it +1, it 220 * will be destroyed when the window is destroyed. 221 * 222 * TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough 223 * knowledge of Metal can proceed. */ 224 CFBridgingRelease(metalview); 225 226 return true; // success! 227} 228 229bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this, 230 SDL_Window *window, 231 VkInstance instance, 232 const struct VkAllocationCallbacks *allocator, 233 VkSurfaceKHR *surface) 234{ 235 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 236 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; 237 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT = 238 (PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr( 239 instance, 240 "vkCreateMetalSurfaceEXT"); 241 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = 242 (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( 243 instance, 244 "vkCreateMacOSSurfaceMVK"); 245 VkResult rc; 246 247 if (!_this->vulkan_config.loader_handle) { 248 return SDL_SetError("Vulkan is not loaded"); 249 } 250 251 if (!vkCreateMetalSurfaceEXT && !vkCreateMacOSSurfaceMVK) { 252 return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME 253 " extensions are not enabled in the Vulkan instance."); 254 } 255 256 if (window->flags & SDL_WINDOW_EXTERNAL) { 257 @autoreleasepool { 258 SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; 259 if (![data.sdlContentView.layer isKindOfClass:[CAMetalLayer class]]) { 260 [data.sdlContentView setLayer:[CAMetalLayer layer]]; 261 } 262 263 if (vkCreateMetalSurfaceEXT) { 264 VkMetalSurfaceCreateInfoEXT createInfo = {}; 265 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; 266 createInfo.pNext = NULL; 267 createInfo.flags = 0; 268 createInfo.pLayer = (CAMetalLayer *)data.sdlContentView.layer; 269 rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface); 270 if (rc != VK_SUCCESS) { 271 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc)); 272 } 273 } else { 274 VkMacOSSurfaceCreateInfoMVK createInfo = {}; 275 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; 276 createInfo.pNext = NULL; 277 createInfo.flags = 0; 278 createInfo.pView = (__bridge const void *)data.sdlContentView; 279 rc = vkCreateMacOSSurfaceMVK(instance, &createInfo, 280 allocator, surface); 281 if (rc != VK_SUCCESS) { 282 return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc)); 283 } 284 } 285 } 286 } else { 287 return Cocoa_Vulkan_CreateSurfaceViaMetalView(_this, window, instance, allocator, surface, vkCreateMetalSurfaceEXT, vkCreateMacOSSurfaceMVK); 288 } 289 290 return true; 291} 292 293void Cocoa_Vulkan_DestroySurface(SDL_VideoDevice *_this, 294 VkInstance instance, 295 VkSurfaceKHR surface, 296 const struct VkAllocationCallbacks *allocator) 297{ 298 if (_this->vulkan_config.loader_handle) { 299 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator); 300 // TODO: Add CFBridgingRelease(metalview) here perhaps? 301 } 302} 303 304#endif 305[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.