xref: /third_party/glfw/src/vulkan.c (revision b877906b)
1//========================================================================
2// GLFW 3.5 - www.glfw.org
3//------------------------------------------------------------------------
4// Copyright (c) 2002-2006 Marcus Geelnard
5// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
6//
7// This software is provided 'as-is', without any express or implied
8// warranty. In no event will the authors be held liable for any damages
9// arising from the use of this software.
10//
11// Permission is granted to anyone to use this software for any purpose,
12// including commercial applications, and to alter it and redistribute it
13// freely, subject to the following restrictions:
14//
15// 1. The origin of this software must not be misrepresented; you must not
16//    claim that you wrote the original software. If you use this software
17//    in a product, an acknowledgment in the product documentation would
18//    be appreciated but is not required.
19//
20// 2. Altered source versions must be plainly marked as such, and must not
21//    be misrepresented as being the original software.
22//
23// 3. This notice may not be removed or altered from any source
24//    distribution.
25//
26//========================================================================
27
28#include "internal.h"
29
30#include <assert.h>
31#include <string.h>
32#include <stdlib.h>
33
34#define _GLFW_FIND_LOADER    1
35#define _GLFW_REQUIRE_LOADER 2
36
37
38//////////////////////////////////////////////////////////////////////////
39//////                       GLFW internal API                      //////
40//////////////////////////////////////////////////////////////////////////
41
42GLFWbool _glfwInitVulkan(int mode)
43{
44    VkResult err;
45    VkExtensionProperties* ep;
46    PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
47    uint32_t i, count;
48
49    if (_glfw.vk.available)
50        return GLFW_TRUE;
51
52    if (_glfw.hints.init.vulkanLoader)
53        _glfw.vk.GetInstanceProcAddr = _glfw.hints.init.vulkanLoader;
54    else
55    {
56#if defined(_GLFW_VULKAN_LIBRARY)
57        _glfw.vk.handle = _glfwPlatformLoadModule(_GLFW_VULKAN_LIBRARY);
58#elif defined(_GLFW_WIN32)
59        _glfw.vk.handle = _glfwPlatformLoadModule("vulkan-1.dll");
60#elif defined(_GLFW_COCOA)
61        _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.1.dylib");
62        if (!_glfw.vk.handle)
63            _glfw.vk.handle = _glfwLoadLocalVulkanLoaderCocoa();
64#elif defined(__OpenBSD__) || defined(__NetBSD__)
65        _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so");
66#else
67        _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so.1");
68#endif
69        if (!_glfw.vk.handle)
70        {
71            if (mode == _GLFW_REQUIRE_LOADER)
72                _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found");
73
74            return GLFW_FALSE;
75        }
76
77        _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)
78            _glfwPlatformGetModuleSymbol(_glfw.vk.handle, "vkGetInstanceProcAddr");
79        if (!_glfw.vk.GetInstanceProcAddr)
80        {
81            _glfwInputError(GLFW_API_UNAVAILABLE,
82                            "Vulkan: Loader does not export vkGetInstanceProcAddr");
83
84            _glfwTerminateVulkan();
85            return GLFW_FALSE;
86        }
87    }
88
89    vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)
90        vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
91    if (!vkEnumerateInstanceExtensionProperties)
92    {
93        _glfwInputError(GLFW_API_UNAVAILABLE,
94                        "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties");
95
96        _glfwTerminateVulkan();
97        return GLFW_FALSE;
98    }
99
100    err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
101    if (err)
102    {
103        // NOTE: This happens on systems with a loader but without any Vulkan ICD
104        if (mode == _GLFW_REQUIRE_LOADER)
105        {
106            _glfwInputError(GLFW_API_UNAVAILABLE,
107                            "Vulkan: Failed to query instance extension count: %s",
108                            _glfwGetVulkanResultString(err));
109        }
110
111        _glfwTerminateVulkan();
112        return GLFW_FALSE;
113    }
114
115    ep = _glfw_calloc(count, sizeof(VkExtensionProperties));
116
117    err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep);
118    if (err)
119    {
120        _glfwInputError(GLFW_API_UNAVAILABLE,
121                        "Vulkan: Failed to query instance extensions: %s",
122                        _glfwGetVulkanResultString(err));
123
124        _glfw_free(ep);
125        _glfwTerminateVulkan();
126        return GLFW_FALSE;
127    }
128
129    for (i = 0;  i < count;  i++)
130    {
131        if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0)
132            _glfw.vk.KHR_surface = GLFW_TRUE;
133        else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0)
134            _glfw.vk.KHR_win32_surface = GLFW_TRUE;
135        else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0)
136            _glfw.vk.MVK_macos_surface = GLFW_TRUE;
137        else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0)
138            _glfw.vk.EXT_metal_surface = GLFW_TRUE;
139        else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0)
140            _glfw.vk.KHR_xlib_surface = GLFW_TRUE;
141        else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0)
142            _glfw.vk.KHR_xcb_surface = GLFW_TRUE;
143        else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0)
144            _glfw.vk.KHR_wayland_surface = GLFW_TRUE;
145        else if (strcmp(ep[i].extensionName, "VK_EXT_headless_surface") == 0)
146            _glfw.vk.EXT_headless_surface = GLFW_TRUE;
147    }
148
149    _glfw_free(ep);
150
151    _glfw.vk.available = GLFW_TRUE;
152
153    _glfw.platform.getRequiredInstanceExtensions(_glfw.vk.extensions);
154
155    return GLFW_TRUE;
156}
157
158void _glfwTerminateVulkan(void)
159{
160    if (_glfw.vk.handle)
161        _glfwPlatformFreeModule(_glfw.vk.handle);
162}
163
164const char* _glfwGetVulkanResultString(VkResult result)
165{
166    switch (result)
167    {
168        case VK_SUCCESS:
169            return "Success";
170        case VK_NOT_READY:
171            return "A fence or query has not yet completed";
172        case VK_TIMEOUT:
173            return "A wait operation has not completed in the specified time";
174        case VK_EVENT_SET:
175            return "An event is signaled";
176        case VK_EVENT_RESET:
177            return "An event is unsignaled";
178        case VK_INCOMPLETE:
179            return "A return array was too small for the result";
180        case VK_ERROR_OUT_OF_HOST_MEMORY:
181            return "A host memory allocation has failed";
182        case VK_ERROR_OUT_OF_DEVICE_MEMORY:
183            return "A device memory allocation has failed";
184        case VK_ERROR_INITIALIZATION_FAILED:
185            return "Initialization of an object could not be completed for implementation-specific reasons";
186        case VK_ERROR_DEVICE_LOST:
187            return "The logical or physical device has been lost";
188        case VK_ERROR_MEMORY_MAP_FAILED:
189            return "Mapping of a memory object has failed";
190        case VK_ERROR_LAYER_NOT_PRESENT:
191            return "A requested layer is not present or could not be loaded";
192        case VK_ERROR_EXTENSION_NOT_PRESENT:
193            return "A requested extension is not supported";
194        case VK_ERROR_FEATURE_NOT_PRESENT:
195            return "A requested feature is not supported";
196        case VK_ERROR_INCOMPATIBLE_DRIVER:
197            return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible";
198        case VK_ERROR_TOO_MANY_OBJECTS:
199            return "Too many objects of the type have already been created";
200        case VK_ERROR_FORMAT_NOT_SUPPORTED:
201            return "A requested format is not supported on this device";
202        case VK_ERROR_SURFACE_LOST_KHR:
203            return "A surface is no longer available";
204        case VK_SUBOPTIMAL_KHR:
205            return "A swapchain no longer matches the surface properties exactly, but can still be used";
206        case VK_ERROR_OUT_OF_DATE_KHR:
207            return "A surface has changed in such a way that it is no longer compatible with the swapchain";
208        case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
209            return "The display used by a swapchain does not use the same presentable image layout";
210        case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
211            return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API";
212        case VK_ERROR_VALIDATION_FAILED_EXT:
213            return "A validation layer found an error";
214        default:
215            return "ERROR: UNKNOWN VULKAN ERROR";
216    }
217}
218
219
220//////////////////////////////////////////////////////////////////////////
221//////                        GLFW public API                       //////
222//////////////////////////////////////////////////////////////////////////
223
224GLFWAPI int glfwVulkanSupported(void)
225{
226    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
227    return _glfwInitVulkan(_GLFW_FIND_LOADER);
228}
229
230GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count)
231{
232    assert(count != NULL);
233
234    *count = 0;
235
236    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
237
238    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))
239        return NULL;
240
241    if (!_glfw.vk.extensions[0])
242        return NULL;
243
244    *count = 2;
245    return (const char**) _glfw.vk.extensions;
246}
247
248GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance,
249                                              const char* procname)
250{
251    GLFWvkproc proc;
252    assert(procname != NULL);
253
254    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
255
256    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))
257        return NULL;
258
259    // NOTE: Vulkan 1.0 and 1.1 vkGetInstanceProcAddr cannot return itself
260    if (strcmp(procname, "vkGetInstanceProcAddr") == 0)
261        return (GLFWvkproc) vkGetInstanceProcAddr;
262
263    proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname);
264    if (!proc)
265    {
266        if (_glfw.vk.handle)
267            proc = (GLFWvkproc) _glfwPlatformGetModuleSymbol(_glfw.vk.handle, procname);
268    }
269
270    return proc;
271}
272
273GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance,
274                                                     VkPhysicalDevice device,
275                                                     uint32_t queuefamily)
276{
277    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
278
279    assert(instance != VK_NULL_HANDLE);
280    assert(device != VK_NULL_HANDLE);
281
282    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))
283        return GLFW_FALSE;
284
285    if (!_glfw.vk.extensions[0])
286    {
287        _glfwInputError(GLFW_API_UNAVAILABLE,
288                        "Vulkan: Window surface creation extensions not found");
289        return GLFW_FALSE;
290    }
291
292    return _glfw.platform.getPhysicalDevicePresentationSupport(instance,
293                                                               device,
294                                                               queuefamily);
295}
296
297GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance,
298                                         GLFWwindow* handle,
299                                         const VkAllocationCallbacks* allocator,
300                                         VkSurfaceKHR* surface)
301{
302    assert(surface != NULL);
303
304    *surface = VK_NULL_HANDLE;
305
306    _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED);
307
308    _GLFWwindow* window = (_GLFWwindow*) handle;
309    assert(window != NULL);
310    assert(instance != VK_NULL_HANDLE);
311
312    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))
313        return VK_ERROR_INITIALIZATION_FAILED;
314
315    if (!_glfw.vk.extensions[0])
316    {
317        _glfwInputError(GLFW_API_UNAVAILABLE,
318                        "Vulkan: Window surface creation extensions not found");
319        return VK_ERROR_EXTENSION_NOT_PRESENT;
320    }
321
322    if (window->context.client != GLFW_NO_API)
323    {
324        _glfwInputError(GLFW_INVALID_VALUE,
325                        "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API");
326        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
327    }
328
329    return _glfw.platform.createWindowSurface(instance, window, allocator, surface);
330}
331
332