#include "../../SDL_internal.h"
#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmdyn.h"
#include "SDL_assert.h"
#include "SDL_loadso.h"
#include "SDL_kmsdrmvulkan.h"
#include "SDL_syswm.h"
#include "sys/ioctl.h"
#if defined(__OpenBSD__)
#define DEFAULT_VULKAN "libvulkan.so"
#else
#define DEFAULT_VULKAN "libvulkan.so.1"
#endif
int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 i, extensionCount = 0;
SDL_bool hasSurfaceExtension = SDL_FALSE;
SDL_bool hasDisplayExtension = SDL_FALSE;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if(_this->vulkan_config.loader_handle)
return SDL_SetError("Vulkan already loaded");
if(!path)
path = SDL_getenv("SDL_VULKAN_LIBRARY");
if(!path)
path = DEFAULT_VULKAN;
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
if(!_this->vulkan_config.loader_handle)
return -1;
SDL_strlcpy(_this->vulkan_config.loader_path, path,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
if(!vkGetInstanceProcAddr)
goto fail;
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if(!_this->vulkan_config.vkEnumerateInstanceExtensionProperties)
goto fail;
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if(!extensions)
goto fail;
for(i = 0; i < extensionCount; i++)
{
if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0)
hasSurfaceExtension = SDL_TRUE;
else if(SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0)
hasDisplayExtension = SDL_TRUE;
}
SDL_free(extensions);
if(!hasSurfaceExtension)
{
SDL_SetError("Installed Vulkan doesn't implement the "
VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
}
else if(!hasDisplayExtension)
{
SDL_SetError("Installed Vulkan doesn't implement the "
VK_KHR_DISPLAY_EXTENSION_NAME "extension");
goto fail;
}
return 0;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return -1;
}
void KMSDRM_Vulkan_UnloadLibrary(_THIS)
{
if(_this->vulkan_config.loader_handle)
{
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
SDL_Window *window,
unsigned *count,
const char **names)
{
static const char *const extensionsForKMSDRM[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
};
if(!_this->vulkan_config.loader_handle)
{
SDL_SetError("Vulkan is not loaded");
return SDL_FALSE;
}
return SDL_Vulkan_GetInstanceExtensions_Helper(
count, names, SDL_arraysize(extensionsForKMSDRM),
extensionsForKMSDRM);
}
void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
{
if (w) {
*w = window->w;
}
if (h) {
*h = window->h;
}
}
SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
SDL_Window *window,
VkInstance instance,
VkSurfaceKHR *surface)
{
VkPhysicalDevice gpu = NULL;
uint32_t gpu_count;
uint32_t display_count;
uint32_t mode_count;
uint32_t plane_count;
uint32_t plane = UINT32_MAX;
VkPhysicalDevice *physical_devices = NULL;
VkPhysicalDeviceProperties *device_props = NULL;
VkDisplayPropertiesKHR *display_props = NULL;
VkDisplayModePropertiesKHR *mode_props = NULL;
VkDisplayPlanePropertiesKHR *plane_props = NULL;
VkDisplayPlaneCapabilitiesKHR plane_caps;
VkDisplayModeCreateInfoKHR display_mode_create_info;
VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
VkExtent2D image_size;
VkDisplayKHR display;
VkDisplayModeKHR display_mode = (VkDisplayModeKHR)0;
VkDisplayModePropertiesKHR display_mode_props = {0};
VkDisplayModeParametersKHR new_mode_parameters = { {0, 0}, 0};
VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
VkResult result;
SDL_bool ret = SDL_FALSE;
SDL_bool valid_gpu = SDL_FALSE;
SDL_bool mode_found = SDL_FALSE;
SDL_bool plane_supports_display = SDL_FALSE;
int display_index = SDL_atoi(SDL_GetDisplayForWindow(window)->name);
int i, j;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR =
(PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr(
instance, "vkCreateDisplayPlaneSurfaceKHR");
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
(PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
instance, "vkEnumeratePhysicalDevices");
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties =
(PFN_vkGetPhysicalDeviceProperties)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceProperties");
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
(PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR =
(PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayModePropertiesKHR");
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR =
(PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
(PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
(PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayPlaneCapabilitiesKHR");
PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR =
(PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr(
instance, "vkCreateDisplayModeKHR");
if(!_this->vulkan_config.loader_handle)
{
SDL_SetError("Vulkan is not loaded");
goto clean;
}
if(!vkCreateDisplayPlaneSurfaceKHR)
{
SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
" extension is not enabled in the Vulkan instance.");
goto clean;
}
vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
if (gpu_count == 0) {
SDL_SetError("Vulkan can't find physical devices (gpus).");
goto clean;
}
physical_devices = SDL_malloc(sizeof(VkPhysicalDevice) * gpu_count);
device_props = SDL_malloc(sizeof(VkPhysicalDeviceProperties));
vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
for (i = 0; i < gpu_count; i++) {
vkGetPhysicalDeviceProperties(
physical_devices[i],
device_props
);
if (device_props->apiVersion >= 1 &&
(device_props->deviceType == 1 || device_props->deviceType == 2))
{
gpu = physical_devices[i];
valid_gpu = SDL_TRUE;
break;
}
}
if (!valid_gpu) {
SDL_SetError("Vulkan can't find a valid physical device (gpu).");
goto clean;
}
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
if (display_count == 0) {
SDL_SetError("Vulkan can't find any displays.");
goto clean;
}
display_props = (VkDisplayPropertiesKHR *) SDL_malloc(display_count * sizeof(*display_props));
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
&display_count,
display_props);
display = display_props[display_index].display;
vkGetDisplayModePropertiesKHR(gpu,
display,
&mode_count, NULL);
if (mode_count == 0) {
SDL_SetError("Vulkan can't find any video modes for display %i (%s)\n", 0,
display_props[display_index].displayName);
goto clean;
}
mode_props = (VkDisplayModePropertiesKHR *) SDL_malloc(mode_count * sizeof(*mode_props));
vkGetDisplayModePropertiesKHR(gpu,
display,
&mode_count, mode_props);
for (i = 0; i < mode_count; i++) {
if (mode_props[i].parameters.visibleRegion.width == window->w &&
mode_props[i].parameters.visibleRegion.height == window->h)
{
display_mode_props = mode_props[i];
mode_found = SDL_TRUE;
break;
}
}
if (mode_found &&
display_mode_props.parameters.visibleRegion.width > 0 &&
display_mode_props.parameters.visibleRegion.height > 0 ) {
display_mode = display_mode_props.displayMode;
} else {
new_mode_parameters.visibleRegion.width = window->w;
new_mode_parameters.visibleRegion.height = window->h;
new_mode_parameters.refreshRate = window->fullscreen_mode.refresh_rate * 1000;
SDL_zero(display_mode_create_info);
display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
display_mode_create_info.parameters = new_mode_parameters;
result = vkCreateDisplayModeKHR(gpu,
display,
&display_mode_create_info,
NULL, &display_mode);
if (result != VK_SUCCESS) {
SDL_SetError("Vulkan couldn't find a predefined mode for that window size and couldn't create a suitable mode.");
goto clean;
}
}
if (!display_mode) {
SDL_SetError("Vulkan couldn't get a display mode.");
goto clean;
}
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL);
if (plane_count == 0) {
SDL_SetError("Vulkan can't find any planes.");
goto clean;
}
plane_props = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, plane_props);
for (i = 0; i < plane_count; i++) {
uint32_t supported_displays_count = 0;
VkDisplayKHR* supported_displays;
vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_displays_count, NULL);
if (supported_displays_count == 0) {
continue;
}
supported_displays = (VkDisplayKHR*)SDL_malloc(sizeof(VkDisplayKHR) * supported_displays_count);
vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i,
&supported_displays_count, supported_displays);
if (!((plane_props[i].currentDisplay == display) ||
(plane_props[i].currentDisplay == VK_NULL_HANDLE)))
continue;
plane_supports_display = SDL_FALSE;
for (j = 0; j < supported_displays_count; j++) {
if (supported_displays[j] == display) {
plane_supports_display = SDL_TRUE;
break;
}
}
if (supported_displays) {
SDL_free(supported_displays);
}
if (!plane_supports_display) {
continue;
}
vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps);
if (plane_caps.supportedAlpha == alpha_mode) {
plane = i;
break;
}
}
if (plane == UINT32_MAX) {
SDL_SetError("Vulkan couldn't find an appropriate plane.");
goto clean;
}
image_size.width = window->w;
image_size.height = window->h;
SDL_zero(display_plane_surface_create_info);
display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
display_plane_surface_create_info.displayMode = display_mode;
display_plane_surface_create_info.planeIndex = plane;
display_plane_surface_create_info.imageExtent = image_size;
display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
display_plane_surface_create_info.alphaMode = alpha_mode;
result = vkCreateDisplayPlaneSurfaceKHR(instance,
&display_plane_surface_create_info,
NULL,
surface);
if(result != VK_SUCCESS)
{
SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
SDL_Vulkan_GetResultString(result));
goto clean;
}
ret = SDL_TRUE;
clean:
if (physical_devices)
SDL_free (physical_devices);
if (display_props)
SDL_free (display_props);
if (device_props)
SDL_free (device_props);
if (plane_props)
SDL_free (plane_props);
if (mode_props)
SDL_free (mode_props);
return ret;
}
#endif