diff --git a/Makefile.common b/Makefile.common index b21ac70dff..5b28285af0 100644 --- a/Makefile.common +++ b/Makefile.common @@ -842,6 +842,7 @@ ifeq ($(HAVE_VULKAN), 1) OBJ += gfx/drivers/vulkan.o \ gfx/common/vulkan_common.o \ + gfx/drivers_context/khr_display_ctx.o \ libretro-common/vulkan/vulkan_symbol_wrapper.o \ gfx/drivers_font/vulkan_raster_font.o \ gfx/drivers_shader/shader_vulkan.o \ diff --git a/cores/libretro-test-vulkan/Makefile b/cores/libretro-test-vulkan/Makefile index db8f8c735d..c41feb44f4 100644 --- a/cores/libretro-test-vulkan/Makefile +++ b/cores/libretro-test-vulkan/Makefile @@ -43,7 +43,7 @@ else CFLAGS += -O3 endif -CFLAGS += -std=gnu99 +CFLAGS += -std=gnu99 -I../../gfx/include OBJECTS := libretro-test.o ../../libretro-common/vulkan/vulkan_symbol_wrapper.o CFLAGS += -Wall -pedantic $(fpic) diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 9f1ad5ad03..331121218a 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -1202,6 +1202,47 @@ end: return ret; } +static bool vulkan_context_init_gpu(gfx_ctx_vulkan_data_t *vk) +{ + uint32_t gpu_count = 0; + VkPhysicalDevice *gpus = NULL; + + if (vk->context.gpu != VK_NULL_HANDLE) + return true; + + if (vkEnumeratePhysicalDevices(vk->context.instance, + &gpu_count, NULL) != VK_SUCCESS) + { + RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); + return false; + } + + gpus = (VkPhysicalDevice*)calloc(gpu_count, sizeof(*gpus)); + if (!gpus) + { + RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); + return false; + } + + if (vkEnumeratePhysicalDevices(vk->context.instance, + &gpu_count, gpus) != VK_SUCCESS) + { + RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); + return false; + } + + if (gpu_count < 1) + { + RARCH_ERR("[Vulkan]: Failed to enumerate Vulkan physical device.\n"); + free(gpus); + return false; + } + + vk->context.gpu = gpus[0]; + free(gpus); + return true; +} + static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) { bool use_device_ext; @@ -1209,9 +1250,7 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) VkResult res; unsigned i; static const float one = 1.0f; - uint32_t gpu_count = 1; bool found_queue = false; - VkPhysicalDevice *gpus = NULL; VkPhysicalDeviceFeatures features = { false }; VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; @@ -1246,7 +1285,7 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) const VkPhysicalDeviceFeatures features = { 0 }; bool ret = iface->create_device(&context, vk->context.instance, - VK_NULL_HANDLE, + vk->context.gpu, vk->vk_surface, vulkan_symbol_wrapper_instance_proc_addr(), device_extensions, @@ -1287,39 +1326,8 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) cached_destroy_device = NULL; } - if (vk->context.gpu == VK_NULL_HANDLE) - { - if (vkEnumeratePhysicalDevices(vk->context.instance, - &gpu_count, NULL) != VK_SUCCESS) - { - RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; - } - - gpus = (VkPhysicalDevice*)calloc(gpu_count, sizeof(*gpus)); - if (!gpus) - { - RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; - } - - if (vkEnumeratePhysicalDevices(vk->context.instance, - &gpu_count, gpus) != VK_SUCCESS) - { - RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; - } - - if (gpu_count < 1) - { - RARCH_ERR("[Vulkan]: Failed to enumerate Vulkan physical device.\n"); - free(gpus); - return false; - } - - vk->context.gpu = gpus[0]; - free(gpus); - } + if (!vulkan_context_init_gpu(vk)) + return false; vkGetPhysicalDeviceProperties(vk->context.gpu, &vk->context.gpu_properties); @@ -1435,12 +1443,13 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, VkResult res; VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + + const char *instance_extensions[4]; + unsigned ext_count = 0; + #ifdef VULKAN_DEBUG - const char *instance_extensions[3]; - instance_extensions[2] = "VK_EXT_debug_report"; + instance_extensions[ext_count++] = "VK_EXT_debug_report"; static const char *instance_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; -#else - const char *instance_extensions[2]; #endif bool use_instance_ext; @@ -1459,31 +1468,33 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, iface = NULL; } - instance_extensions[0] = "VK_KHR_surface"; + instance_extensions[ext_count++] = "VK_KHR_surface"; switch (type) { case VULKAN_WSI_WAYLAND: - instance_extensions[1] = "VK_KHR_wayland_surface"; + instance_extensions[ext_count++] = "VK_KHR_wayland_surface"; break; case VULKAN_WSI_ANDROID: - instance_extensions[1] = "VK_KHR_android_surface"; + instance_extensions[ext_count++] = "VK_KHR_android_surface"; break; case VULKAN_WSI_WIN32: - instance_extensions[1] = "VK_KHR_win32_surface"; + instance_extensions[ext_count++] = "VK_KHR_win32_surface"; break; case VULKAN_WSI_XLIB: - instance_extensions[1] = "VK_KHR_xlib_surface"; + instance_extensions[ext_count++] = "VK_KHR_xlib_surface"; break; case VULKAN_WSI_XCB: - instance_extensions[1] = "VK_KHR_xcb_surface"; + instance_extensions[ext_count++] = "VK_KHR_xcb_surface"; break; case VULKAN_WSI_MIR: - instance_extensions[1] = "VK_KHR_mir_surface"; + instance_extensions[ext_count++] = "VK_KHR_mir_surface"; + break; + case VULKAN_WSI_DISPLAY: + instance_extensions[ext_count++] = "VK_KHR_display"; break; case VULKAN_WSI_NONE: default: - instance_extensions[1] = NULL; break; } @@ -1521,7 +1532,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, return false; } - use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ARRAY_SIZE(instance_extensions)); + use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ext_count); app.pApplicationName = "RetroArch"; app.applicationVersion = 0; @@ -1530,7 +1541,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, app.apiVersion = VK_MAKE_VERSION(1, 0, 18); info.pApplicationInfo = &app; - info.enabledExtensionCount = use_instance_ext ? ARRAY_SIZE(instance_extensions) : 0; + info.enabledExtensionCount = use_instance_ext ? ext_count : 0; info.ppEnabledExtensionNames = use_instance_ext ? instance_extensions : NULL; #ifdef VULKAN_DEBUG info.enabledLayerCount = ARRAY_SIZE(instance_layers); @@ -1608,6 +1619,214 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, return true; } +static bool vulkan_update_display_mode( + unsigned *width, + unsigned *height, + const VkDisplayModePropertiesKHR *mode, + const struct vulkan_display_surface_info *info) +{ + unsigned visible_width = mode->parameters.visibleRegion.width; + unsigned visible_height = mode->parameters.visibleRegion.height; + + if (!info->width || !info->height) + { + /* Strategy here is to pick something which is largest resolution. */ + unsigned area = visible_width * visible_height; + if (area > (*width) * (*height)) + { + *width = visible_width; + *height = visible_height; + return true; + } + else + return false; + } + else + { + /* For particular resolutions, find the closest. */ + int delta_x = (int)info->width - (int)visible_width; + int delta_y = (int)info->height - (int)visible_height; + int old_delta_x = (int)info->width - (int)*width; + int old_delta_y = (int)info->height - (int)*height; + + int dist = delta_x * delta_x + delta_y * delta_y; + int old_dist = old_delta_x * old_delta_x + old_delta_y * old_delta_y; + if (dist < old_dist) + { + *width = visible_width; + *height = visible_height; + return true; + } + else + return false; + } +} + +static bool vulkan_create_display_surface(gfx_ctx_vulkan_data_t *vk, + unsigned *width, unsigned *height, + const struct vulkan_display_surface_info *info) +{ + bool ret = true; + uint32_t display_count = 0; + uint32_t plane_count = 0; + VkDisplayPropertiesKHR *displays = NULL; + VkDisplayPlanePropertiesKHR *planes = NULL; + uint32_t mode_count = 0; + VkDisplayModePropertiesKHR *modes = NULL; + unsigned dpy, i, j; + uint32_t best_plane = UINT32_MAX; + VkDisplayPlaneAlphaFlagsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + VkDisplaySurfaceCreateInfoKHR create_info = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR }; + VkDisplayModeKHR best_mode = VK_NULL_HANDLE; + + /* We need to decide on GPU here to be able to query support. */ + if (!vulkan_context_init_gpu(vk)) + return false; + + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkGetPhysicalDeviceDisplayPropertiesKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkGetPhysicalDeviceDisplayPlanePropertiesKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkGetDisplayPlaneSupportedDisplaysKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkGetDisplayModePropertiesKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkCreateDisplayModeKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkGetDisplayPlaneCapabilitiesKHR); + VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance, + vkCreateDisplayPlaneSurfaceKHR); + +#define GOTO_FAIL() do { \ + ret = false; \ + goto end; \ +} while(0) + + if (vkGetPhysicalDeviceDisplayPropertiesKHR(vk->context.gpu, &display_count, NULL) != VK_SUCCESS) + GOTO_FAIL(); + displays = (VkDisplayPropertiesKHR*)calloc(display_count, sizeof(*displays)); + if (!displays) + GOTO_FAIL(); + if (vkGetPhysicalDeviceDisplayPropertiesKHR(vk->context.gpu, &display_count, displays) != VK_SUCCESS) + GOTO_FAIL(); + + if (vkGetPhysicalDeviceDisplayPlanePropertiesKHR(vk->context.gpu, &plane_count, NULL) != VK_SUCCESS) + GOTO_FAIL(); + planes = (VkDisplayPlanePropertiesKHR*)calloc(plane_count, sizeof(*planes)); + if (!planes) + GOTO_FAIL(); + if (vkGetPhysicalDeviceDisplayPlanePropertiesKHR(vk->context.gpu, &plane_count, planes) != VK_SUCCESS) + GOTO_FAIL(); + + for (dpy = 0; dpy < display_count; dpy++) + { + VkDisplayKHR display = displays[dpy].display; + best_mode = VK_NULL_HANDLE; + best_plane = UINT32_MAX; + + if (vkGetDisplayModePropertiesKHR(vk->context.gpu, + display, &mode_count, NULL) != VK_SUCCESS) + GOTO_FAIL(); + + modes = (VkDisplayModePropertiesKHR*)calloc(mode_count, sizeof(*modes)); + if (!modes) + GOTO_FAIL(); + + if (vkGetDisplayModePropertiesKHR(vk->context.gpu, + display, &mode_count, modes) != VK_SUCCESS) + GOTO_FAIL(); + + for (i = 0; i < mode_count; i++) + { + const VkDisplayModePropertiesKHR *mode = &modes[i]; + if (vulkan_update_display_mode(width, height, mode, info)) + best_mode = modes[i].displayMode; + } + + free(modes); + modes = NULL; + mode_count = 0; + + if (best_mode == VK_NULL_HANDLE) + continue; + + for (i = 0; i < plane_count; i++) + { + uint32_t supported_count = 0; + VkDisplayKHR *supported = NULL; + VkDisplayPlaneCapabilitiesKHR plane_caps; + vkGetDisplayPlaneSupportedDisplaysKHR(vk->context.gpu, i, &supported_count, NULL); + if (!supported_count) + continue; + + supported = (VkDisplayKHR*)calloc(supported_count, sizeof(*supported)); + if (!supported) + GOTO_FAIL(); + + vkGetDisplayPlaneSupportedDisplaysKHR(vk->context.gpu, i, &supported_count, + supported); + + for (j = 0; j < supported_count; j++) + { + if (supported[j] == display) + { + if (best_plane == UINT32_MAX) + best_plane = j; + break; + } + } + + free(supported); + supported = NULL; + + if (j == supported_count) + continue; + + if (planes[i].currentDisplay == VK_NULL_HANDLE || + planes[i].currentDisplay == display) + best_plane = j; + else + continue; + + vkGetDisplayPlaneCapabilitiesKHR(vk->context.gpu, + best_mode, i, &plane_caps); + + if (plane_caps.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR) + { + best_plane = j; + alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + goto out; + } + } + } +out: + + if (best_mode == VK_NULL_HANDLE) + GOTO_FAIL(); + if (best_plane == UINT32_MAX) + GOTO_FAIL(); + + create_info.displayMode = best_mode; + create_info.planeIndex = best_plane; + create_info.planeStackIndex = planes[best_plane].currentStackIndex; + create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + create_info.globalAlpha = 1.0f; + create_info.alphaMode = alpha_mode; + create_info.imageExtent.width = *width; + create_info.imageExtent.height = *height; + + if (vkCreateDisplayPlaneSurfaceKHR(vk->context.instance, + &create_info, NULL, &vk->vk_surface) != VK_SUCCESS) + GOTO_FAIL(); + +end: + free(displays); + free(planes); + free(modes); + return ret; +} + bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, enum vulkan_wsi_type type, void *display, void *surface, @@ -1751,6 +1970,14 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, } #endif break; + case VULKAN_WSI_DISPLAY: + { + if (!vulkan_create_display_surface(vk, + &width, &height, + (const struct vulkan_display_surface_info*)display)) + return false; + } + break; case VULKAN_WSI_NONE: default: return false; diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index 7a87d6c032..7e69ca3e7c 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -82,7 +82,8 @@ enum vulkan_wsi_type VULKAN_WSI_ANDROID, VULKAN_WSI_WIN32, VULKAN_WSI_XCB, - VULKAN_WSI_XLIB + VULKAN_WSI_XLIB, + VULKAN_WSI_DISPLAY }; typedef struct vulkan_context @@ -125,6 +126,12 @@ typedef struct gfx_ctx_vulkan_data bool need_new_swapchain; } gfx_ctx_vulkan_data_t; +struct vulkan_display_surface_info +{ + unsigned width; + unsigned height; +}; + struct vk_color { float r, g, b, a; diff --git a/gfx/drivers_context/khr_display_ctx.c b/gfx/drivers_context/khr_display_ctx.c new file mode 100644 index 0000000000..b4c0b8f209 --- /dev/null +++ b/gfx/drivers_context/khr_display_ctx.c @@ -0,0 +1,277 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2016 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include "../../driver.h" +#include "../../general.h" +#include "../../runloop.h" +#include "../common/vulkan_common.h" + +typedef struct +{ + gfx_ctx_vulkan_data_t vk; + unsigned swap_interval; + unsigned width; + unsigned height; +} khr_display_ctx_data_t; + +static volatile sig_atomic_t g_khr_quit; +static void khr_sighandler(int sig) +{ + if (g_khr_quit) + exit(1); + g_khr_quit = 1; +} + +static void gfx_ctx_khr_display_destroy(void *data) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + if (!khr) + return; + + vulkan_context_destroy(&khr->vk, true); +#ifdef HAVE_THREADS + if (khr->vk.context.queue_lock) + slock_free(khr->vk.context.queue_lock); +#endif + free(khr); +} + +static void gfx_ctx_khr_display_get_video_size(void *data, + unsigned *width, unsigned *height) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + + *width = khr->width; + *height = khr->height; +} + +static void *gfx_ctx_khr_display_init(void *video_driver) +{ + struct sigaction sa; + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)calloc(1, sizeof(*khr)); + if (!khr) + return NULL; + + if (!vulkan_context_init(&khr->vk, VULKAN_WSI_DISPLAY)) + { + RARCH_ERR("[Vulkan]: Failed to create Vulkan context.\n"); + goto error; + } + + sa.sa_sigaction = NULL; + sa.sa_handler = khr_sighandler; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + return khr; + +error: + gfx_ctx_khr_display_destroy(khr); + return NULL; +} + +static void gfx_ctx_khr_display_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height, unsigned frame_count) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + (void)frame_count; + + *resize = khr->vk.need_new_swapchain; + + if (khr->width != *width || khr->height != *height) + { + *width = khr->width; + *height = khr->height; + *resize = true; + } + + if (runloop_ctl(RUNLOOP_CTL_IS_SHUTDOWN, NULL) || g_khr_quit) + *quit = true; +} + +static bool gfx_ctx_khr_display_set_resize(void *data, + unsigned width, unsigned height) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + + khr->width = width; + khr->height = height; + if (!vulkan_create_swapchain(&khr->vk, khr->width, khr->height, khr->swap_interval)) + { + RARCH_ERR("[Vulkan]: Failed to update swapchain.\n"); + return false; + } + + khr->vk.context.invalid_swapchain = true; + khr->vk.need_new_swapchain = false; + return false; +} + +static void gfx_ctx_khr_display_update_window_title(void *data) +{ + char buf[128] = {0}; + char buf_fps[128] = {0}; + settings_t *settings = config_get_ptr(); + + (void)data; + + video_monitor_get_fps(buf, sizeof(buf), + buf_fps, sizeof(buf_fps)); + if (settings->fps_show) + runloop_msg_queue_push(buf_fps, 1, 1, false); +} + +static bool gfx_ctx_khr_display_set_video_mode(void *data, + unsigned width, unsigned height, + bool fullscreen) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + + if (!fullscreen) + { + width = 0; + height = 0; + } + + struct vulkan_display_surface_info info = { width, height }; + if (!vulkan_surface_create(&khr->vk, VULKAN_WSI_DISPLAY, &info, NULL, + 0, 0, khr->swap_interval)) + { + RARCH_ERR("[Vulkan]: Failed to create KHR_display surface.\n"); + goto error; + } + + khr->width = khr->vk.context.swapchain_width; + khr->height = khr->vk.context.swapchain_height; + + return true; + +error: + gfx_ctx_khr_display_destroy(data); + return false; +} + +static void gfx_ctx_khr_display_input_driver(void *data, + const input_driver_t **input, void **input_data) +{ + (void)data; + *input = NULL; + *input_data = NULL; +} + +static bool gfx_ctx_khr_display_bind_api(void *data, + enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + (void)data; + (void)major; + (void)minor; + return api == GFX_CTX_VULKAN_API; +} + +static bool gfx_ctx_khr_display_has_focus(void *data) +{ + (void)data; + return true; +} + +static bool gfx_ctx_khr_display_suppress_screensaver(void *data, bool enable) +{ + (void)data; + (void)enable; + return false; +} + +static bool gfx_ctx_khr_display_has_windowed(void *data) +{ + (void)data; + return false; +} + +static void gfx_ctx_khr_display_set_swap_interval(void *data, unsigned swap_interval) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + if (khr->swap_interval != swap_interval) + { + khr->swap_interval = swap_interval; + if (khr->vk.swapchain) + khr->vk.need_new_swapchain = true; + } +} + +static void gfx_ctx_khr_display_swap_buffers(void *data) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + vulkan_present(&khr->vk, khr->vk.context.current_swapchain_index); + vulkan_acquire_next_image(&khr->vk); +} + +static gfx_ctx_proc_t gfx_ctx_khr_display_get_proc_address(const char *symbol) +{ + return NULL; +} + +static uint32_t gfx_ctx_khr_display_get_flags(void *data) +{ + uint32_t flags = 0; + BIT32_SET(flags, GFX_CTX_FLAGS_NONE); + return flags; +} + +static void gfx_ctx_khr_display_set_flags(void *data, uint32_t flags) +{ + (void)data; +} + +static void *gfx_ctx_khr_display_get_context_data(void *data) +{ + khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; + return &khr->vk.context; +} + +const gfx_ctx_driver_t gfx_ctx_khr_display = { + gfx_ctx_khr_display_init, + gfx_ctx_khr_display_destroy, + gfx_ctx_khr_display_bind_api, + gfx_ctx_khr_display_set_swap_interval, + gfx_ctx_khr_display_set_video_mode, + gfx_ctx_khr_display_get_video_size, + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + NULL, /* get_metrics */ + NULL, + gfx_ctx_khr_display_update_window_title, + gfx_ctx_khr_display_check_window, + gfx_ctx_khr_display_set_resize, + gfx_ctx_khr_display_has_focus, + gfx_ctx_khr_display_suppress_screensaver, + gfx_ctx_khr_display_has_windowed, + gfx_ctx_khr_display_swap_buffers, + gfx_ctx_khr_display_input_driver, + gfx_ctx_khr_display_get_proc_address, + NULL, + NULL, + NULL, + "khr_display", + gfx_ctx_khr_display_get_flags, + gfx_ctx_khr_display_set_flags, + NULL, + gfx_ctx_khr_display_get_context_data, +}; + diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 66b7dbbdc9..4b336c9896 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -177,8 +177,10 @@ static void display_handle_mode(void *data, wl->width = width; wl->height = height; - RARCH_LOG("[Wayland]: Video mode: %d x %d @ %d Hz.\n", - width, height, refresh); + /* Certain older Wayland implementations report in Hz, + * but it should be mHz. */ + RARCH_LOG("[Wayland]: Video mode: %d x %d @ %.4f Hz.\n", + width, height, refresh > 1000 ? refresh / 1000.0 : (double)refresh); } static void display_handle_done(void *data, diff --git a/gfx/video_context_driver.c b/gfx/video_context_driver.c index 6df37d8023..a2cb8a196d 100644 --- a/gfx/video_context_driver.c +++ b/gfx/video_context_driver.c @@ -80,6 +80,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #endif #ifdef EMSCRIPTEN &gfx_ctx_emscripten, +#endif +#ifdef HAVE_VULKAN + &gfx_ctx_khr_display, #endif &gfx_ctx_null, NULL diff --git a/gfx/video_context_driver.h b/gfx/video_context_driver.h index c98ca0ebb2..e4bba14826 100644 --- a/gfx/video_context_driver.h +++ b/gfx/video_context_driver.h @@ -243,6 +243,7 @@ extern const gfx_ctx_driver_t gfx_ctx_cgl; extern const gfx_ctx_driver_t gfx_ctx_cocoagl; extern const gfx_ctx_driver_t gfx_ctx_emscripten; extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev; +extern const gfx_ctx_driver_t gfx_ctx_khr_display; extern const gfx_ctx_driver_t gfx_ctx_null; /**