#include "../../SDL_internal.h"
#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_UIKIT
#include "SDL_uikitvideo.h"
#include "SDL_uikitwindow.h"
#include "SDL_loadso.h"
#include "SDL_uikitvulkan.h"
#include "SDL_uikitmetalview.h"
#include "SDL_syswm.h"
#include <dlfcn.h>
const char* defaultPaths[] = {
"libvulkan.dylib",
};
#define DEFAULT_HANDLE RTLD_DEFAULT
int UIKit_Vulkan_LoadLibrary(_THIS, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 extensionCount = 0;
SDL_bool hasSurfaceExtension = SDL_FALSE;
SDL_bool hasMetalSurfaceExtension = SDL_FALSE;
SDL_bool hasIOSSurfaceExtension = SDL_FALSE;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan Portability library is already loaded.");
}
if (!path) {
path = SDL_getenv("SDL_VULKAN_LIBRARY");
}
if (!path) {
vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE,
"vkGetInstanceProcAddr");
}
if (vkGetInstanceProcAddr) {
_this->vulkan_config.loader_handle = DEFAULT_HANDLE;
} else {
const char** paths;
const char *foundPath = NULL;
int numPaths;
int i;
if (path) {
paths = &path;
numPaths = 1;
} else {
paths = defaultPaths;
numPaths = SDL_arraysize(defaultPaths);
}
for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
foundPath = paths[i];
_this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
}
if (_this->vulkan_config.loader_handle == NULL) {
return SDL_SetError("Failed to load Vulkan Portability library");
}
SDL_strlcpy(_this->vulkan_config.loader_path, path,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle,
"vkGetInstanceProcAddr");
}
if (!vkGetInstanceProcAddr) {
SDL_SetError("Failed to find %s in either executable or %s: %s",
"vkGetInstanceProcAddr",
"linked Vulkan Portability library",
(const char *) dlerror());
goto fail;
}
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
SDL_SetError("No vkEnumerateInstanceExtensionProperties found.");
goto fail;
}
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (!extensions) {
goto fail;
}
for (Uint32 i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = SDL_TRUE;
} else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasMetalSurfaceExtension = SDL_TRUE;
} else if (SDL_strcmp(VK_MVK_IOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasIOSSurfaceExtension = SDL_TRUE;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan Portability doesn't implement the "
VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasMetalSurfaceExtension && !hasIOSSurfaceExtension) {
SDL_SetError("Installed Vulkan Portability doesn't implement the "
VK_EXT_METAL_SURFACE_EXTENSION_NAME " or "
VK_MVK_IOS_SURFACE_EXTENSION_NAME " extensions");
goto fail;
}
return 0;
fail:
_this->vulkan_config.loader_handle = NULL;
return -1;
}
void UIKit_Vulkan_UnloadLibrary(_THIS)
{
if (_this->vulkan_config.loader_handle) {
if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
}
_this->vulkan_config.loader_handle = NULL;
}
}
SDL_bool UIKit_Vulkan_GetInstanceExtensions(_THIS,
SDL_Window *window,
unsigned *count,
const char **names)
{
static const char *const extensionsForUIKit[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME
};
if (!_this->vulkan_config.loader_handle) {
SDL_SetError("Vulkan is not loaded");
return SDL_FALSE;
}
return SDL_Vulkan_GetInstanceExtensions_Helper(
count, names, SDL_arraysize(extensionsForUIKit),
extensionsForUIKit);
}
SDL_bool UIKit_Vulkan_CreateSurface(_THIS,
SDL_Window *window,
VkInstance instance,
VkSurfaceKHR *surface)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
(PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
(VkInstance)instance,
"vkCreateMetalSurfaceEXT");
PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK =
(PFN_vkCreateIOSSurfaceMVK)vkGetInstanceProcAddr(
(VkInstance)instance,
"vkCreateIOSSurfaceMVK");
VkResult result;
SDL_MetalView metalview;
if (!_this->vulkan_config.loader_handle) {
SDL_SetError("Vulkan is not loaded");
return SDL_FALSE;
}
if (!vkCreateMetalSurfaceEXT && !vkCreateIOSSurfaceMVK) {
SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or "
VK_MVK_IOS_SURFACE_EXTENSION_NAME
" extensions are not enabled in the Vulkan instance.");
return SDL_FALSE;
}
metalview = UIKit_Metal_CreateView(_this, window);
if (metalview == NULL) {
return SDL_FALSE;
}
if (vkCreateMetalSurfaceEXT) {
VkMetalSurfaceCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pLayer = (__bridge const CAMetalLayer *)
UIKit_Metal_GetLayer(_this, metalview);
result = vkCreateMetalSurfaceEXT(instance, &createInfo, NULL, surface);
if (result != VK_SUCCESS) {
UIKit_Metal_DestroyView(_this, metalview);
SDL_SetError("vkCreateMetalSurfaceEXT failed: %s",
SDL_Vulkan_GetResultString(result));
return SDL_FALSE;
}
} else {
VkIOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = (const void *)metalview;
result = vkCreateIOSSurfaceMVK(instance, &createInfo,
NULL, surface);
if (result != VK_SUCCESS) {
UIKit_Metal_DestroyView(_this, metalview);
SDL_SetError("vkCreateIOSSurfaceMVK failed: %s",
SDL_Vulkan_GetResultString(result));
return SDL_FALSE;
}
}
CFBridgingRelease(metalview);
return SDL_TRUE;
}
void UIKit_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
{
UIKit_Metal_GetDrawableSize(_this, window, w, h);
}
#endif