summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m265
1 files changed, 265 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m
new file mode 100644
index 0000000..332593b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitvulkan.m
@@ -0,0 +1,265 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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 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,
171 Uint32 *count)
172{
173 static const char *const extensionsForUIKit[] = {
174 VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME
175 };
176 if(count) {
177 *count = SDL_arraysize(extensionsForUIKit);
178 }
179 return extensionsForUIKit;
180}
181
182bool UIKit_Vulkan_CreateSurface(SDL_VideoDevice *_this,
183 SDL_Window *window,
184 VkInstance instance,
185 const struct VkAllocationCallbacks *allocator,
186 VkSurfaceKHR *surface)
187{
188 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
189 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
190 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
191 (PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
192 (VkInstance)instance,
193 "vkCreateMetalSurfaceEXT");
194 PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK =
195 (PFN_vkCreateIOSSurfaceMVK)vkGetInstanceProcAddr(
196 (VkInstance)instance,
197 "vkCreateIOSSurfaceMVK");
198 VkResult result;
199 SDL_MetalView metalview;
200
201 if (!_this->vulkan_config.loader_handle) {
202 return SDL_SetError("Vulkan is not loaded");
203 }
204
205 if (!vkCreateMetalSurfaceEXT && !vkCreateIOSSurfaceMVK) {
206 return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_IOS_SURFACE_EXTENSION_NAME
207 " extensions are not enabled in the Vulkan instance.");
208 }
209
210 metalview = UIKit_Metal_CreateView(_this, window);
211 if (metalview == NULL) {
212 return false;
213 }
214
215 if (vkCreateMetalSurfaceEXT) {
216 VkMetalSurfaceCreateInfoEXT createInfo = {};
217 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
218 createInfo.pNext = NULL;
219 createInfo.flags = 0;
220 createInfo.pLayer = (__bridge const CAMetalLayer *)
221 UIKit_Metal_GetLayer(_this, metalview);
222 result = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
223 if (result != VK_SUCCESS) {
224 UIKit_Metal_DestroyView(_this, metalview);
225 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(result));
226 }
227 } else {
228 VkIOSSurfaceCreateInfoMVK createInfo = {};
229 createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
230 createInfo.pNext = NULL;
231 createInfo.flags = 0;
232 createInfo.pView = (const void *)metalview;
233 result = vkCreateIOSSurfaceMVK(instance, &createInfo,
234 allocator, surface);
235 if (result != VK_SUCCESS) {
236 UIKit_Metal_DestroyView(_this, metalview);
237 return SDL_SetError("vkCreateIOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(result));
238 }
239 }
240
241 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call
242 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one
243 * from returning a new view object in CreateView, and one because it's
244 * a subview of the window.) If we release the view here to make it +1, it
245 * will be destroyed when the window is destroyed.
246 *
247 * TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough
248 * knowledge of Metal can proceed. */
249 CFBridgingRelease(metalview);
250
251 return true;
252}
253
254void UIKit_Vulkan_DestroySurface(SDL_VideoDevice *_this,
255 VkInstance instance,
256 VkSurfaceKHR surface,
257 const struct VkAllocationCallbacks *allocator)
258{
259 if (_this->vulkan_config.loader_handle) {
260 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
261 // TODO: Add CFBridgingRelease(metalview) here perhaps?
262 }
263}
264
265#endif