From 10a6d7a45893b98eea78785581b55bcfdf92b0eb Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 25 Jun 2016 21:00:58 +0200 Subject: [PATCH] Vulkan: Begin hooking up negotiation interface. --- dynamic.c | 15 +- gfx/common/vulkan_common.c | 360 ++++++++++++---------- gfx/video_driver.c | 13 + gfx/video_driver.h | 2 + libretro-common/include/libretro_vulkan.h | 39 ++- 5 files changed, 245 insertions(+), 184 deletions(-) diff --git a/dynamic.c b/dynamic.c index ff4c68c14b..169484dd66 100644 --- a/dynamic.c +++ b/dynamic.c @@ -1434,9 +1434,7 @@ bool rarch_environment_cb(unsigned cmd, void *data) { RARCH_WARN("Environ SET_MEMORY_MAPS, but system pointer not initialized..\n"); } - - - + break; } @@ -1495,7 +1493,16 @@ bool rarch_environment_cb(unsigned cmd, void *data) cheevos_set_support_cheevos(state); } #endif - break; + break; + + case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE: + { + const struct retro_hw_render_context_negotiation_interface *iface = + (const struct retro_hw_render_context_negotiation_interface*)data; + RARCH_LOG("Environ SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE.\n"); + video_driver_set_context_negotiation_interface(iface); + break; + } /* Default */ default: diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 7c00819f48..2fdf69408d 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -1372,150 +1372,30 @@ end: return ret; } - -bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, - enum vulkan_wsi_type type) +static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) { - unsigned i; - uint32_t queue_count; - VkResult res; - VkQueueFamilyProperties queue_properties[32]; - VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - VkPhysicalDeviceFeatures features = { false }; - VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; - VkDeviceCreateInfo device_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + bool use_device_ext; + static const float one = 1.0f; uint32_t gpu_count = 1; bool found_queue = false; VkPhysicalDevice *gpus = NULL; - static const float one = 1.0f; + + VkPhysicalDeviceFeatures features = { false }; + VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + VkDeviceCreateInfo device_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + VkQueueFamilyProperties queue_properties[32]; + uint32_t queue_count; + VkResult res; + unsigned i; + static const char *device_extensions[] = { "VK_KHR_swapchain", }; #ifdef VULKAN_DEBUG - const char *instance_extensions[3]; - instance_extensions[2] = "VK_EXT_debug_report"; - static const char *instance_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; static const char *device_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; -#else - const char *instance_extensions[2]; #endif - bool use_instance_ext, use_device_ext; - - instance_extensions[0] = "VK_KHR_surface"; - - switch (type) - { - case VULKAN_WSI_WAYLAND: - instance_extensions[1] = "VK_KHR_wayland_surface"; - break; - case VULKAN_WSI_ANDROID: - instance_extensions[1] = "VK_KHR_android_surface"; - break; - case VULKAN_WSI_WIN32: - instance_extensions[1] = "VK_KHR_win32_surface"; - break; - case VULKAN_WSI_XLIB: - instance_extensions[1] = "VK_KHR_xlib_surface"; - break; - case VULKAN_WSI_XCB: - instance_extensions[1] = "VK_KHR_xcb_surface"; - break; - case VULKAN_WSI_MIR: - instance_extensions[1] = "VK_KHR_mir_surface"; - break; - case VULKAN_WSI_NONE: - default: - instance_extensions[1] = NULL; - break; - } - -#ifdef _WIN32 - vulkan_library = dylib_load("vulkan-1.dll"); -#else - vulkan_library = dylib_load("libvulkan.so"); -#endif - - if (!vulkan_library) - { - RARCH_ERR("[Vulkan]: Failed to open Vulkan loader.\n"); - return false; - } - - RARCH_LOG("Vulkan dynamic library loaded.\n"); - - VKSYM(vk, GetInstanceProcAddr); - VK_GET_INSTANCE_PROC_ADDR(EnumerateInstanceExtensionProperties); - - use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ARRAY_SIZE(instance_extensions)); - - app.pApplicationName = "RetroArch"; - app.applicationVersion = 0; - app.pEngineName = "RetroArch"; - app.engineVersion = 0; - app.apiVersion = VK_MAKE_VERSION(1, 0, 6); - - info.pApplicationInfo = &app; - info.enabledExtensionCount = use_instance_ext ? ARRAY_SIZE(instance_extensions) : 0; - info.ppEnabledExtensionNames = use_instance_ext ? instance_extensions : NULL; -#ifdef VULKAN_DEBUG - info.enabledLayerCount = ARRAY_SIZE(instance_layers); - info.ppEnabledLayerNames = instance_layers; -#endif - - if (cached_instance) - { - vk->context.instance = cached_instance; - cached_instance = NULL; - res = VK_SUCCESS; - } - else - { - /* This will be called with a NULL instance, which - * is what we want. */ - VK_GET_INSTANCE_PROC_ADDR(CreateInstance); - res = VKFUNC(vkCreateInstance)(&info, NULL, &vk->context.instance); - } - -#ifdef VULKAN_DEBUG - VK_GET_INSTANCE_PROC_ADDR(CreateDebugReportCallbackEXT); - VK_GET_INSTANCE_PROC_ADDR(DebugReportMessageEXT); - VK_GET_INSTANCE_PROC_ADDR(DestroyDebugReportCallbackEXT); - - { - VkDebugReportCallbackCreateInfoEXT info = - { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; - info.flags = - VK_DEBUG_REPORT_ERROR_BIT_EXT | - VK_DEBUG_REPORT_WARNING_BIT_EXT | - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - info.pfnCallback = vulkan_debug_cb; - VKFUNC(vkCreateDebugReportCallbackEXT)(vk->context.instance, &info, NULL, &vk->context.debug_callback); - } -#endif - - /* Try different API versions if driver has compatible - * but slightly different VK_API_VERSION. */ - for (i = 1; i < 4 && res == VK_ERROR_INCOMPATIBLE_DRIVER; i++) - { - app.apiVersion = VK_MAKE_VERSION(1, 0, i); - res = VKFUNC(vkCreateInstance)(&info, NULL, &vk->context.instance); - } - - if (res == VK_ERROR_INCOMPATIBLE_DRIVER) - { - RARCH_ERR("Failed to create Vulkan instance.\n"); - return false; - } - - if (!vulkan_load_instance_symbols(vk)) - { - RARCH_ERR("[Vulkan]: Failed to load instance symbols.\n"); - return false; - } - if (VKFUNC(vkEnumeratePhysicalDevices)(vk->context.instance, &gpu_count, NULL) != VK_SUCCESS) { @@ -1594,8 +1474,8 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, device_info.ppEnabledExtensionNames = use_device_ext ? device_extensions : NULL; device_info.pEnabledFeatures = &features; #ifdef VULKAN_DEBUG - info.enabledLayerCount = ARRAY_SIZE(device_layers); - info.ppEnabledLayerNames = device_layers; + device_info.enabledLayerCount = ARRAY_SIZE(device_layers); + device_info.ppEnabledLayerNames = device_layers; #endif if (cached_device) @@ -1622,43 +1502,6 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, VKFUNC(vkGetDeviceQueue)(vk->context.device, vk->context.graphics_queue_index, 0, &vk->context.queue); - switch (type) - { - case VULKAN_WSI_WAYLAND: -#ifdef HAVE_WAYLAND - VK_GET_INSTANCE_PROC_ADDR(CreateWaylandSurfaceKHR); -#endif - break; - case VULKAN_WSI_ANDROID: -#ifdef ANDROID - VK_GET_INSTANCE_PROC_ADDR(CreateAndroidSurfaceKHR); -#endif - break; - case VULKAN_WSI_WIN32: -#ifdef _WIN32 - VK_GET_INSTANCE_PROC_ADDR(CreateWin32SurfaceKHR); -#endif - break; - case VULKAN_WSI_XLIB: -#ifdef HAVE_XLIB - VK_GET_INSTANCE_PROC_ADDR(CreateXlibSurfaceKHR); -#endif - break; - case VULKAN_WSI_XCB: -#ifdef HAVE_XCB - VK_GET_INSTANCE_PROC_ADDR(CreateXcbSurfaceKHR); -#endif - break; - case VULKAN_WSI_MIR: -#ifdef HAVE_MIR - VK_GET_INSTANCE_PROC_ADDR(CreateMirSurfaceKHR); -#endif - break; - case VULKAN_WSI_NONE: - default: - break; - } - #ifdef HAVE_THREADS vk->context.queue_lock = slock_new(); if (!vk->context.queue_lock) @@ -1671,6 +1514,171 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, return true; } +bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, + enum vulkan_wsi_type type) +{ + unsigned i; + VkResult res; + VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; +#ifdef VULKAN_DEBUG + const char *instance_extensions[3]; + instance_extensions[2] = "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; + struct retro_hw_render_context_negotiation_interface_vulkan *iface = + (struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface(); + + if (iface && iface->interface_type != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN) + { + RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong API.\n"); + iface = NULL; + } + + if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION) + { + RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n"); + iface = NULL; + } + + instance_extensions[0] = "VK_KHR_surface"; + + switch (type) + { + case VULKAN_WSI_WAYLAND: + instance_extensions[1] = "VK_KHR_wayland_surface"; + break; + case VULKAN_WSI_ANDROID: + instance_extensions[1] = "VK_KHR_android_surface"; + break; + case VULKAN_WSI_WIN32: + instance_extensions[1] = "VK_KHR_win32_surface"; + break; + case VULKAN_WSI_XLIB: + instance_extensions[1] = "VK_KHR_xlib_surface"; + break; + case VULKAN_WSI_XCB: + instance_extensions[1] = "VK_KHR_xcb_surface"; + break; + case VULKAN_WSI_MIR: + instance_extensions[1] = "VK_KHR_mir_surface"; + break; + case VULKAN_WSI_NONE: + default: + instance_extensions[1] = NULL; + break; + } + +#ifdef _WIN32 + vulkan_library = dylib_load("vulkan-1.dll"); +#else + vulkan_library = dylib_load("libvulkan.so"); +#endif + + if (!vulkan_library) + { + RARCH_ERR("[Vulkan]: Failed to open Vulkan loader.\n"); + return false; + } + + RARCH_LOG("Vulkan dynamic library loaded.\n"); + + VKSYM(vk, GetInstanceProcAddr); + VK_GET_INSTANCE_PROC_ADDR(EnumerateInstanceExtensionProperties); + + use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ARRAY_SIZE(instance_extensions)); + + app.pApplicationName = "RetroArch"; + app.applicationVersion = 0; + app.pEngineName = "RetroArch"; + app.engineVersion = 0; + app.apiVersion = VK_MAKE_VERSION(1, 0, 18); + + info.pApplicationInfo = &app; + info.enabledExtensionCount = use_instance_ext ? ARRAY_SIZE(instance_extensions) : 0; + info.ppEnabledExtensionNames = use_instance_ext ? instance_extensions : NULL; +#ifdef VULKAN_DEBUG + info.enabledLayerCount = ARRAY_SIZE(instance_layers); + info.ppEnabledLayerNames = instance_layers; +#endif + + if (iface && iface->get_application_info) + { + info.pApplicationInfo = iface->get_application_info(); + if (info.pApplicationInfo->pApplicationName) + { + RARCH_LOG("[Vulkan]: App: %s (version %u)\n", + info.pApplicationInfo->pApplicationName, + info.pApplicationInfo->applicationVersion); + } + + if (info.pApplicationInfo->pEngineName) + { + RARCH_LOG("[Vulkan]: Engine: %s (version %u)\n", + info.pApplicationInfo->pEngineName, + info.pApplicationInfo->engineVersion); + } + } + + if (cached_instance) + { + vk->context.instance = cached_instance; + cached_instance = NULL; + res = VK_SUCCESS; + } + else + { + /* This will be called with a NULL instance, which + * is what we want. */ + VK_GET_INSTANCE_PROC_ADDR(CreateInstance); + res = VKFUNC(vkCreateInstance)(&info, NULL, &vk->context.instance); + } + +#ifdef VULKAN_DEBUG + VK_GET_INSTANCE_PROC_ADDR(CreateDebugReportCallbackEXT); + VK_GET_INSTANCE_PROC_ADDR(DebugReportMessageEXT); + VK_GET_INSTANCE_PROC_ADDR(DestroyDebugReportCallbackEXT); + + { + VkDebugReportCallbackCreateInfoEXT info = + { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + info.flags = + VK_DEBUG_REPORT_ERROR_BIT_EXT | + VK_DEBUG_REPORT_WARNING_BIT_EXT | + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + info.pfnCallback = vulkan_debug_cb; + VKFUNC(vkCreateDebugReportCallbackEXT)(vk->context.instance, &info, NULL, &vk->context.debug_callback); + } +#endif + + /* Try different API versions if driver has compatible + * but slightly different VK_API_VERSION. */ + for (i = 1; i < 4 && res == VK_ERROR_INCOMPATIBLE_DRIVER; i++) + { + info.pApplicationInfo = &app; + app.apiVersion = VK_MAKE_VERSION(1, 0, i); + res = VKFUNC(vkCreateInstance)(&info, NULL, &vk->context.instance); + } + + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) + { + RARCH_ERR("Failed to create Vulkan instance.\n"); + return false; + } + + if (!vulkan_load_instance_symbols(vk)) + { + RARCH_ERR("[Vulkan]: Failed to load instance symbols.\n"); + return false; + } + + return true; +} + bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, enum vulkan_wsi_type type, void *display, void *surface, @@ -1681,6 +1689,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, { case VULKAN_WSI_WAYLAND: #ifdef HAVE_WAYLAND + VK_GET_INSTANCE_PROC_ADDR(CreateWaylandSurfaceKHR); { VkWaylandSurfaceCreateInfoKHR surf_info; @@ -1700,6 +1709,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, break; case VULKAN_WSI_ANDROID: #ifdef ANDROID + VK_GET_INSTANCE_PROC_ADDR(CreateAndroidSurfaceKHR); { VkAndroidSurfaceCreateInfoKHR surf_info; @@ -1722,6 +1732,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, break; case VULKAN_WSI_WIN32: #ifdef _WIN32 + VK_GET_INSTANCE_PROC_ADDR(CreateWin32SurfaceKHR); { VkWin32SurfaceCreateInfoKHR surf_info; @@ -1740,6 +1751,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, break; case VULKAN_WSI_XLIB: #ifdef HAVE_XLIB + VK_GET_INSTANCE_PROC_ADDR(CreateXlibSurfaceKHR); { VkXlibSurfaceCreateInfoKHR surf_info; @@ -1760,6 +1772,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, case VULKAN_WSI_XCB: #ifdef HAVE_X11 #ifdef HAVE_XCB + VK_GET_INSTANCE_PROC_ADDR(CreateXcbSurfaceKHR); { VkXcbSurfaceCreateInfoKHR surf_info; @@ -1780,6 +1793,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, break; case VULKAN_WSI_MIR: #ifdef HAVE_MIR + VK_GET_INSTANCE_PROC_ADDR(CreateMirSurfaceKHR); { VkMirSurfaceCreateInfoKHR surf_info; @@ -1801,6 +1815,10 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, return false; } + /* Must create device after surface since we need to be able to query queues to use for presentation. */ + if (!vulkan_context_init_device(vk)) + return false; + if (!vulkan_create_swapchain( vk, width, height, swap_interval)) return false; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 50aaec98b3..4013ad9037 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1283,6 +1283,8 @@ void video_driver_menu_settings(void **list_data, void *list_info_data, * used for GLES. * TODO: Refactor this better. */ static struct retro_hw_render_callback hw_render; +static const struct retro_hw_render_context_negotiation_interface *hw_render_context_negotiation; + static bool video_driver_use_rgba = false; static bool video_driver_data_own = false; static bool video_driver_active = false; @@ -1820,6 +1822,7 @@ void video_driver_deinit_hw_context(void) hw_render.context_destroy(); memset(&hw_render, 0, sizeof(hw_render)); + hw_render_context_negotiation = NULL; } struct retro_hw_render_callback *video_driver_get_hw_context(void) @@ -1827,6 +1830,16 @@ struct retro_hw_render_callback *video_driver_get_hw_context(void) return &hw_render; } +const struct retro_hw_render_context_negotiation_interface *video_driver_get_context_negotiation_interface(void) +{ + return hw_render_context_negotiation; +} + +void video_driver_set_context_negotiation_interface(const struct retro_hw_render_context_negotiation_interface *iface) +{ + hw_render_context_negotiation = iface; +} + void video_driver_set_video_cache_context(void) { video_driver_cache_context = true; diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 3ae3af07c6..07eaeb6b33 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -312,6 +312,8 @@ bool video_driver_owns_driver(void); bool video_driver_is_hw_context(void); void video_driver_deinit_hw_context(void); struct retro_hw_render_callback *video_driver_get_hw_context(void); +const struct retro_hw_render_context_negotiation_interface *video_driver_get_context_negotiation_interface(void); +void video_driver_set_context_negotiation_interface(const struct retro_hw_render_context_negotiation_interface *iface); void video_driver_set_video_cache_context(void); void video_driver_unset_video_cache_context(void); bool video_driver_is_video_cache_context(void); diff --git a/libretro-common/include/libretro_vulkan.h b/libretro-common/include/libretro_vulkan.h index c7e2586e2d..abb0f0c926 100644 --- a/libretro-common/include/libretro_vulkan.h +++ b/libretro-common/include/libretro_vulkan.h @@ -51,20 +51,27 @@ typedef void (*retro_vulkan_wait_sync_index_t)(void *handle); typedef void (*retro_vulkan_lock_queue_t)(void *handle); typedef void (*retro_vulkan_unlock_queue_t)(void *handle); +typedef const VkApplicationInfo *(*retro_vulkan_get_application_info_t)(void); + struct retro_vulkan_context { VkPhysicalDevice gpu; VkDevice device; VkQueue queue; uint32_t queue_family_index; + VkQueue presentation_queue; + uint32_t presentation_queue_family_index; }; -typedef void *(*retro_vulkan_create_device_t)( +typedef bool (*retro_vulkan_create_device_t)( struct retro_vulkan_context *context, VkInstance instance, - PFN_vkGetInstanceProcAddr get_proc_addr, + VkSurfaceKHR surface, + PFN_vkGetInstanceProcAddr get_instance_proc_addr, const char **required_device_extensions, unsigned num_required_device_extensions, + const char **required_device_layers, + unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features); typedef void (*retro_vulkan_destroy_handle_t)(void *data); @@ -79,12 +86,31 @@ typedef void (*retro_vulkan_destroy_handle_t)(void *data); struct retro_hw_render_context_negotiation_interface_vulkan { /* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */ - enum retro_hw_render_interface_type interface_type; + enum retro_hw_render_context_negotiation_interface_type interface_type; /* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION. */ unsigned interface_version; + /* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of + * its "default" application info. + */ + retro_vulkan_get_application_info_t get_application_info; + + /* If non-NULL, the libretro core will choose one or more physical devices, + * create one or more logical devices and create one or more queues. + * The core must prepare a designated PhysicalDevice, Device, Queue and queue family index + * which the frontend will use for its internal operation. + * + * The frontend will request certain extensions and layers for a device which is created. + * The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE. + * + * If presentation to "surface" is supported on the queue, presentation_queue must be equal to queue. + * If not, a second queue must be provided in presentation_queue and presentation_queue_index. + * + * The core is free to set its own queue priorities. + * Device provided to frontend is owned by the frontend, but any additional device resources must be freed by core + * in either destroy_context callback or retro_unload_game(). + */ retro_vulkan_create_device_t create_device; - retro_vulkan_destroy_handle_t destroy_handle; }; struct retro_hw_render_interface_vulkan @@ -108,11 +134,6 @@ struct retro_hw_render_interface_vulkan */ void *handle; - /* An opaque handle that is used to pass data from context negotiation interface - * to the hardware interface. - */ - void *core_handle; - /* The Vulkan instance the context is using. */ VkInstance instance; /* The physical device used. */