blob: cb206babd198de97ed59f28611fcbb1ff11ab4f0 [file] [log] [blame]
// 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