summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m304
1 files changed, 304 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m
new file mode 100644
index 0000000..a440627
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoavulkan.m
@@ -0,0 +1,304 @@
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#include "SDL_internal.h"
27
28#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA)
29
30#include "SDL_cocoavideo.h"
31#include "SDL_cocoawindow.h"
32
33#include "SDL_cocoametalview.h"
34#include "SDL_cocoavulkan.h"
35
36#include <dlfcn.h>
37
38const char *defaultPaths[] = {
39 "vulkan.framework/vulkan",
40 "libvulkan.1.dylib",
41 "libvulkan.dylib",
42 "MoltenVK.framework/MoltenVK",
43 "libMoltenVK.dylib"
44};
45
46// Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF.
47#define DEFAULT_HANDLE RTLD_DEFAULT
48
49bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
50{
51 VkExtensionProperties *extensions = NULL;
52 Uint32 extensionCount = 0;
53 bool hasSurfaceExtension = false;
54 bool hasMetalSurfaceExtension = false;
55 bool hasMacOSSurfaceExtension = 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_GetHint(SDL_HINT_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 framework or .dylib packaged with the application
87 * instead. */
88 paths = defaultPaths;
89 numPaths = SDL_arraysize(defaultPaths);
90 }
91
92 for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
93 foundPath = paths[i];
94 _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
95 }
96
97 if (_this->vulkan_config.loader_handle == NULL) {
98 return SDL_SetError("Failed to load Vulkan Portability library");
99 }
100
101 SDL_strlcpy(_this->vulkan_config.loader_path, foundPath,
102 SDL_arraysize(_this->vulkan_config.loader_path));
103 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
104 _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
105 }
106
107 if (!vkGetInstanceProcAddr) {
108 SDL_SetError("Failed to find %s in either executable or %s: %s",
109 "vkGetInstanceProcAddr",
110 _this->vulkan_config.loader_path,
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 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
120 goto fail;
121 }
122 extensions = SDL_Vulkan_CreateInstanceExtensionsList(
123 (PFN_vkEnumerateInstanceExtensionProperties)
124 _this->vulkan_config.vkEnumerateInstanceExtensionProperties,
125 &extensionCount);
126 if (!extensions) {
127 goto fail;
128 }
129 for (Uint32 i = 0; i < extensionCount; i++) {
130 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
131 hasSurfaceExtension = true;
132 } else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
133 hasMetalSurfaceExtension = true;
134 } else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
135 hasMacOSSurfaceExtension = true;
136 }
137 }
138 SDL_free(extensions);
139 if (!hasSurfaceExtension) {
140 SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
141 goto fail;
142 } else if (!hasMetalSurfaceExtension && !hasMacOSSurfaceExtension) {
143 SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME " extensions");
144 goto fail;
145 }
146 return true;
147
148fail:
149 SDL_UnloadObject(_this->vulkan_config.loader_handle);
150 _this->vulkan_config.loader_handle = NULL;
151 return false;
152}
153
154void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
155{
156 if (_this->vulkan_config.loader_handle) {
157 if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) {
158 SDL_UnloadObject(_this->vulkan_config.loader_handle);
159 }
160 _this->vulkan_config.loader_handle = NULL;
161 }
162}
163
164char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
165 Uint32 *count)
166{
167 static const char *const extensionsForCocoa[] = {
168 VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
169 };
170 if(count) {
171 *count = SDL_arraysize(extensionsForCocoa);
172 }
173 return extensionsForCocoa;
174}
175
176static bool Cocoa_Vulkan_CreateSurfaceViaMetalView(SDL_VideoDevice *_this,
177 SDL_Window *window,
178 VkInstance instance,
179 const struct VkAllocationCallbacks *allocator,
180 VkSurfaceKHR *surface,
181 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT,
182 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK)
183{
184 VkResult rc;
185 SDL_MetalView metalview = Cocoa_Metal_CreateView(_this, window);
186 if (metalview == NULL) {
187 return false;
188 }
189
190 if (vkCreateMetalSurfaceEXT) {
191 VkMetalSurfaceCreateInfoEXT createInfo = {};
192 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
193 createInfo.pNext = NULL;
194 createInfo.flags = 0;
195 createInfo.pLayer = (__bridge const CAMetalLayer *)
196 Cocoa_Metal_GetLayer(_this, metalview);
197 rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
198 if (rc != VK_SUCCESS) {
199 Cocoa_Metal_DestroyView(_this, metalview);
200 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
201 }
202 } else {
203 VkMacOSSurfaceCreateInfoMVK createInfo = {};
204 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
205 createInfo.pNext = NULL;
206 createInfo.flags = 0;
207 createInfo.pView = (const void *)metalview;
208 rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
209 NULL, surface);
210 if (rc != VK_SUCCESS) {
211 Cocoa_Metal_DestroyView(_this, metalview);
212 return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
213 }
214 }
215
216 /* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call
217 * Metal_DestroyView from. Right now the metal view's ref count is +2 (one
218 * from returning a new view object in CreateView, and one because it's
219 * a subview of the window.) If we release the view here to make it +1, it
220 * will be destroyed when the window is destroyed.
221 *
222 * TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough
223 * knowledge of Metal can proceed. */
224 CFBridgingRelease(metalview);
225
226 return true; // success!
227}
228
229bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this,
230 SDL_Window *window,
231 VkInstance instance,
232 const struct VkAllocationCallbacks *allocator,
233 VkSurfaceKHR *surface)
234{
235 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
236 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
237 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
238 (PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
239 instance,
240 "vkCreateMetalSurfaceEXT");
241 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK =
242 (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr(
243 instance,
244 "vkCreateMacOSSurfaceMVK");
245 VkResult rc;
246
247 if (!_this->vulkan_config.loader_handle) {
248 return SDL_SetError("Vulkan is not loaded");
249 }
250
251 if (!vkCreateMetalSurfaceEXT && !vkCreateMacOSSurfaceMVK) {
252 return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME
253 " extensions are not enabled in the Vulkan instance.");
254 }
255
256 if (window->flags & SDL_WINDOW_EXTERNAL) {
257 @autoreleasepool {
258 SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
259 if (![data.sdlContentView.layer isKindOfClass:[CAMetalLayer class]]) {
260 [data.sdlContentView setLayer:[CAMetalLayer layer]];
261 }
262
263 if (vkCreateMetalSurfaceEXT) {
264 VkMetalSurfaceCreateInfoEXT createInfo = {};
265 createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
266 createInfo.pNext = NULL;
267 createInfo.flags = 0;
268 createInfo.pLayer = (CAMetalLayer *)data.sdlContentView.layer;
269 rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
270 if (rc != VK_SUCCESS) {
271 return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
272 }
273 } else {
274 VkMacOSSurfaceCreateInfoMVK createInfo = {};
275 createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
276 createInfo.pNext = NULL;
277 createInfo.flags = 0;
278 createInfo.pView = (__bridge const void *)data.sdlContentView;
279 rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
280 allocator, surface);
281 if (rc != VK_SUCCESS) {
282 return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
283 }
284 }
285 }
286 } else {
287 return Cocoa_Vulkan_CreateSurfaceViaMetalView(_this, window, instance, allocator, surface, vkCreateMetalSurfaceEXT, vkCreateMacOSSurfaceMVK);
288 }
289
290 return true;
291}
292
293void Cocoa_Vulkan_DestroySurface(SDL_VideoDevice *_this,
294 VkInstance instance,
295 VkSurfaceKHR surface,
296 const struct VkAllocationCallbacks *allocator)
297{
298 if (_this->vulkan_config.loader_handle) {
299 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
300 // TODO: Add CFBridgingRelease(metalview) here perhaps?
301 }
302}
303
304#endif