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