1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23 * @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
24 * SDL_x11vulkan.c.
25 */
26
27#include "SDL_internal.h"
28
29#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND)
30
31#include "../SDL_vulkan_internal.h"
32
33#include "SDL_waylandvideo.h"
34#include "SDL_waylandwindow.h"
35
36#include "SDL_waylandvulkan.h"
37
38#ifdef SDL_PLATFORM_OPENBSD
39#define DEFAULT_VULKAN "libvulkan.so"
40#else
41#define DEFAULT_VULKAN "libvulkan.so.1"
42#endif
43
44bool Wayland_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
45{
46 VkExtensionProperties *extensions = NULL;
47 Uint32 i, extensionCount = 0;
48 bool hasSurfaceExtension = false;
49 bool hasWaylandSurfaceExtension = false;
50 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
51 if (_this->vulkan_config.loader_handle) {
52 return SDL_SetError("Vulkan already loaded");
53 }
54
55 // Load the Vulkan loader library
56 if (!path) {
57 path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
58 }
59 if (!path) {
60 path = DEFAULT_VULKAN;
61 }
62 _this->vulkan_config.loader_handle = SDL_LoadObject(path);
63 if (!_this->vulkan_config.loader_handle) {
64 return false;
65 }
66 SDL_strlcpy(_this->vulkan_config.loader_path, path,
67 SDL_arraysize(_this->vulkan_config.loader_path));
68 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
69 _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
70 if (!vkGetInstanceProcAddr) {
71 goto fail;
72 }
73 _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
74 _this->vulkan_config.vkEnumerateInstanceExtensionProperties =
75 (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
76 VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
77 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
78 goto fail;
79 }
80 extensions = SDL_Vulkan_CreateInstanceExtensionsList(
81 (PFN_vkEnumerateInstanceExtensionProperties)
82 _this->vulkan_config.vkEnumerateInstanceExtensionProperties,
83 &extensionCount);
84 if (!extensions) {
85 goto fail;
86 }
87 for (i = 0; i < extensionCount; i++) {
88 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
89 hasSurfaceExtension = true;
90 } else if (SDL_strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
91 hasWaylandSurfaceExtension = true;
92 }
93 }
94 SDL_free(extensions);
95 if (!hasSurfaceExtension) {
96 SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
97 goto fail;
98 } else if (!hasWaylandSurfaceExtension) {
99 SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "extension");
100 goto fail;
101 }
102 return true;
103
104fail:
105 SDL_UnloadObject(_this->vulkan_config.loader_handle);
106 _this->vulkan_config.loader_handle = NULL;
107 return false;
108}
109
110void Wayland_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
111{
112 if (_this->vulkan_config.loader_handle) {
113 SDL_UnloadObject(_this->vulkan_config.loader_handle);
114 _this->vulkan_config.loader_handle = NULL;
115 }
116}
117
118char const* const* Wayland_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
119{
120 static const char *const extensionsForWayland[] = {
121 VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
122 };
123
124 if (count) {
125 *count = SDL_arraysize(extensionsForWayland);
126 }
127
128 return extensionsForWayland;
129}
130
131bool Wayland_Vulkan_CreateSurface(SDL_VideoDevice *_this,
132 SDL_Window *window,
133 VkInstance instance,
134 const struct VkAllocationCallbacks *allocator,
135 VkSurfaceKHR *surface)
136{
137 SDL_WindowData *windowData = window->internal;
138 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
139 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
140 PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR =
141 (PFN_vkCreateWaylandSurfaceKHR)vkGetInstanceProcAddr(
142 instance,
143 "vkCreateWaylandSurfaceKHR");
144 VkWaylandSurfaceCreateInfoKHR createInfo;
145 VkResult result;
146
147 if (!_this->vulkan_config.loader_handle) {
148 return SDL_SetError("Vulkan is not loaded");
149 }
150
151 if (!vkCreateWaylandSurfaceKHR) {
152 return SDL_SetError(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
153 " extension is not enabled in the Vulkan instance.");
154 }
155 SDL_zero(createInfo);
156 createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
157 createInfo.pNext = NULL;
158 createInfo.flags = 0;
159 createInfo.display = windowData->waylandData->display;
160 createInfo.surface = windowData->surface;
161 result = vkCreateWaylandSurfaceKHR(instance, &createInfo, allocator, surface);
162 if (result != VK_SUCCESS) {
163 return SDL_SetError("vkCreateWaylandSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
164 }
165 return true;
166}
167
168void Wayland_Vulkan_DestroySurface(SDL_VideoDevice *_this,
169 VkInstance instance,
170 VkSurfaceKHR surface,
171 const struct VkAllocationCallbacks *allocator)
172{
173 if (_this->vulkan_config.loader_handle) {
174 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
175 }
176}
177
178bool Wayland_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
179 VkInstance instance,
180 VkPhysicalDevice physicalDevice,
181 Uint32 queueFamilyIndex)
182{
183 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
184 (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
185 PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR =
186 (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)vkGetInstanceProcAddr(
187 instance,
188 "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
189
190 if (!_this->vulkan_config.loader_handle) {
191 return SDL_SetError("Vulkan is not loaded");
192 }
193
194 if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) {
195 return SDL_SetError(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
196 }
197
198 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice,
199 queueFamilyIndex,
200 _this->internal->display);
201}
202
203#endif
204