Atlas - SDL_uikitvulkan.m
Home / ext / SDL / src / video / uikit Lines: 1 | Size: 9765 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 27#include "SDL_internal.h" 28 29#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_UIKIT) 30 31#include "SDL_uikitvideo.h" 32#include "SDL_uikitwindow.h" 33 34#include "SDL_uikitvulkan.h" 35#include "SDL_uikitmetalview.h" 36 37#include <dlfcn.h> 38 39const char *defaultPaths[] = { 40 "libvulkan.dylib", 41}; 42 43/* Since libSDL is static, could use RTLD_SELF. Using RTLD_DEFAULT is future 44 * proofing. */ 45#define DEFAULT_HANDLE RTLD_DEFAULT 46 47bool UIKit_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path) 48{ 49 VkExtensionProperties *extensions = NULL; 50 Uint32 extensionCount = 0; 51 bool hasSurfaceExtension = false; 52 bool hasMetalSurfaceExtension = false; 53 bool hasIOSSurfaceExtension = false; 54 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; 55 56 if (_this->vulkan_config.loader_handle) { 57 return SDL_SetError("Vulkan Portability library is already loaded."); 58 } 59 60 // Load the Vulkan loader library 61 if (!path) { 62 path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY); 63 } 64 65 if (!path) { 66 // Handle the case where Vulkan Portability is linked statically. 67 vkGetInstanceProcAddr = 68 (PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE, 69 "vkGetInstanceProcAddr"); 70 } 71 72 if (vkGetInstanceProcAddr) { 73 _this->vulkan_config.loader_handle = DEFAULT_HANDLE; 74 } else { 75 const char **paths; 76 const char *foundPath = NULL; 77 int numPaths; 78 int i; 79 80 if (path) { 81 paths = &path; 82 numPaths = 1; 83 } else { 84 // Look for the .dylib packaged with the application instead. 85 paths = defaultPaths; 86 numPaths = SDL_arraysize(defaultPaths); 87 } 88 89 for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) { 90 foundPath = paths[i]; 91 _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath); 92 } 93 94 if (_this->vulkan_config.loader_handle == NULL) { 95 return SDL_SetError("Failed to load Vulkan Portability library"); 96 } 97 98 SDL_strlcpy(_this->vulkan_config.loader_path, path, 99 SDL_arraysize(_this->vulkan_config.loader_path)); 100 vkGetInstanceProcAddr = 101 (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( 102 _this->vulkan_config.loader_handle, 103 "vkGetInstanceProcAddr"); 104 } 105 106 if (!vkGetInstanceProcAddr) { 107 SDL_SetError("Failed to find %s in either executable or %s: %s", 108 "vkGetInstanceProcAddr", 109 "linked Vulkan Portability library", 110 (const char *)dlerror()); 111 goto fail; 112 } 113 114 _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; 115 _this->vulkan_config.vkEnumerateInstanceExtensionProperties = 116 (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( 117 VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"); 118 119 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { 120 SDL_SetError("No vkEnumerateInstanceExtensionProperties found."); 121 goto fail; 122 } 123 124 extensions = SDL_Vulkan_CreateInstanceExtensionsList( 125 (PFN_vkEnumerateInstanceExtensionProperties) 126 _this->vulkan_config.vkEnumerateInstanceExtensionProperties, 127 &extensionCount); 128 129 if (!extensions) { 130 goto fail; 131 } 132 133 for (Uint32 i = 0; i < extensionCount; i++) { 134 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 135 hasSurfaceExtension = true; 136 } else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 137 hasMetalSurfaceExtension = true; 138 } else if (SDL_strcmp(VK_MVK_IOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { 139 hasIOSSurfaceExtension = true; 140 } 141 } 142 143 SDL_free(extensions); 144 145 if (!hasSurfaceExtension) { 146 SDL_SetError("Installed Vulkan Portability doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension"); 147 goto fail; 148 } else if (!hasMetalSurfaceExtension && !hasIOSSurfaceExtension) { 149 SDL_SetError("Installed Vulkan Portability doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_IOS_SURFACE_EXTENSION_NAME " extensions"); 150 goto fail; 151 } 152 153 return true; 154 155fail: 156 _this->vulkan_config.loader_handle = NULL; 157 return false; 158} 159 160void UIKit_Vulkan_UnloadLibrary(SDL_VideoDevice *_this) 161{ 162 if (_this->vulkan_config.loader_handle) { 163 if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) { 164 SDL_UnloadObject(_this->vulkan_config.loader_handle); 165 } 166 _this->vulkan_config.loader_handle = NULL; 167 } 168} 169 170char const * const *UIKit_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count) 171{ 172 static const char *const extensionsForUIKit[] = { 173 VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME 174 }; 175 if(count) { 176 *count = SDL_arraysize(extensionsForUIKit); 177 } 178 return extensionsForUIKit; 179} 180 181bool UIKit_Vulkan_CreateSurface(SDL_VideoDevice *_this, 182 SDL_Window *window, 183 VkInstance instance, 184 const struct VkAllocationCallbacks *allocator, 185 VkSurfaceKHR *surface) 186{ 187 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 188 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; 189 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT = 190 (PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr( 191 (VkInstance)instance, 192 "vkCreateMetalSurfaceEXT"); 193 PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK = 194 (PFN_vkCreateIOSSurfaceMVK)vkGetInstanceProcAddr( 195 (VkInstance)instance, 196 "vkCreateIOSSurfaceMVK"); 197 VkResult result; 198 SDL_MetalView metalview; 199 200 if (!_this->vulkan_config.loader_handle) { 201 return SDL_SetError("Vulkan is not loaded"); 202 } 203 204 if (!vkCreateMetalSurfaceEXT && !vkCreateIOSSurfaceMVK) { 205 return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_IOS_SURFACE_EXTENSION_NAME 206 " extensions are not enabled in the Vulkan instance."); 207 } 208 209 metalview = UIKit_Metal_CreateView(_this, window); 210 if (metalview == NULL) { 211 return false; 212 } 213 214 if (vkCreateMetalSurfaceEXT) { 215 VkMetalSurfaceCreateInfoEXT createInfo = {}; 216 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; 217 createInfo.pNext = NULL; 218 createInfo.flags = 0; 219 createInfo.pLayer = (__bridge const CAMetalLayer *) 220 UIKit_Metal_GetLayer(_this, metalview); 221 result = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface); 222 if (result != VK_SUCCESS) { 223 UIKit_Metal_DestroyView(_this, metalview); 224 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(result)); 225 } 226 } else { 227 VkIOSSurfaceCreateInfoMVK createInfo = {}; 228 createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; 229 createInfo.pNext = NULL; 230 createInfo.flags = 0; 231 createInfo.pView = (const void *)metalview; 232 result = vkCreateIOSSurfaceMVK(instance, &createInfo, 233 allocator, surface); 234 if (result != VK_SUCCESS) { 235 UIKit_Metal_DestroyView(_this, metalview); 236 return SDL_SetError("vkCreateIOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(result)); 237 } 238 } 239 240 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call 241 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one 242 * from returning a new view object in CreateView, and one because it's 243 * a subview of the window.) If we release the view here to make it +1, it 244 * will be destroyed when the window is destroyed. 245 * 246 * TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough 247 * knowledge of Metal can proceed. */ 248 CFBridgingRelease(metalview); 249 250 return true; 251} 252 253void UIKit_Vulkan_DestroySurface(SDL_VideoDevice *_this, 254 VkInstance instance, 255 VkSurfaceKHR surface, 256 const struct VkAllocationCallbacks *allocator) 257{ 258 if (_this->vulkan_config.loader_handle) { 259 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator); 260 // TODO: Add CFBridgingRelease(metalview) here perhaps? 261 } 262} 263 264#endif 265[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.