From be6ca785e321503e07a8898d5e9c76ee37e409da Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sun, 25 Feb 2018 23:02:09 -0800 Subject: [PATCH] Support official Vulkan SDK for macOS. This tries to load vulkan.framework or libvulkan.1.dylib before MoltenVK.framework or libMoltenVK.dylib. In the previous version, layers would not work for applications run-time loading the default library. --- include/SDL_vulkan.h | 39 ++++++++++++++++++--------- src/video/cocoa/SDL_cocoavulkan.m | 44 +++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/include/SDL_vulkan.h b/include/SDL_vulkan.h index d238e2236..f04c21adb 100644 --- a/include/SDL_vulkan.h +++ b/include/SDL_vulkan.h @@ -69,30 +69,43 @@ typedef VkSurfaceKHR SDL_vulkanSurface; /* for compatibility with Tizen */ * \brief Dynamically load a Vulkan loader library. * * \param [in] path The platform dependent Vulkan loader library name, or - * \c NULL to open the default Vulkan loader library. + * \c NULL. * * \return \c 0 on success, or \c -1 if the library couldn't be loaded. * - * This should be done after initializing the video driver, but before + * If \a path is NULL SDL will use the value of the environment variable + * \c SDL_VULKAN_LIBRARY, if set, otherwise it loads the default Vulkan + * loader library. + * + * This should be called after initializing the video driver, but before * creating any Vulkan windows. If no Vulkan loader library is loaded, the * default library will be loaded upon creation of the first Vulkan window. * - * \note If you specify a non-NULL \a path, you should retrieve all of the - * Vulkan functions used in your program from the dynamic library using + * \note It is fairly common for Vulkan applications to link with \a libvulkan + * instead of explicitly loading it at run time. This will work with + * SDL provided the application links to a dynamic library and both it + * and SDL use the same search path. + * + * \note If you specify a non-NULL \c path, an application should retrieve all + * of the Vulkan functions it uses from the dynamic library using * \c SDL_Vulkan_GetVkGetInstanceProcAddr() unless you can guarantee - * \a path points to the same vulkan loader library that you linked to. + * \c path points to the same vulkan loader library the application + * linked to. * * \note On Apple devices, if \a path is NULL, SDL will attempt to find * the vkGetInstanceProcAddr address within all the mach-o images of - * the current process. This is because the currently (v0.17.0) - * recommended MoltenVK (Vulkan on Metal) usage is as a static library. - * If it is not found then SDL will attempt to load \c libMoltenVK.dylib. - * Applications using the dylib alternative therefore do not need to do - * anything special when calling SDL. + * the current process. This is because it is fairly common for Vulkan + * applications to link with libvulkan (and historically MoltenVK was + * provided as a static library). If it is not found then, on macOS, SDL + * will attempt to load \c vulkan.framework/vulkan, \c libvulkan.1.dylib, + * \c MoltenVK.framework/MoltenVK and \c libMoltenVK.dylib in that order. + * On iOS SDL will attempt to load \c libMoltenVK.dylib. Applications + * using a dynamic framework or .dylib must ensure it is included in its + * application bundle. * - * \note On non-Apple devices, SDL requires you to either not link to the - * Vulkan loader or link to a dynamic library version. This limitation - * may be removed in a future version of SDL. + * \note On non-Apple devices, application linking with a static libvulkan is + * not supported. Either do not link to the Vulkan loader or link to a + * dynamic library version. * * \note This function will fail if there are no working Vulkan drivers * installed. diff --git a/src/video/cocoa/SDL_cocoavulkan.m b/src/video/cocoa/SDL_cocoavulkan.m index ce7277442..2cf55bb5a 100644 --- a/src/video/cocoa/SDL_cocoavulkan.m +++ b/src/video/cocoa/SDL_cocoavulkan.m @@ -39,7 +39,13 @@ #include -#define DEFAULT_MOLTENVK "libMoltenVK.dylib" +const char* defaultPaths[] = { + "vulkan.framework/vulkan", + "libvulkan.1.dylib", + "MoltenVK.framework/MoltenVK", + "libMoltenVK.dylib" +}; + /* Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF. */ #define DEFAULT_HANDLE RTLD_DEFAULT @@ -52,7 +58,7 @@ int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; if (_this->vulkan_config.loader_handle) { - SDL_SetError("MoltenVK/Vulkan already loaded"); + SDL_SetError("Vulkan/MoltenVK already loaded"); return -1; } @@ -60,6 +66,7 @@ int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) if (!path) { path = SDL_getenv("SDL_VULKAN_LIBRARY"); } + if (!path) { /* MoltenVK framework, currently, v0.17.0, has a static library and is * the recommended way to use the package. There is likely no object to @@ -68,20 +75,35 @@ int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) (PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE, "vkGetInstanceProcAddr"); } - + if (vkGetInstanceProcAddr) { _this->vulkan_config.loader_handle = DEFAULT_HANDLE; } else { - if (!path) { - /* Look for the .dylib packaged with the application instead. */ - path = DEFAULT_MOLTENVK; + const char** paths; + int numPaths; + int i; + + if (path) { + paths = &path; + numPaths = 1; + } else { + /* Look for framework or .dylib packaged with the application + * instead. */ + paths = defaultPaths; + numPaths = SDL_arraysize(defaultPaths); } - _this->vulkan_config.loader_handle = SDL_LoadObject(path); - if (!_this->vulkan_config.loader_handle) { - return -1; + for (i=0; i < numPaths; i++) { + _this->vulkan_config.loader_handle = SDL_LoadObject(paths[i]); + if (_this->vulkan_config.loader_handle) + break; + else + continue; } - SDL_strlcpy(_this->vulkan_config.loader_path, path, + if (i == numPaths) + return -1; + + SDL_strlcpy(_this->vulkan_config.loader_path, paths[i], SDL_arraysize(_this->vulkan_config.loader_path)); vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr"); @@ -90,7 +112,7 @@ int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) if (!vkGetInstanceProcAddr) { SDL_SetError("Failed to find %s in either executable or %s: %s", "vkGetInstanceProcAddr", - DEFAULT_MOLTENVK, + _this->vulkan_config.loader_path, (const char *) dlerror()); goto fail; }