1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 | #include "../../SDL_internal.h" |
22 | |
23 | #if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_X11 |
24 | |
25 | #include "SDL_x11video.h" |
26 | |
27 | #include "SDL_loadso.h" |
28 | #include "SDL_x11vulkan.h" |
29 | |
30 | #include <X11/Xlib.h> |
31 | /*#include <xcb/xcb.h>*/ |
32 | |
33 | #if defined(__OpenBSD__) |
34 | #define DEFAULT_VULKAN "libvulkan.so" |
35 | #else |
36 | #define DEFAULT_VULKAN "libvulkan.so.1" |
37 | #endif |
38 | |
39 | /* |
40 | typedef uint32_t xcb_window_t; |
41 | typedef uint32_t xcb_visualid_t; |
42 | */ |
43 | |
44 | int X11_Vulkan_LoadLibrary(_THIS, const char *path) |
45 | { |
46 | SDL_VideoData *videoData = (SDL_VideoData *)_this->driverdata; |
47 | VkExtensionProperties *extensions = NULL; |
48 | Uint32 extensionCount = 0; |
49 | SDL_bool hasSurfaceExtension = SDL_FALSE; |
50 | SDL_bool hasXlibSurfaceExtension = SDL_FALSE; |
51 | SDL_bool hasXCBSurfaceExtension = SDL_FALSE; |
52 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; |
53 | Uint32 i; |
54 | if(_this->vulkan_config.loader_handle) |
55 | return SDL_SetError("Vulkan already loaded" ); |
56 | |
57 | /* Load the Vulkan loader library */ |
58 | if(!path) |
59 | path = SDL_getenv("SDL_VULKAN_LIBRARY" ); |
60 | if(!path) |
61 | path = DEFAULT_VULKAN; |
62 | _this->vulkan_config.loader_handle = SDL_LoadObject(path); |
63 | if(!_this->vulkan_config.loader_handle) |
64 | return -1; |
65 | SDL_strlcpy(_this->vulkan_config.loader_path, path, SDL_arraysize(_this->vulkan_config.loader_path)); |
66 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( |
67 | _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr" ); |
68 | if(!vkGetInstanceProcAddr) |
69 | goto fail; |
70 | _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; |
71 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties = |
72 | (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( |
73 | VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties" ); |
74 | if(!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) |
75 | goto fail; |
76 | extensions = SDL_Vulkan_CreateInstanceExtensionsList( |
77 | (PFN_vkEnumerateInstanceExtensionProperties) |
78 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties, |
79 | &extensionCount); |
80 | if(!extensions) |
81 | goto fail; |
82 | for(i = 0; i < extensionCount; i++) |
83 | { |
84 | if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) |
85 | hasSurfaceExtension = SDL_TRUE; |
86 | else if(SDL_strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) |
87 | hasXCBSurfaceExtension = SDL_TRUE; |
88 | else if(SDL_strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) |
89 | hasXlibSurfaceExtension = SDL_TRUE; |
90 | } |
91 | SDL_free(extensions); |
92 | if(!hasSurfaceExtension) |
93 | { |
94 | SDL_SetError("Installed Vulkan doesn't implement the " |
95 | VK_KHR_SURFACE_EXTENSION_NAME " extension" ); |
96 | goto fail; |
97 | } |
98 | if(hasXlibSurfaceExtension) |
99 | { |
100 | videoData->vulkan_xlib_xcb_library = NULL; |
101 | } |
102 | else if(!hasXCBSurfaceExtension) |
103 | { |
104 | SDL_SetError("Installed Vulkan doesn't implement either the " |
105 | VK_KHR_XCB_SURFACE_EXTENSION_NAME "extension or the " |
106 | VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension" ); |
107 | goto fail; |
108 | } |
109 | else |
110 | { |
111 | const char *libX11XCBLibraryName = SDL_getenv("SDL_X11_XCB_LIBRARY" ); |
112 | if(!libX11XCBLibraryName) |
113 | libX11XCBLibraryName = "libX11-xcb.so" ; |
114 | videoData->vulkan_xlib_xcb_library = SDL_LoadObject(libX11XCBLibraryName); |
115 | if(!videoData->vulkan_xlib_xcb_library) |
116 | goto fail; |
117 | videoData->vulkan_XGetXCBConnection = |
118 | SDL_LoadFunction(videoData->vulkan_xlib_xcb_library, "XGetXCBConnection" ); |
119 | if(!videoData->vulkan_XGetXCBConnection) |
120 | { |
121 | SDL_UnloadObject(videoData->vulkan_xlib_xcb_library); |
122 | goto fail; |
123 | } |
124 | } |
125 | return 0; |
126 | |
127 | fail: |
128 | SDL_UnloadObject(_this->vulkan_config.loader_handle); |
129 | _this->vulkan_config.loader_handle = NULL; |
130 | return -1; |
131 | } |
132 | |
133 | void X11_Vulkan_UnloadLibrary(_THIS) |
134 | { |
135 | SDL_VideoData *videoData = (SDL_VideoData *)_this->driverdata; |
136 | if(_this->vulkan_config.loader_handle) |
137 | { |
138 | if(videoData->vulkan_xlib_xcb_library) |
139 | SDL_UnloadObject(videoData->vulkan_xlib_xcb_library); |
140 | SDL_UnloadObject(_this->vulkan_config.loader_handle); |
141 | _this->vulkan_config.loader_handle = NULL; |
142 | } |
143 | } |
144 | |
145 | SDL_bool X11_Vulkan_GetInstanceExtensions(_THIS, |
146 | SDL_Window *window, |
147 | unsigned *count, |
148 | const char **names) |
149 | { |
150 | SDL_VideoData *videoData = (SDL_VideoData *)_this->driverdata; |
151 | if(!_this->vulkan_config.loader_handle) |
152 | { |
153 | SDL_SetError("Vulkan is not loaded" ); |
154 | return SDL_FALSE; |
155 | } |
156 | if(videoData->vulkan_xlib_xcb_library) |
157 | { |
158 | static const char *const extensionsForXCB[] = { |
159 | VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_EXTENSION_NAME, |
160 | }; |
161 | return SDL_Vulkan_GetInstanceExtensions_Helper( |
162 | count, names, SDL_arraysize(extensionsForXCB), extensionsForXCB); |
163 | } |
164 | else |
165 | { |
166 | static const char *const extensionsForXlib[] = { |
167 | VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_EXTENSION_NAME, |
168 | }; |
169 | return SDL_Vulkan_GetInstanceExtensions_Helper( |
170 | count, names, SDL_arraysize(extensionsForXlib), extensionsForXlib); |
171 | } |
172 | } |
173 | |
174 | SDL_bool X11_Vulkan_CreateSurface(_THIS, |
175 | SDL_Window *window, |
176 | VkInstance instance, |
177 | VkSurfaceKHR *surface) |
178 | { |
179 | SDL_VideoData *videoData = (SDL_VideoData *)_this->driverdata; |
180 | SDL_WindowData *windowData = (SDL_WindowData *)window->driverdata; |
181 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; |
182 | if(!_this->vulkan_config.loader_handle) |
183 | { |
184 | SDL_SetError("Vulkan is not loaded" ); |
185 | return SDL_FALSE; |
186 | } |
187 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; |
188 | if(videoData->vulkan_xlib_xcb_library) |
189 | { |
190 | PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = |
191 | (PFN_vkCreateXcbSurfaceKHR)vkGetInstanceProcAddr(instance, |
192 | "vkCreateXcbSurfaceKHR" ); |
193 | VkXcbSurfaceCreateInfoKHR createInfo; |
194 | VkResult result; |
195 | if(!vkCreateXcbSurfaceKHR) |
196 | { |
197 | SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME |
198 | " extension is not enabled in the Vulkan instance." ); |
199 | return SDL_FALSE; |
200 | } |
201 | SDL_zero(createInfo); |
202 | createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; |
203 | createInfo.connection = videoData->vulkan_XGetXCBConnection(videoData->display); |
204 | if(!createInfo.connection) |
205 | { |
206 | SDL_SetError("XGetXCBConnection failed" ); |
207 | return SDL_FALSE; |
208 | } |
209 | createInfo.window = (xcb_window_t)windowData->xwindow; |
210 | result = vkCreateXcbSurfaceKHR(instance, &createInfo, |
211 | NULL, surface); |
212 | if(result != VK_SUCCESS) |
213 | { |
214 | SDL_SetError("vkCreateXcbSurfaceKHR failed: %s" , SDL_Vulkan_GetResultString(result)); |
215 | return SDL_FALSE; |
216 | } |
217 | return SDL_TRUE; |
218 | } |
219 | else |
220 | { |
221 | PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = |
222 | (PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance, |
223 | "vkCreateXlibSurfaceKHR" ); |
224 | VkXlibSurfaceCreateInfoKHR createInfo; |
225 | VkResult result; |
226 | if(!vkCreateXlibSurfaceKHR) |
227 | { |
228 | SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME |
229 | " extension is not enabled in the Vulkan instance." ); |
230 | return SDL_FALSE; |
231 | } |
232 | SDL_zero(createInfo); |
233 | createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; |
234 | createInfo.dpy = videoData->display; |
235 | createInfo.window = (xcb_window_t)windowData->xwindow; |
236 | result = vkCreateXlibSurfaceKHR(instance, &createInfo, |
237 | NULL, surface); |
238 | if(result != VK_SUCCESS) |
239 | { |
240 | SDL_SetError("vkCreateXlibSurfaceKHR failed: %s" , SDL_Vulkan_GetResultString(result)); |
241 | return SDL_FALSE; |
242 | } |
243 | return SDL_TRUE; |
244 | } |
245 | } |
246 | |
247 | #endif |
248 | |
249 | /* vim: set ts=4 sw=4 expandtab: */ |
250 | |