Commit be6ca785e321503e07a8898d5e9c76ee37e409da

Mark Callow 2018-02-25T23:02:09

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.

diff --git a/include/SDL_vulkan.h b/include/SDL_vulkan.h
index d238e22..f04c21a 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.
- *
- *  \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.
+ *        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, 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 ce72774..2cf55bb 100644
--- a/src/video/cocoa/SDL_cocoavulkan.m
+++ b/src/video/cocoa/SDL_cocoavulkan.m
@@ -39,7 +39,13 @@
 
 #include <dlfcn.h>
 
-#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;
     }