diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 2fdf69408d..cc10de52ae 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -1383,7 +1383,6 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) 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; @@ -1396,101 +1395,176 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) static const char *device_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; #endif - if (VKFUNC(vkEnumeratePhysicalDevices)(vk->context.instance, - &gpu_count, NULL) != VK_SUCCESS) + 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_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; + RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong API.\n"); + iface = NULL; } - gpus = (VkPhysicalDevice*)calloc(gpu_count, sizeof(*gpus)); - if (!gpus) + if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION) { - RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; + RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n"); + iface = NULL; } - if (VKFUNC(vkEnumeratePhysicalDevices)(vk->context.instance, - &gpu_count, gpus) != VK_SUCCESS) + if (!cached_device && iface && iface->create_device) { - RARCH_ERR("[Vulkan]: Failed to enumerate physical devices.\n"); - return false; + struct retro_vulkan_context context = { 0 }; + const VkPhysicalDeviceFeatures features = { 0 }; + + bool ret = iface->create_device(&context, vk->context.instance, + vk->vk_surface, + VKFUNC(vkGetInstanceProcAddr), + device_extensions, + ARRAY_SIZE(device_extensions), +#ifdef VULKAN_DEBUG + device_layers, + ARRAY_SIZE(device_layers), +#else + NULL, + 0, +#endif + &features); + + if (!ret) + { + RARCH_WARN("[Vulkan]: Failed to create device with negotiation interface. Falling back to default path.\n"); + } + else + { + vk->context.destroy_device = iface->destroy_device; + + vk->context.device = context.device; + vk->context.queue = context.queue; + vk->context.gpu = context.gpu; + vk->context.graphics_queue_index = context.queue_family_index; + + if (context.presentation_queue != context.queue) + { + RARCH_ERR("[Vulkan]: Present queue != graphics queue. This is currently not supported.\n"); + return false; + } + } } - if (gpu_count < 1) + if (vk->context.gpu == VK_NULL_HANDLE) { - RARCH_ERR("[Vulkan]: Failed to enumerate Vulkan physical device.\n"); + if (VKFUNC(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 (VKFUNC(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 false; } - vk->context.gpu = gpus[0]; - free(gpus); - VKFUNC(vkGetPhysicalDeviceProperties)(vk->context.gpu, &vk->context.gpu_properties); VKFUNC(vkGetPhysicalDeviceMemoryProperties)(vk->context.gpu, &vk->context.memory_properties); - VKFUNC(vkGetPhysicalDeviceQueueFamilyProperties)(vk->context.gpu, - &queue_count, NULL); - if (queue_count < 1 || queue_count > 32) + if (vk->context.device == VK_NULL_HANDLE) { - RARCH_ERR("[Vulkan]: Invalid number of queues detected.\n"); - return false; - } + VkQueueFamilyProperties *queue_properties = NULL; + VKFUNC(vkGetPhysicalDeviceQueueFamilyProperties)(vk->context.gpu, + &queue_count, NULL); - VKFUNC(vkGetPhysicalDeviceQueueFamilyProperties)(vk->context.gpu, - &queue_count, queue_properties); - - for (i = 0; i < queue_count; i++) - { - VkQueueFlags required = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; - if ((queue_properties[i].queueFlags & required) == required) + if (queue_count < 1) { - vk->context.graphics_queue_index = i; - RARCH_LOG("[Vulkan]: Device supports %u sub-queues.\n", - queue_properties[i].queueCount); - found_queue = true; - break; + RARCH_ERR("[Vulkan]: Invalid number of queues detected.\n"); + return false; } - } - if (!found_queue) - { - RARCH_ERR("[Vulkan]: Did not find suitable graphics queue.\n"); - return false; - } + queue_properties = (VkQueueFamilyProperties*)malloc(queue_count * sizeof(*queue_properties)); + if (!queue_properties) + return false; - use_device_ext = vulkan_find_device_extensions(vk->context.gpu, device_extensions, ARRAY_SIZE(device_extensions)); + VKFUNC(vkGetPhysicalDeviceQueueFamilyProperties)(vk->context.gpu, + &queue_count, queue_properties); - queue_info.queueFamilyIndex = vk->context.graphics_queue_index; - queue_info.queueCount = 1; - queue_info.pQueuePriorities = &one; + for (i = 0; i < queue_count; i++) + { + VkBool32 supported = VK_FALSE; + VKFUNC(vkGetPhysicalDeviceSurfaceSupportKHR)( + vk->context.gpu, vk->context.graphics_queue_index, + vk->vk_surface, &supported); - device_info.queueCreateInfoCount = 1; - device_info.pQueueCreateInfos = &queue_info; - device_info.enabledExtensionCount = use_device_ext ? ARRAY_SIZE(device_extensions) : 0; - device_info.ppEnabledExtensionNames = use_device_ext ? device_extensions : NULL; - device_info.pEnabledFeatures = &features; + VkQueueFlags required = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + if (supported && ((queue_properties[i].queueFlags & required) == required)) + { + vk->context.graphics_queue_index = i; + RARCH_LOG("[Vulkan]: Queue family %u supports %u sub-queues.\n", + i, queue_properties[i].queueCount); + found_queue = true; + break; + } + } + + free(queue_properties); + + if (!found_queue) + { + RARCH_ERR("[Vulkan]: Did not find suitable graphics queue.\n"); + return false; + } + + use_device_ext = vulkan_find_device_extensions(vk->context.gpu, + device_extensions, ARRAY_SIZE(device_extensions)); + + queue_info.queueFamilyIndex = vk->context.graphics_queue_index; + queue_info.queueCount = 1; + queue_info.pQueuePriorities = &one; + + device_info.queueCreateInfoCount = 1; + device_info.pQueueCreateInfos = &queue_info; + device_info.enabledExtensionCount = use_device_ext ? ARRAY_SIZE(device_extensions) : 0; + device_info.ppEnabledExtensionNames = use_device_ext ? device_extensions : NULL; + device_info.pEnabledFeatures = &features; #ifdef VULKAN_DEBUG - device_info.enabledLayerCount = ARRAY_SIZE(device_layers); - device_info.ppEnabledLayerNames = device_layers; + device_info.enabledLayerCount = ARRAY_SIZE(device_layers); + device_info.ppEnabledLayerNames = device_layers; #endif - if (cached_device) - { - vk->context.device = cached_device; - cached_device = NULL; + if (cached_device) + { + vk->context.device = cached_device; + cached_device = NULL; - video_driver_set_video_cache_context_ack(); - RARCH_LOG("[Vulkan]: Using cached Vulkan context.\n"); - } - else if (VKFUNC(vkCreateDevice)(vk->context.gpu, &device_info, - NULL, &vk->context.device) != VK_SUCCESS) - { - RARCH_ERR("[Vulkan]: Failed to create device.\n"); - return false; + video_driver_set_video_cache_context_ack(); + RARCH_LOG("[Vulkan]: Using cached Vulkan context.\n"); + } + else if (VKFUNC(vkCreateDevice)(vk->context.gpu, &device_info, + NULL, &vk->context.device) != VK_SUCCESS) + { + RARCH_ERR("[Vulkan]: Failed to create device.\n"); + return false; + } } if (!vulkan_load_device_symbols(vk)) @@ -1499,8 +1573,11 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) return false; } - VKFUNC(vkGetDeviceQueue)(vk->context.device, - vk->context.graphics_queue_index, 0, &vk->context.queue); + if (vk->context.queue == VK_NULL_HANDLE) + { + VKFUNC(vkGetDeviceQueue)(vk->context.device, + vk->context.graphics_queue_index, 0, &vk->context.queue); + } #ifdef HAVE_THREADS vk->context.queue_lock = slock_new(); @@ -1896,7 +1973,12 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, if (vk->context.device) VKFUNC(vkDestroyDevice)(vk->context.device, NULL); if (vk->context.instance) + { + if (vk->context.destroy_device) + vk->context.destroy_device(); + VKFUNC(vkDestroyInstance)(vk->context.instance, NULL); + } } if (vulkan_library) @@ -1967,7 +2049,6 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, VkExtent2D swapchain_size; VkSwapchainKHR old_swapchain; VkSurfaceTransformFlagBitsKHR pre_transform; - VkBool32 supported; VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; settings_t *settings = config_get_ptr(); @@ -2086,15 +2167,6 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - if (VKFUNC(vkGetPhysicalDeviceSurfaceSupportKHR)( - vk->context.gpu, vk->context.graphics_queue_index, - vk->vk_surface, &supported) != VK_SUCCESS || !supported) - { - RARCH_ERR("[Vulkan]: GPU does not supported presentation on queue family %u with surface 0x%llx.\n", - vk->context.graphics_queue_index, (unsigned long long)vk->vk_surface); - return false; - } - if (VKFUNC(vkCreateSwapchainKHR)(vk->context.device, &info, NULL, &vk->swapchain) != VK_SUCCESS) { RARCH_ERR("[Vulkan]: Failed to create swapchain.\n"); diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index 9eb0a35799..3afff082a8 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -107,6 +107,7 @@ typedef struct vulkan_context VkFormat swapchain_format; slock_t *queue_lock; + retro_vulkan_destroy_device_t destroy_device; #ifdef VULKAN_DEBUG VkDebugReportCallbackEXT debug_callback; diff --git a/libretro-common/include/libretro_vulkan.h b/libretro-common/include/libretro_vulkan.h index abb0f0c926..2bcf9dc124 100644 --- a/libretro-common/include/libretro_vulkan.h +++ b/libretro-common/include/libretro_vulkan.h @@ -74,7 +74,7 @@ typedef bool (*retro_vulkan_create_device_t)( unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features); -typedef void (*retro_vulkan_destroy_handle_t)(void *data); +typedef void (*retro_vulkan_destroy_device_t)(void); /* Note on thread safety: * The Vulkan API is heavily designed around multi-threading, and @@ -108,9 +108,24 @@ struct retro_hw_render_context_negotiation_interface_vulkan * * 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(). + * in destroy_device callback. + * + * If this function returns true, a PhysicalDevice, Device and Queues are initialized. + * If false, none of the above have been initialized and the frontend will attempt + * to fallback to "default" device creation, as if this function was never called. */ retro_vulkan_create_device_t create_device; + + /* If non-NULL, this callback is called similar to context_destroy for HW_RENDER_INTERFACE. + * However, it will be called even if context_reset was not called. + * This can happen if the context never succeeds in being created. + * destroy_device will always be called before the VkInstance + * of the frontend is destroyed if create_device was called successfully so that the core has a chance of + * tearing down its own device resources. + * + * Only auxillary resources should be freed here, i.e. resources which are not part of retro_vulkan_context. + */ + retro_vulkan_destroy_device_t destroy_device; }; struct retro_hw_render_interface_vulkan