From 3b218c64b101aa2c0239622a566fc026f58b4f98 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 29 Jan 2017 22:14:09 +1000 Subject: [PATCH] Vulkan: Refactor initialization to only use a single instance Hopefully will fix the crash in vkDestroyInstance on the NV Shield TV, and likely reduce boot times slightly for drivers that take a while to create instances. --- .../Core/VideoBackends/Vulkan/VideoBackend.h | 1 + .../VideoBackends/Vulkan/VulkanContext.cpp | 5 +- .../Core/VideoBackends/Vulkan/VulkanContext.h | 3 +- Source/Core/VideoBackends/Vulkan/main.cpp | 78 ++++++++++--------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VideoBackend.h b/Source/Core/VideoBackends/Vulkan/VideoBackend.h index 8e29326389..4a32ae076b 100644 --- a/Source/Core/VideoBackends/Vulkan/VideoBackend.h +++ b/Source/Core/VideoBackends/Vulkan/VideoBackend.h @@ -10,6 +10,7 @@ namespace Vulkan { class VideoBackend : public VideoBackendBase { +public: bool Initialize(void* window_handle) override; void Shutdown() override; diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index e3fe3ff799..a7d1b628fb 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -327,7 +327,7 @@ void VulkanContext::PopulateBackendInfoMultisampleModes( } std::unique_ptr VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu, - VkSurfaceKHR surface, VideoConfig* config, + VkSurfaceKHR surface, bool enable_debug_reports, bool enable_validation_layer) { @@ -354,9 +354,6 @@ std::unique_ptr VulkanContext::Create(VkInstance instance, VkPhys return nullptr; } - // Update video config with features. - PopulateBackendInfoFeatures(config, gpu, context->m_device_features); - PopulateBackendInfoMultisampleModes(config, gpu, context->m_device_properties); return context; } diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 1faac8cf37..e7c8f27ff1 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -43,8 +43,7 @@ public: // This assumes that PopulateBackendInfo and PopulateBackendInfoAdapters has already // been called for the specified VideoConfig. static std::unique_ptr Create(VkInstance instance, VkPhysicalDevice gpu, - VkSurfaceKHR surface, VideoConfig* config, - bool enable_debug_reports, + VkSurfaceKHR surface, bool enable_debug_reports, bool enable_validation_layer); // Enable/disable debug message runtime. diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index 559b5a3976..8cff0fef74 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -98,13 +98,6 @@ bool VideoBackend::Initialize(void* window_handle) return false; } - // HACK: Use InitBackendInfo to initially populate backend features. - // This is because things like stereo get disabled when the config is validated, - // which happens before our device is created (settings control instance behavior), - // and we don't want that to happen if the device actually supports it. - InitBackendInfo(); - InitializeShared(); - // Check for presence of the validation layers before trying to enable it bool enable_validation_layer = g_Config.bEnableValidationLayer; if (enable_validation_layer && !VulkanContext::CheckValidationLayerAvailablility()) @@ -113,7 +106,8 @@ bool VideoBackend::Initialize(void* window_handle) enable_validation_layer = false; } - // Create Vulkan instance, needed before we can create a surface. + // Create Vulkan instance, needed before we can create a surface, or enumerate devices. + // We use this instance to fill in backend info, then re-use it for the actual device. bool enable_surface = window_handle != nullptr; bool enable_debug_reports = ShouldEnableDebugReports(enable_validation_layer); VkInstance instance = VulkanContext::CreateVulkanInstance(enable_surface, enable_debug_reports, @@ -122,21 +116,34 @@ bool VideoBackend::Initialize(void* window_handle) { PanicAlert("Failed to create Vulkan instance."); UnloadVulkanLibrary(); - ShutdownShared(); return false; } - // Load instance function pointers + // Load instance function pointers. if (!LoadVulkanInstanceFunctions(instance)) { PanicAlert("Failed to load Vulkan instance functions."); vkDestroyInstance(instance, nullptr); UnloadVulkanLibrary(); - ShutdownShared(); return false; } - // Create Vulkan surface + // Obtain a list of physical devices (GPUs) from the instance. + // We'll re-use this list later when creating the device. + VulkanContext::GPUList gpu_list = VulkanContext::EnumerateGPUs(instance); + if (gpu_list.empty()) + { + PanicAlert("No Vulkan physical devices available."); + vkDestroyInstance(instance, nullptr); + UnloadVulkanLibrary(); + return false; + } + + // Populate BackendInfo with as much information as we can at this point. + VulkanContext::PopulateBackendInfo(&g_Config); + VulkanContext::PopulateBackendInfoAdapters(&g_Config, gpu_list); + + // We need the surface before we can create a device, as some parameters depend on it. VkSurfaceKHR surface = VK_NULL_HANDLE; if (enable_surface) { @@ -146,45 +153,39 @@ bool VideoBackend::Initialize(void* window_handle) PanicAlert("Failed to create Vulkan surface."); vkDestroyInstance(instance, nullptr); UnloadVulkanLibrary(); - ShutdownShared(); return false; } } - // Fill the adapter list, and check if the user has selected an invalid device - // For some reason nvidia's driver crashes randomly if you call vkEnumeratePhysicalDevices - // after creating a device.. - VulkanContext::GPUList gpu_list = VulkanContext::EnumerateGPUs(instance); + // Since we haven't called InitializeShared yet, iAdapter may be out of range, + // so we have to check it ourselves. size_t selected_adapter_index = static_cast(g_Config.iAdapter); - if (gpu_list.empty()) - { - PanicAlert("No Vulkan physical devices available."); - if (surface != VK_NULL_HANDLE) - vkDestroySurfaceKHR(instance, surface, nullptr); - - vkDestroyInstance(instance, nullptr); - UnloadVulkanLibrary(); - ShutdownShared(); - return false; - } - else if (selected_adapter_index >= gpu_list.size()) + if (selected_adapter_index >= gpu_list.size()) { WARN_LOG(VIDEO, "Vulkan adapter index out of range, selecting first adapter."); selected_adapter_index = 0; } - // Pass ownership over to VulkanContext, and let it take care of everything. - g_vulkan_context = - VulkanContext::Create(instance, gpu_list[selected_adapter_index], surface, &g_Config, - enable_debug_reports, enable_validation_layer); + // Now we can create the Vulkan device. VulkanContext takes ownership of the instance and surface. + g_vulkan_context = VulkanContext::Create(instance, gpu_list[selected_adapter_index], surface, + enable_debug_reports, enable_validation_layer); if (!g_vulkan_context) { PanicAlert("Failed to create Vulkan device"); UnloadVulkanLibrary(); - ShutdownShared(); return false; } + // Since VulkanContext maintains a copy of the device features and properties, we can use this + // to initialize the backend information, so that we don't need to enumerate everything again. + VulkanContext::PopulateBackendInfoFeatures(&g_Config, g_vulkan_context->GetPhysicalDevice(), + g_vulkan_context->GetDeviceFeatures()); + VulkanContext::PopulateBackendInfoMultisampleModes( + &g_Config, g_vulkan_context->GetPhysicalDevice(), g_vulkan_context->GetDeviceProperties()); + + // With the backend information populated, we can now initialize videocommon. + InitializeShared(); + // Create swap chain. This has to be done early so that the target size is correct for auto-scale. std::unique_ptr swap_chain; if (surface != VK_NULL_HANDLE) @@ -193,6 +194,9 @@ bool VideoBackend::Initialize(void* window_handle) if (!swap_chain) { PanicAlert("Failed to create Vulkan swap chain."); + g_vulkan_context.reset(); + ShutdownShared(); + UnloadVulkanLibrary(); return false; } } @@ -204,8 +208,8 @@ bool VideoBackend::Initialize(void* window_handle) PanicAlert("Failed to create Vulkan command buffers"); g_command_buffer_mgr.reset(); g_vulkan_context.reset(); - UnloadVulkanLibrary(); ShutdownShared(); + UnloadVulkanLibrary(); return false; } @@ -227,8 +231,8 @@ bool VideoBackend::Initialize(void* window_handle) g_object_cache.reset(); g_command_buffer_mgr.reset(); g_vulkan_context.reset(); - UnloadVulkanLibrary(); ShutdownShared(); + UnloadVulkanLibrary(); return false; } @@ -249,8 +253,8 @@ bool VideoBackend::Initialize(void* window_handle) g_object_cache.reset(); g_command_buffer_mgr.reset(); g_vulkan_context.reset(); - UnloadVulkanLibrary(); ShutdownShared(); + UnloadVulkanLibrary(); return false; }