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/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 9f88032508..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); @@ -1611,17 +1619,47 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, return true; } -static void vulkan_update_display_mode(VkDisplayModeKHR *best_mode, +static bool vulkan_update_display_mode( unsigned *width, unsigned *height, const VkDisplayModePropertiesKHR *mode, const struct vulkan_display_surface_info *info) { - *best_mode = VK_NULL_HANDLE; - *width = 0; - *height = 0; - (void)mode; - (void)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, @@ -1641,6 +1679,10 @@ static bool vulkan_create_display_surface(gfx_ctx_vulkan_data_t *vk, 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, @@ -1698,7 +1740,8 @@ static bool vulkan_create_display_surface(gfx_ctx_vulkan_data_t *vk, for (i = 0; i < mode_count; i++) { const VkDisplayModePropertiesKHR *mode = &modes[i]; - vulkan_update_display_mode(&best_mode, width, height, mode, info); + if (vulkan_update_display_mode(width, height, mode, info)) + best_mode = modes[i].displayMode; } free(modes); diff --git a/gfx/drivers_context/khr_display_ctx.c b/gfx/drivers_context/khr_display_ctx.c new file mode 100644 index 0000000000..1ab7ec45e8 --- /dev/null +++ b/gfx/drivers_context/khr_display_ctx.c @@ -0,0 +1,260 @@ +/* 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 "../../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 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) +{ + 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; + } + + 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)) + *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/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; /**