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