| // Copyright (c) 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/vulkan/vulkan_instance.h" |
| |
| #include <vector> |
| |
| #include "base/containers/flat_set.h" |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "build/build_config.h" |
| #include "gpu/vulkan/vulkan_crash_keys.h" |
| #include "gpu/vulkan/vulkan_device_queue.h" |
| #include "gpu/vulkan/vulkan_function_pointers.h" |
| #include "gpu/vulkan/vulkan_util.h" |
| #include "ui/gl/gl_angle_util_vulkan.h" |
| #include "ui/gl/gl_switches.h" |
| |
| namespace gpu { |
| |
| namespace { |
| |
| #if DCHECK_IS_ON() |
| const char* kSkippedErrors[] = { |
| // http://anglebug.com/4583 |
| "VUID-VkGraphicsPipelineCreateInfo-blendEnable-02023", |
| }; |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL |
| VulkanErrorCallback(VkDebugReportFlagsEXT flags, |
| VkDebugReportObjectTypeEXT object_type, |
| uint64_t object, |
| size_t location, |
| int32_t message_code, |
| const char* layer_prefix, |
| const char* message, |
| void* user_data) { |
| static base::flat_set<const char*> hitted_errors; |
| for (const char* error : kSkippedErrors) { |
| if (strstr(message, error) != nullptr) { |
| if (hitted_errors.find(error) != hitted_errors.end()) |
| return VK_FALSE; |
| hitted_errors.insert(error); |
| } |
| } |
| LOG(ERROR) << message; |
| return VK_FALSE; |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL |
| VulkanWarningCallback(VkDebugReportFlagsEXT flags, |
| VkDebugReportObjectTypeEXT object_type, |
| uint64_t object, |
| size_t location, |
| int32_t message_code, |
| const char* layer_prefix, |
| const char* message, |
| void* user_data) { |
| LOG(WARNING) << message; |
| return VK_FALSE; |
| } |
| #endif // DCHECK_IS_ON() |
| |
| } // namespace |
| |
| VulkanInstance::VulkanInstance() |
| : is_from_angle_(base::FeatureList::IsEnabled(features::kVulkanFromANGLE)) { |
| } |
| |
| VulkanInstance::~VulkanInstance() { |
| Destroy(); |
| } |
| |
| bool VulkanInstance::Initialize( |
| const base::FilePath& vulkan_loader_library_path, |
| const std::vector<const char*>& required_extensions, |
| const std::vector<const char*>& required_layers) { |
| if (!BindUnassignedFunctionPointers(vulkan_loader_library_path)) |
| return false; |
| return InitializeInstace(required_extensions, required_layers); |
| } |
| |
| bool VulkanInstance::BindUnassignedFunctionPointers( |
| const base::FilePath& vulkan_loader_library_path) { |
| VulkanFunctionPointers* vulkan_function_pointers = |
| gpu::GetVulkanFunctionPointers(); |
| if (is_from_angle_) { |
| PFN_vkGetInstanceProcAddr proc = gl::QueryVkGetInstanceProcAddrFromANGLE(); |
| if (!proc) { |
| LOG(ERROR) << "Failed to get vkGetInstanceProcAddr pointer from ANGLE."; |
| return false; |
| } |
| if (!vulkan_function_pointers |
| ->BindUnassociatedFunctionPointersFromGetProcAddr(proc)) { |
| return false; |
| } |
| } else { |
| base::NativeLibraryLoadError error; |
| loader_library_ = |
| base::LoadNativeLibrary(vulkan_loader_library_path, &error); |
| if (!loader_library_) { |
| LOG(ERROR) << "Failed to load vulkan:" << error.ToString(); |
| return false; |
| } |
| if (!vulkan_function_pointers |
| ->BindUnassociatedFunctionPointersFromLoaderLib(loader_library_)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool VulkanInstance::InitializeInstace( |
| const std::vector<const char*>& required_extensions, |
| const std::vector<const char*>& required_layers) { |
| if (is_from_angle_) |
| return InitializeFromANGLE(required_extensions, required_layers); |
| return CreateInstance(required_extensions, required_layers); |
| } |
| |
| bool VulkanInstance::CreateInstance( |
| const std::vector<const char*>& required_extensions, |
| const std::vector<const char*>& required_layers) { |
| DCHECK(!vk_instance_); |
| |
| if (!CollectBasicInfo(required_layers)) |
| return false; |
| |
| vulkan_info_.used_api_version = kVulkanRequiredApiVersion; |
| |
| VkApplicationInfo app_info = {}; |
| app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| app_info.pApplicationName = "Chromium"; |
| app_info.apiVersion = vulkan_info_.used_api_version; |
| |
| vulkan_info_.enabled_instance_extensions = required_extensions; |
| |
| for (const VkExtensionProperties& ext_property : |
| vulkan_info_.instance_extensions) { |
| if (strcmp(ext_property.extensionName, |
| VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { |
| debug_report_enabled_ = true; |
| vulkan_info_.enabled_instance_extensions.push_back( |
| VK_EXT_DEBUG_REPORT_EXTENSION_NAME); |
| } |
| } |
| |
| #if DCHECK_IS_ON() |
| for (const char* enabled_extension : |
| vulkan_info_.enabled_instance_extensions) { |
| bool found = false; |
| for (const VkExtensionProperties& ext_property : |
| vulkan_info_.instance_extensions) { |
| if (strcmp(ext_property.extensionName, enabled_extension) == 0) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| DLOG(ERROR) << "Required extension " << enabled_extension |
| << " missing from enumerated Vulkan extensions. " |
| "vkCreateInstance will likely fail."; |
| } |
| } |
| #endif |
| |
| VkInstanceCreateInfo instance_create_info = { |
| VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType |
| nullptr, // pNext |
| 0, // flags |
| &app_info, // pApplicationInfo |
| base::checked_cast<uint32_t>(required_layers.size()), |
| // enableLayerCount |
| required_layers.data(), // ppEnabledLayerNames |
| base::checked_cast<uint32_t>( |
| vulkan_info_.enabled_instance_extensions.size()), |
| // enabledExtensionCount |
| vulkan_info_.enabled_instance_extensions.data(), |
| // ppEnabledExtensionNames |
| }; |
| |
| VkResult result = |
| vkCreateInstance(&instance_create_info, nullptr, &owned_vk_instance_); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkCreateInstance() failed: " << result; |
| return false; |
| } |
| vk_instance_ = owned_vk_instance_; |
| |
| gfx::ExtensionSet enabled_extensions( |
| std::begin(vulkan_info_.enabled_instance_extensions), |
| std::end(vulkan_info_.enabled_instance_extensions)); |
| |
| VulkanFunctionPointers* vulkan_function_pointers = |
| gpu::GetVulkanFunctionPointers(); |
| if (!vulkan_function_pointers->BindInstanceFunctionPointers( |
| vk_instance_, vulkan_info_.used_api_version, enabled_extensions)) { |
| return false; |
| } |
| |
| #if DCHECK_IS_ON() |
| // Register our error logging function. |
| if (debug_report_enabled_) { |
| VkDebugReportCallbackCreateInfoEXT cb_create_info = {}; |
| cb_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; |
| |
| cb_create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT; |
| cb_create_info.pfnCallback = &VulkanErrorCallback; |
| result = vkCreateDebugReportCallbackEXT(vk_instance_, &cb_create_info, |
| nullptr, &error_callback_); |
| if (VK_SUCCESS != result) { |
| error_callback_ = VK_NULL_HANDLE; |
| DLOG(ERROR) << "vkCreateDebugReportCallbackEXT(ERROR) failed: " << result; |
| return false; |
| } |
| |
| cb_create_info.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | |
| VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; |
| cb_create_info.pfnCallback = &VulkanWarningCallback; |
| result = vkCreateDebugReportCallbackEXT(vk_instance_, &cb_create_info, |
| nullptr, &warning_callback_); |
| if (VK_SUCCESS != result) { |
| warning_callback_ = VK_NULL_HANDLE; |
| DLOG(ERROR) << "vkCreateDebugReportCallbackEXT(WARN) failed: " << result; |
| return false; |
| } |
| } |
| #endif |
| |
| if (!CollectDeviceInfo()) |
| return false; |
| |
| return true; |
| } |
| |
| bool VulkanInstance::InitializeFromANGLE( |
| const std::vector<const char*>& required_extensions, |
| const std::vector<const char*>& required_layers) { |
| vk_instance_ = gl::QueryVkInstanceFromANGLE(); |
| if (vk_instance_ == VK_NULL_HANDLE) |
| return false; |
| |
| uint32_t api_version = gl::QueryVkVersionFromANGLE(); |
| if (api_version < kVulkanRequiredApiVersion) |
| return false; |
| |
| if (!CollectBasicInfo({})) |
| return false; |
| |
| vulkan_info_.used_api_version = api_version; |
| |
| auto extensions = gl::QueryVkInstanceExtensionsFromANGLE(); |
| DCHECK(!extensions.empty()); |
| |
| for (const auto& extension : extensions) |
| vulkan_info_.enabled_instance_extensions.push_back(extension.data()); |
| |
| VulkanFunctionPointers* vulkan_function_pointers = |
| gpu::GetVulkanFunctionPointers(); |
| if (!vulkan_function_pointers->BindInstanceFunctionPointers( |
| vk_instance_, vulkan_info_.used_api_version, extensions)) { |
| return false; |
| } |
| |
| VkPhysicalDevice physical_device = gl::QueryVkPhysicalDeviceFromANGLE(); |
| if (physical_device == VK_NULL_HANDLE) |
| return false; |
| |
| if (!CollectDeviceInfo(physical_device)) |
| return false; |
| |
| return true; |
| } |
| |
| bool VulkanInstance::CollectBasicInfo( |
| const std::vector<const char*>& required_layers) { |
| VkResult result = vkEnumerateInstanceVersion(&vulkan_info_.api_version); |
| if (result != VK_SUCCESS) { |
| DLOG(ERROR) << "vkEnumerateInstanceVersion() failed: " << result; |
| return false; |
| } |
| |
| if (vulkan_info_.api_version < kVulkanRequiredApiVersion) |
| return false; |
| |
| gpu::crash_keys::vulkan_api_version.Set( |
| VkVersionToString(vulkan_info_.api_version)); |
| |
| // Query the extensions from all layers, including ones that are implicitly |
| // available (identified by passing a null ptr as the layer name). |
| std::vector<const char*> all_required_layers = required_layers; |
| |
| // Include the extension properties provided by the Vulkan implementation as |
| // part of the enumeration. |
| all_required_layers.push_back(nullptr); |
| |
| for (const char* layer_name : all_required_layers) { |
| uint32_t num_instance_exts = 0; |
| result = vkEnumerateInstanceExtensionProperties( |
| layer_name, &num_instance_exts, nullptr); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkEnumerateInstanceExtensionProperties(" << layer_name |
| << ") failed: " << result; |
| return false; |
| } |
| |
| const size_t previous_extension_count = |
| vulkan_info_.instance_extensions.size(); |
| vulkan_info_.instance_extensions.resize(previous_extension_count + |
| num_instance_exts); |
| result = vkEnumerateInstanceExtensionProperties( |
| layer_name, &num_instance_exts, |
| &vulkan_info_.instance_extensions.data()[previous_extension_count]); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkEnumerateInstanceExtensionProperties(" << layer_name |
| << ") failed: " << result; |
| return false; |
| } |
| } |
| |
| for (const VkExtensionProperties& ext_property : |
| vulkan_info_.instance_extensions) { |
| if (strcmp(ext_property.extensionName, |
| VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { |
| debug_report_enabled_ = true; |
| vulkan_info_.enabled_instance_extensions.push_back( |
| VK_EXT_DEBUG_REPORT_EXTENSION_NAME); |
| } |
| } |
| |
| uint32_t num_instance_layers = 0; |
| result = vkEnumerateInstanceLayerProperties(&num_instance_layers, nullptr); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkEnumerateInstanceLayerProperties(NULL) failed: " |
| << result; |
| return false; |
| } |
| |
| vulkan_info_.instance_layers.resize(num_instance_layers); |
| result = vkEnumerateInstanceLayerProperties( |
| &num_instance_layers, vulkan_info_.instance_layers.data()); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkEnumerateInstanceLayerProperties() failed: " << result; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool VulkanInstance::CollectDeviceInfo(VkPhysicalDevice physical_device) { |
| std::vector<VkPhysicalDevice> physical_devices; |
| if (physical_device == VK_NULL_HANDLE) { |
| uint32_t count = 0; |
| VkResult result = vkEnumeratePhysicalDevices(vk_instance_, &count, nullptr); |
| if (result != VK_SUCCESS) { |
| DLOG(ERROR) << "vkEnumeratePhysicalDevices failed: " << result; |
| return false; |
| } |
| |
| if (!count) { |
| DLOG(ERROR) << "vkEnumeratePhysicalDevices returns zero device."; |
| return false; |
| } |
| |
| physical_devices.resize(count); |
| result = vkEnumeratePhysicalDevices(vk_instance_, &count, |
| physical_devices.data()); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkEnumeratePhysicalDevices() failed: " << result; |
| return false; |
| } |
| } else { |
| physical_devices.push_back(physical_device); |
| } |
| |
| vulkan_info_.physical_devices.reserve(physical_devices.size()); |
| for (VkPhysicalDevice device : physical_devices) { |
| vulkan_info_.physical_devices.emplace_back(); |
| auto& info = vulkan_info_.physical_devices.back(); |
| info.device = device; |
| |
| vkGetPhysicalDeviceProperties(device, &info.properties); |
| |
| uint32_t count = 0; |
| VkResult result = vkEnumerateDeviceExtensionProperties( |
| device, nullptr /* pLayerName */, &count, nullptr); |
| DLOG_IF(ERROR, result != VK_SUCCESS) |
| << "vkEnumerateDeviceExtensionProperties failed: " << result; |
| |
| info.extensions.resize(count); |
| result = vkEnumerateDeviceExtensionProperties( |
| device, nullptr /* pLayerName */, &count, info.extensions.data()); |
| DLOG_IF(ERROR, result != VK_SUCCESS) |
| << "vkEnumerateDeviceExtensionProperties failed: " << result; |
| |
| // The API version of the VkInstance might be different than the supported |
| // API version of the VkPhysicalDevice, so we need to check the GPU's |
| // API version instead of just testing to see if |
| // vkGetPhysicalDeviceProperties2 and vkGetPhysicalDeviceFeatures2 are |
| // non-null. |
| static_assert(kVulkanRequiredApiVersion >= VK_API_VERSION_1_1, ""); |
| if (info.properties.apiVersion >= kVulkanRequiredApiVersion) { |
| info.driver_properties = VkPhysicalDeviceDriverProperties{ |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, |
| }; |
| |
| VkPhysicalDeviceProperties2 properties2 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, |
| .pNext = &info.driver_properties, |
| }; |
| vkGetPhysicalDeviceProperties2(device, &properties2); |
| |
| VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcr_conversion_features = |
| {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES}; |
| VkPhysicalDeviceProtectedMemoryFeatures protected_memory_feature = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES}; |
| VkPhysicalDeviceFeatures2 features_2 = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; |
| features_2.pNext = &ycbcr_conversion_features; |
| ycbcr_conversion_features.pNext = &protected_memory_feature; |
| |
| vkGetPhysicalDeviceFeatures2(device, &features_2); |
| info.features = features_2.features; |
| info.feature_sampler_ycbcr_conversion = |
| ycbcr_conversion_features.samplerYcbcrConversion; |
| info.feature_protected_memory = protected_memory_feature.protectedMemory; |
| } |
| |
| count = 0; |
| vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr); |
| if (count) { |
| info.queue_families.resize(count); |
| vkGetPhysicalDeviceQueueFamilyProperties(device, &count, |
| info.queue_families.data()); |
| } |
| } |
| return true; |
| } |
| |
| void VulkanInstance::Destroy() { |
| #if DCHECK_IS_ON() |
| if (debug_report_enabled_ && (error_callback_ != VK_NULL_HANDLE || |
| warning_callback_ != VK_NULL_HANDLE)) { |
| if (error_callback_ != VK_NULL_HANDLE) { |
| vkDestroyDebugReportCallbackEXT(vk_instance_, error_callback_, nullptr); |
| error_callback_ = VK_NULL_HANDLE; |
| } |
| if (warning_callback_ != VK_NULL_HANDLE) { |
| vkDestroyDebugReportCallbackEXT(vk_instance_, warning_callback_, nullptr); |
| warning_callback_ = VK_NULL_HANDLE; |
| } |
| } |
| #endif |
| if (owned_vk_instance_ != VK_NULL_HANDLE) { |
| vkDestroyInstance(owned_vk_instance_, nullptr); |
| owned_vk_instance_ = VK_NULL_HANDLE; |
| } |
| vk_instance_ = VK_NULL_HANDLE; |
| |
| if (loader_library_) { |
| base::UnloadNativeLibrary(loader_library_); |
| loader_library_ = nullptr; |
| } |
| } |
| |
| } // namespace gpu |