Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / uikit / SDL_uikitvulkan.m
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: */