// 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 #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 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& required_extensions, const std::vector& 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_loader_library_path << "': " << error.ToString(); return false; } if (!vulkan_function_pointers ->BindUnassociatedFunctionPointersFromLoaderLib(loader_library_)) { return false; } } return true; } bool VulkanInstance::InitializeInstace( const std::vector& required_extensions, const std::vector& required_layers) { if (is_from_angle_) return InitializeFromANGLE(required_extensions, required_layers); return CreateInstance(required_extensions, required_layers); } bool VulkanInstance::CreateInstance( const std::vector& required_extensions, const std::vector& 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(required_layers.size()), // enableLayerCount required_layers.data(), // ppEnabledLayerNames base::checked_cast( 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& required_extensions, const std::vector& 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& 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 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 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