kitty/glfw/vulkan.c
2026-08-05 19:10:55 +05:30

259 lines
10 KiB
C
Vendored

//========================================================================
// GLFW 3.4 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
// Copyright (c) 2006-2018 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.
//
//========================================================================
// Please use C89 style variable declarations in this file because VS 2010
//========================================================================
#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 //////
//////////////////////////////////////////////////////////////////////////
bool
_glfwInitVulkan(int mode) {
VkResult err;
VkExtensionProperties *ep;
uint32_t i, count;
if (_glfw.vk.available) return 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");
if (!_glfw.vk.handle) _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS();
#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 false;
}
*(void **)&_glfw.vk.GetInstanceProcAddr = _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr");
if (!_glfw.vk.GetInstanceProcAddr) {
_glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader does not export vkGetInstanceProcAddr");
_glfwTerminateVulkan();
return false;
}
_glfw.vk.EnumerateInstanceExtensionProperties =
(PFN_vkEnumerateInstanceExtensionProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
if (!_glfw.vk.EnumerateInstanceExtensionProperties) {
_glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties");
_glfwTerminateVulkan();
return 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 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 false;
}
for (i = 0; i < count; i++) {
if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) _glfw.vk.KHR_surface = true;
#if defined(_GLFW_WIN32)
else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = true;
#elif defined(_GLFW_COCOA)
else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) _glfw.vk.MVK_macos_surface = true;
else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) _glfw.vk.EXT_metal_surface = true;
#elif defined(_GLFW_X11)
else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = true;
else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) _glfw.vk.KHR_xcb_surface = true;
#elif defined(_GLFW_WAYLAND)
else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = true;
#endif
}
free(ep);
_glfw.vk.available = true;
_glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions);
return 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(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) *(void **)&proc = _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(false);
if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return false;
if (!_glfw.vk.extensions[0]) {
_glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found");
return 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);
}