| //======================================================================== |
| // GLFW 3.3 - www.glfw.org |
| //------------------------------------------------------------------------ |
| // Copyright (c) 2002-2006 Marcus Geelnard |
| // Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org> |
| // |
| // This software is provided 'as-is', without any express or implied |
| // warranty. In no event will the authors be held liable for any damages |
| // arising from the use of this software. |
| // |
| // Permission is granted to anyone to use this software for any purpose, |
| // including commercial applications, and to alter it and redistribute it |
| // freely, subject to the following restrictions: |
| // |
| // 1. The origin of this software must not be misrepresented; you must not |
| // claim that you wrote the original software. If you use this software |
| // in a product, an acknowledgment in the product documentation would |
| // be appreciated but is not required. |
| // |
| // 2. Altered source versions must be plainly marked as such, and must not |
| // be misrepresented as being the original software. |
| // |
| // 3. This notice may not be removed or altered from any source |
| // distribution. |
| // |
| //======================================================================== |
| |
| #include "internal.h" |
| |
| #include <assert.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #define _GLFW_FIND_LOADER 1 |
| #define _GLFW_REQUIRE_LOADER 2 |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| ////// GLFW internal API ////// |
| ////////////////////////////////////////////////////////////////////////// |
| |
| GLFWbool _glfwInitVulkan(int mode) |
| { |
| VkResult err; |
| VkExtensionProperties* ep; |
| uint32_t i, count; |
| |
| if (_glfw.vk.available) |
| return GLFW_TRUE; |
| |
| #if !defined(_GLFW_VULKAN_STATIC) |
| #if defined(_GLFW_VULKAN_LIBRARY) |
| _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); |
| #elif defined(_GLFW_WIN32) |
| _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); |
| #elif defined(_GLFW_COCOA) |
| _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); |
| #else |
| _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); |
| #endif |
| if (!_glfw.vk.handle) |
| { |
| if (mode == _GLFW_REQUIRE_LOADER) |
| _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); |
| |
| return GLFW_FALSE; |
| } |
| |
| _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) |
| _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); |
| if (!_glfw.vk.GetInstanceProcAddr) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Loader does not export vkGetInstanceProcAddr"); |
| |
| _glfwTerminateVulkan(); |
| return GLFW_FALSE; |
| } |
| |
| _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) |
| vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); |
| if (!_glfw.vk.EnumerateInstanceExtensionProperties) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); |
| |
| _glfwTerminateVulkan(); |
| return GLFW_FALSE; |
| } |
| #endif // _GLFW_VULKAN_STATIC |
| |
| err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); |
| if (err) |
| { |
| // NOTE: This happens on systems with a loader but without any Vulkan ICD |
| if (mode == _GLFW_REQUIRE_LOADER) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Failed to query instance extension count: %s", |
| _glfwGetVulkanResultString(err)); |
| } |
| |
| _glfwTerminateVulkan(); |
| return GLFW_FALSE; |
| } |
| |
| ep = calloc(count, sizeof(VkExtensionProperties)); |
| |
| err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); |
| if (err) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Failed to query instance extensions: %s", |
| _glfwGetVulkanResultString(err)); |
| |
| free(ep); |
| _glfwTerminateVulkan(); |
| return GLFW_FALSE; |
| } |
| |
| for (i = 0; i < count; i++) |
| { |
| if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) |
| _glfw.vk.KHR_surface = GLFW_TRUE; |
| #if defined(_GLFW_WIN32) |
| else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) |
| _glfw.vk.KHR_win32_surface = GLFW_TRUE; |
| #elif defined(_GLFW_COCOA) |
| else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) |
| _glfw.vk.MVK_macos_surface = GLFW_TRUE; |
| #elif defined(_GLFW_X11) |
| else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) |
| _glfw.vk.KHR_xlib_surface = GLFW_TRUE; |
| else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) |
| _glfw.vk.KHR_xcb_surface = GLFW_TRUE; |
| #elif defined(_GLFW_WAYLAND) |
| else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) |
| _glfw.vk.KHR_wayland_surface = GLFW_TRUE; |
| #endif |
| } |
| |
| free(ep); |
| |
| _glfw.vk.available = GLFW_TRUE; |
| |
| _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); |
| |
| return GLFW_TRUE; |
| } |
| |
| void _glfwTerminateVulkan(void) |
| { |
| #if !defined(_GLFW_VULKAN_STATIC) |
| if (_glfw.vk.handle) |
| _glfw_dlclose(_glfw.vk.handle); |
| #endif |
| } |
| |
| const char* _glfwGetVulkanResultString(VkResult result) |
| { |
| switch (result) |
| { |
| case VK_SUCCESS: |
| return "Success"; |
| case VK_NOT_READY: |
| return "A fence or query has not yet completed"; |
| case VK_TIMEOUT: |
| return "A wait operation has not completed in the specified time"; |
| case VK_EVENT_SET: |
| return "An event is signaled"; |
| case VK_EVENT_RESET: |
| return "An event is unsignaled"; |
| case VK_INCOMPLETE: |
| return "A return array was too small for the result"; |
| case VK_ERROR_OUT_OF_HOST_MEMORY: |
| return "A host memory allocation has failed"; |
| case VK_ERROR_OUT_OF_DEVICE_MEMORY: |
| return "A device memory allocation has failed"; |
| case VK_ERROR_INITIALIZATION_FAILED: |
| return "Initialization of an object could not be completed for implementation-specific reasons"; |
| case VK_ERROR_DEVICE_LOST: |
| return "The logical or physical device has been lost"; |
| case VK_ERROR_MEMORY_MAP_FAILED: |
| return "Mapping of a memory object has failed"; |
| case VK_ERROR_LAYER_NOT_PRESENT: |
| return "A requested layer is not present or could not be loaded"; |
| case VK_ERROR_EXTENSION_NOT_PRESENT: |
| return "A requested extension is not supported"; |
| case VK_ERROR_FEATURE_NOT_PRESENT: |
| return "A requested feature is not supported"; |
| case VK_ERROR_INCOMPATIBLE_DRIVER: |
| return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; |
| case VK_ERROR_TOO_MANY_OBJECTS: |
| return "Too many objects of the type have already been created"; |
| case VK_ERROR_FORMAT_NOT_SUPPORTED: |
| return "A requested format is not supported on this device"; |
| case VK_ERROR_SURFACE_LOST_KHR: |
| return "A surface is no longer available"; |
| case VK_SUBOPTIMAL_KHR: |
| return "A swapchain no longer matches the surface properties exactly, but can still be used"; |
| case VK_ERROR_OUT_OF_DATE_KHR: |
| return "A surface has changed in such a way that it is no longer compatible with the swapchain"; |
| case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: |
| return "The display used by a swapchain does not use the same presentable image layout"; |
| case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: |
| return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; |
| case VK_ERROR_VALIDATION_FAILED_EXT: |
| return "A validation layer found an error"; |
| default: |
| return "ERROR: UNKNOWN VULKAN ERROR"; |
| } |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| ////// GLFW public API ////// |
| ////////////////////////////////////////////////////////////////////////// |
| |
| GLFWAPI int glfwVulkanSupported(void) |
| { |
| _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); |
| return _glfwInitVulkan(_GLFW_FIND_LOADER); |
| } |
| |
| GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) |
| { |
| assert(count != NULL); |
| |
| *count = 0; |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
| |
| if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) |
| return NULL; |
| |
| if (!_glfw.vk.extensions[0]) |
| return NULL; |
| |
| *count = 2; |
| return (const char**) _glfw.vk.extensions; |
| } |
| |
| GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, |
| const char* procname) |
| { |
| GLFWvkproc proc; |
| assert(procname != NULL); |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
| |
| if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) |
| return NULL; |
| |
| proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); |
| #if defined(_GLFW_VULKAN_STATIC) |
| if (!proc) |
| { |
| if (strcmp(procname, "vkGetInstanceProcAddr") == 0) |
| return (GLFWvkproc) vkGetInstanceProcAddr; |
| } |
| #else |
| if (!proc) |
| proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); |
| #endif |
| |
| return proc; |
| } |
| |
| GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, |
| VkPhysicalDevice device, |
| uint32_t queuefamily) |
| { |
| assert(instance != VK_NULL_HANDLE); |
| assert(device != VK_NULL_HANDLE); |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); |
| |
| if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) |
| return GLFW_FALSE; |
| |
| if (!_glfw.vk.extensions[0]) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Window surface creation extensions not found"); |
| return GLFW_FALSE; |
| } |
| |
| return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, |
| device, |
| queuefamily); |
| } |
| |
| GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, |
| GLFWwindow* handle, |
| const VkAllocationCallbacks* allocator, |
| VkSurfaceKHR* surface) |
| { |
| _GLFWwindow* window = (_GLFWwindow*) handle; |
| assert(instance != VK_NULL_HANDLE); |
| assert(window != NULL); |
| assert(surface != NULL); |
| |
| *surface = VK_NULL_HANDLE; |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); |
| |
| if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) |
| return VK_ERROR_INITIALIZATION_FAILED; |
| |
| if (!_glfw.vk.extensions[0]) |
| { |
| _glfwInputError(GLFW_API_UNAVAILABLE, |
| "Vulkan: Window surface creation extensions not found"); |
| return VK_ERROR_EXTENSION_NOT_PRESENT; |
| } |
| |
| if (window->context.client != GLFW_NO_API) |
| { |
| _glfwInputError(GLFW_INVALID_VALUE, |
| "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); |
| return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; |
| } |
| |
| return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); |
| } |
| |