SDL  2.0
SDL_kmsdrmvulkan.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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 Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
24  * Based on Jacob Lifshay's SDL_x11vulkan.c.
25  */
26 
27 #include "../../SDL_internal.h"
28 
29 #if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
30 
31 #include "SDL_kmsdrmvideo.h"
32 #include "SDL_kmsdrmdyn.h"
33 #include "SDL_assert.h"
34 
35 #include "SDL_loadso.h"
36 #include "SDL_kmsdrmvulkan.h"
37 #include "SDL_syswm.h"
38 #include "sys/ioctl.h"
39 
40 #if defined(__OpenBSD__)
41 #define DEFAULT_VULKAN "libvulkan.so"
42 #else
43 #define DEFAULT_VULKAN "libvulkan.so.1"
44 #endif
45 
46 int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path)
47 {
48  VkExtensionProperties *extensions = NULL;
49  Uint32 i, extensionCount = 0;
50  SDL_bool hasSurfaceExtension = SDL_FALSE;
51  SDL_bool hasDisplayExtension = SDL_FALSE;
53 
55  return SDL_SetError("Vulkan already loaded");
56 
57  /* Load the Vulkan library */
58  if(!path)
59  path = SDL_getenv("SDL_VULKAN_LIBRARY");
60  if(!path)
61  path = DEFAULT_VULKAN;
62 
64 
66  return -1;
67 
70 
72  _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
73 
75  goto fail;
76 
80  VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
81 
83  goto fail;
84 
85  extensions = SDL_Vulkan_CreateInstanceExtensionsList(
88  &extensionCount);
89 
90  if(!extensions)
91  goto fail;
92 
93  for(i = 0; i < extensionCount; i++)
94  {
95  if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0)
96  hasSurfaceExtension = SDL_TRUE;
97  else if(SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0)
98  hasDisplayExtension = SDL_TRUE;
99  }
100 
101  SDL_free(extensions);
102 
103  if(!hasSurfaceExtension)
104  {
105  SDL_SetError("Installed Vulkan doesn't implement the "
106  VK_KHR_SURFACE_EXTENSION_NAME " extension");
107  goto fail;
108  }
109  else if(!hasDisplayExtension)
110  {
111  SDL_SetError("Installed Vulkan doesn't implement the "
112  VK_KHR_DISPLAY_EXTENSION_NAME "extension");
113  goto fail;
114  }
115 
116  return 0;
117 
118 fail:
121  return -1;
122 }
123 
124 void KMSDRM_Vulkan_UnloadLibrary(_THIS)
125 {
127  {
130  }
131 }
132 
133 /*********************************************************************/
134 /* Here we can put whatever Vulkan extensions we want to be enabled */
135 /* at instance creation, which is done in the programs, not in SDL. */
136 /* So: programs call SDL_Vulkan_GetInstanceExtensions() and here */
137 /* we put the extensions specific to this backend so the programs */
138 /* get a list with the extension we want, so they can include that */
139 /* list in the ppEnabledExtensionNames and EnabledExtensionCount */
140 /* members of the VkInstanceCreateInfo struct passed to */
141 /* vkCreateInstance(). */
142 /*********************************************************************/
143 SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
145  unsigned *count,
146  const char **names)
147 {
148  static const char *const extensionsForKMSDRM[] = {
150  };
152  {
153  SDL_SetError("Vulkan is not loaded");
154  return SDL_FALSE;
155  }
156  return SDL_Vulkan_GetInstanceExtensions_Helper(
157  count, names, SDL_arraysize(extensionsForKMSDRM),
158  extensionsForKMSDRM);
159 }
160 
161 void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
162 {
163  if (w) {
164  *w = window->w;
165  }
166 
167  if (h) {
168  *h = window->h;
169  }
170 }
171 
172 /***********************************************************************/
173 /* First thing to know is that we don't call vkCreateInstance() here. */
174 /* Instead, programs using SDL and Vulkan create their Vulkan instance */
175 /* and we get it here, ready to use. */
176 /* Extensions specific for this platform are activated in */
177 /* KMSDRM_Vulkan_GetInstanceExtensions(), like we do with */
178 /* VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. */
179 /***********************************************************************/
180 SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
182  VkInstance instance,
183  VkSurfaceKHR *surface)
184 {
185  VkPhysicalDevice gpu;
186  uint32_t gpu_count;
187  uint32_t display_count;
188  uint32_t mode_count;
189  uint32_t plane_count;
190 
191  VkPhysicalDevice *physical_devices = NULL;
192  VkDisplayPropertiesKHR *displays_props = NULL;
193  VkDisplayModePropertiesKHR *modes_props = NULL;
194  VkDisplayPlanePropertiesKHR *planes_props = NULL;
195 
196  VkDisplayModeCreateInfoKHR display_mode_create_info;
197  VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
198 
199  VkExtent2D image_size;
200  VkDisplayModeKHR display_mode;
201  VkDisplayModePropertiesKHR display_mode_props = {0};
202 
204  SDL_bool ret = SDL_FALSE;
205 
206  /* We don't receive a display index in KMSDRM_CreateDevice(), only
207  a device index, which determines the GPU to use, but not the output.
208  So we simply use the first connected output (ie, the first connected
209  video output) for now.
210  In other words, change this index to select a different output. Easy! */
211  int display_index = 0;
212 
213  int i;
214 
215  SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
216 
217  /* Get the function pointers for the functions we will use. */
220 
223  instance, "vkCreateDisplayPlaneSurfaceKHR");
224 
227  instance, "vkEnumeratePhysicalDevices");
228 
231  instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
232 
235  instance, "vkGetDisplayModePropertiesKHR");
236 
239  instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
240 
241  /*PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
242  (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
243  instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
244 
245  PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
246  (PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
247  instance, "vkGetDisplayPlaneCapabilitiesKHR");
248  */
249 
252  instance, "vkCreateDisplayModeKHR");
253 
255  {
256  SDL_SetError("Vulkan is not loaded");
257  goto clean;
258  }
259 
260  /*************************************/
261  /* Block for vulkan surface creation */
262  /*************************************/
263 
264  /****************************************************************/
265  /* If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means */
266  /* that the VK_KHR_Display extension is active on the instance. */
267  /* That's the central extension we need for x-less VK! */
268  /****************************************************************/
270  {
272  " extension is not enabled in the Vulkan instance.");
273  goto clean;
274  }
275 
276  /* Get the physical device count. */
277  vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
278 
279  if (gpu_count == 0) {
280  SDL_SetError("Vulkan can't find physical devices (gpus).");
281  goto clean;
282  }
283 
284  /* Get the physical devices. */
285  physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
286  vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
287 
288  /* A GPU (or physical_device, in vkcube terms) is a GPU. A machine with more
289  than one video output doen't need to have more than one GPU, like the Pi4
290  which has 1 GPU and 2 video outputs.
291  We grab the GPU/physical_device with the index we got in KMSDR_CreateDevice(). */
292  gpu = physical_devices[viddata->devindex];
293 
294  /* A display is a video output. 1 GPU can have N displays.
295  Vulkan only counts the connected displays.
296  Get the display count of the GPU. */
297  vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
298  if (display_count == 0) {
299  SDL_SetError("Vulkan can't find any displays.");
300  goto clean;
301  }
302 
303  /* Get the props of the displays of the physical device. */
304  displays_props = (VkDisplayPropertiesKHR *) malloc(display_count * sizeof(*displays_props));
306  &display_count,
307  displays_props);
308 
309  /* Get the videomode count for the first display. */
311  displays_props[display_index].display,
312  &mode_count, NULL);
313 
314  if (mode_count == 0) {
315  SDL_SetError("Vulkan can't find any video modes for display %i (%s)\n", 0,
316  displays_props[display_index].displayName);
317  goto clean;
318  }
319 
320  /* Get the props of the videomodes for the first display. */
321  modes_props = (VkDisplayModePropertiesKHR *) malloc(mode_count * sizeof(*modes_props));
323  displays_props[display_index].display,
324  &mode_count, modes_props);
325 
326  /* Get the planes count of the physical device. */
328  if (plane_count == 0) {
329  SDL_SetError("Vulkan can't find any planes.");
330  goto clean;
331  }
332 
333  /* Get the props of the planes for the physical device. */
334  planes_props = malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
335  vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes_props);
336 
337  /* Get a video mode equal or smaller than the window size. REMEMBER:
338  We have to get a small enough videomode for the window size,
339  because videomode determines how big the scanout region is and we can't
340  scanout a region bigger than the window (we would be reading past the
341  buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */
342  for (i = 0; i < mode_count; i++) {
343  if (modes_props[i].parameters.visibleRegion.width <= window->w &&
344  modes_props[i].parameters.visibleRegion.height <= window->h)
345  {
346  display_mode_props = modes_props[i];
347  break;
348  }
349  }
350 
351  if (display_mode_props.parameters.visibleRegion.width == 0
352  || display_mode_props.parameters.visibleRegion.height == 0)
353  {
354  SDL_SetError("Vulkan can't find a proper display mode for the window size.");
355  goto clean;
356  }
357 
358  /* We have the props of the display mode, but we need an actual display mode. */
359  display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
360  display_mode_create_info.parameters = display_mode_props.parameters;
362  displays_props[display_index].display,
363  &display_mode_create_info,
364  NULL, &display_mode);
365  if (result != VK_SUCCESS) {
366  SDL_SetError("Vulkan can't create the display mode.");
367  goto clean;
368  }
369 
370  /* Let's finally create the Vulkan surface! */
371 
372  image_size.width = window->w;
373  image_size.height = window->h;
374 
375  display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
376  display_plane_surface_create_info.displayMode = display_mode;
377  /* For now, simply use the first plane. */
378  display_plane_surface_create_info.planeIndex = 0;
379  display_plane_surface_create_info.imageExtent = image_size;
381  &display_plane_surface_create_info,
382  NULL,
383  surface);
384  if(result != VK_SUCCESS)
385  {
386  SDL_SetError("vkCreateKMSDRMSurfaceKHR failed: %s",
387  SDL_Vulkan_GetResultString(result));
388  goto clean;
389  }
390 
391  ret = SDL_TRUE;
392 
393 clean:
394  if (physical_devices)
395  free (physical_devices);
396  if (displays_props)
397  free (displays_props);
398  if (planes_props)
399  free (planes_props);
400  if (modes_props)
401  free (modes_props);
402 
403  return ret;
404 }
405 
406 #endif
407 
408 /* vim: set ts=4 sw=4 expandtab: */
#define _THIS
unsigned int uint32_t
#define SDL_SetError
#define SDL_LoadObject
#define SDL_UnloadObject
#define SDL_getenv
#define SDL_strlcpy
#define SDL_free
#define SDL_strcmp
SDL_EventEntry * free
Definition: SDL_events.c:89
void * SDL_LoadFunction(void *handle, const char *name)
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
GLuint64EXT * result
GLuint GLuint * names
GLsizei const GLchar *const * path
GLfloat GLfloat GLfloat GLfloat h
GLubyte GLubyte GLubyte GLubyte w
#define malloc
Definition: SDL_qsort.c:46
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:121
uint32_t Uint32
Definition: SDL_stdinc.h:209
static SDL_VideoDevice * _this
Definition: SDL_video.c:126
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:163
EGLSurface surface
Definition: eglext.h:248
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
struct SDL_VideoDevice::@441 vulkan_config
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr
Definition: SDL_sysvideo.h:387
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties
Definition: SDL_sysvideo.h:388
char loader_path[256]
Definition: SDL_sysvideo.h:390
void * loader_handle
Definition: SDL_sysvideo.h:391
The type used to identify a window.
Definition: SDL_sysvideo.h:75
VkDisplayModeParametersKHR parameters
Definition: vulkan_core.h:5949
VkDisplayModeParametersKHR parameters
Definition: vulkan_core.h:5954
VkDisplayModeKHR displayMode
Definition: vulkan_core.h:5988
uint32_t width
Definition: vulkan_core.h:1994
uint32_t height
Definition: vulkan_core.h:1995
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *pName)
VkResult(VKAPI_PTR * PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
Definition: vulkan_core.h:6003
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties)
#define VK_KHR_SURFACE_EXTENSION_NAME
Definition: vulkan_core.h:5654
VkResult(VKAPI_PTR * PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices)
Definition: vulkan_core.h:3110
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayModeKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDisplayModeKHR *pMode)
VkResult(VKAPI_PTR * PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties)
Definition: vulkan_core.h:5998
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties)
VkResult(VKAPI_PTR * PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties)
Definition: vulkan_core.h:5997
VkResult(VKAPI_PTR * PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties)
Definition: vulkan_core.h:6000
VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices)
#define VK_KHR_DISPLAY_EXTENSION_NAME
Definition: vulkan_core.h:5928
VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties)
VkResult
Definition: vulkan_core.h:103
@ VK_SUCCESS
Definition: vulkan_core.h:104
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
VkResult(VKAPI_PTR * PFN_vkEnumerateInstanceExtensionProperties)(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties)
Definition: vulkan_core.h:3121
#define VK_NULL_HANDLE
Definition: vulkan_core.h:55
VkResult(VKAPI_PTR * PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDisplayModeKHR *pMode)
Definition: vulkan_core.h:6001
PFN_vkVoidFunction(VKAPI_PTR * PFN_vkGetInstanceProcAddr)(VkInstance instance, const char *pName)
Definition: vulkan_core.h:3117
@ VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR
Definition: vulkan_core.h:325
@ VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR
Definition: vulkan_core.h:326