blob: 0e1c7b504381a9cd41887bf88a41ac8ed742772f [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "iree/hal/vulkan/vulkan_driver.h"
#include <memory>
#include "absl/container/inlined_vector.h"
#include "iree/base/memory.h"
#include "iree/base/status.h"
#include "iree/base/target_platform.h"
#include "iree/base/tracing.h"
#include "iree/hal/device_info.h"
#include "iree/hal/vulkan/extensibility_util.h"
#include "iree/hal/vulkan/status_util.h"
namespace iree {
namespace hal {
namespace vulkan {
namespace {
// Returns a VkApplicationInfo struct populated with the default app info.
// We may allow hosting applications to override this via weak-linkage if it's
// useful, otherwise this is enough to create the application.
VkApplicationInfo GetDefaultApplicationInfo() {
VkApplicationInfo info;
info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
info.pNext = nullptr;
info.pApplicationName = "IREE-ML";
info.applicationVersion = 0;
info.pEngineName = "IREE";
info.engineVersion = 0;
#ifdef IREE_PLATFORM_ANDROID
info.apiVersion = VK_API_VERSION_1_1;
#else
info.apiVersion = VK_API_VERSION_1_2;
#endif
return info;
}
// Populates device information from the given Vulkan physical device handle.
StatusOr<DeviceInfo> PopulateDeviceInfo(VkPhysicalDevice physical_device,
const ref_ptr<DynamicSymbols>& syms) {
VkPhysicalDeviceFeatures physical_device_features;
syms->vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features);
// TODO(benvanik): check and optionally require these features:
// - physical_device_features.robustBufferAccess
// - physical_device_features.shaderInt16
// - physical_device_features.shaderInt64
// - physical_device_features.shaderFloat64
VkPhysicalDeviceProperties physical_device_properties;
syms->vkGetPhysicalDeviceProperties(physical_device,
&physical_device_properties);
// TODO(benvanik): check and optionally require reasonable limits.
// TODO(benvanik): more clever/sanitized device naming.
std::string name = std::string(physical_device_properties.deviceName);
DeviceFeatureBitfield supported_features = DeviceFeature::kNone;
// TODO(benvanik): implement debugging/profiling features.
// TODO(benvanik): use props to determine if we have timing info.
// supported_features |= DeviceFeature::kDebugging;
// supported_features |= DeviceFeature::kCoverage;
// supported_features |= DeviceFeature::kProfiling;
return DeviceInfo("vulkan", std::move(name), supported_features,
reinterpret_cast<DriverDeviceID>(physical_device));
}
} // namespace
// static
StatusOr<ref_ptr<VulkanDriver>> VulkanDriver::Create(
Options options, ref_ptr<DynamicSymbols> syms) {
IREE_TRACE_SCOPE0("VulkanDriver::Create");
// Load and connect to RenderDoc before instance creation.
// Note: RenderDoc assumes that only a single VkDevice is used:
// https://renderdoc.org/docs/behind_scenes/vulkan_support.html#current-support
std::unique_ptr<RenderDocCaptureManager> renderdoc_capture_manager;
if (options.enable_renderdoc) {
renderdoc_capture_manager = std::make_unique<RenderDocCaptureManager>();
IREE_RETURN_IF_ERROR(renderdoc_capture_manager->Connect());
}
// Find the layers and extensions we need (or want) that are also available
// on the instance. This will fail when required ones are not present.
IREE_ASSIGN_OR_RETURN(
auto enabled_layer_names,
MatchAvailableInstanceLayers(options.instance_extensibility, *syms));
IREE_ASSIGN_OR_RETURN(
auto enabled_extension_names,
MatchAvailableInstanceExtensions(options.instance_extensibility, *syms));
auto instance_extensions =
PopulateEnabledInstanceExtensions(enabled_extension_names);
// Create the instance this driver will use for all requests.
VkApplicationInfo app_info = GetDefaultApplicationInfo();
app_info.apiVersion = options.api_version;
VkInstanceCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.pApplicationInfo = &app_info;
create_info.enabledLayerCount =
static_cast<uint32_t>(enabled_layer_names.size());
create_info.ppEnabledLayerNames = enabled_layer_names.data();
create_info.enabledExtensionCount =
static_cast<uint32_t>(enabled_extension_names.size());
create_info.ppEnabledExtensionNames = enabled_extension_names.data();
// If we have the debug_utils extension then we can chain a one-shot messenger
// callback that we can use to log out the instance creation errors. Once we
// have the real instance we can then register a real messenger.
union {
VkDebugUtilsMessengerCreateInfoEXT debug_utils_create_info;
VkDebugReportCallbackCreateInfoEXT debug_report_create_info;
};
if (instance_extensions.debug_utils) {
create_info.pNext = &debug_utils_create_info;
DebugReporter::PopulateStaticCreateInfo(&debug_utils_create_info);
} else if (instance_extensions.debug_report) {
create_info.pNext = &debug_report_create_info;
DebugReporter::PopulateStaticCreateInfo(&debug_report_create_info);
}
// Some ICDs appear to leak in here, out of our control.
// Warning: leak checks remain disabled if an error is returned.
IREE_DISABLE_LEAK_CHECKS();
VkInstance instance = VK_NULL_HANDLE;
VK_RETURN_IF_ERROR(
syms->vkCreateInstance(&create_info, /*pAllocator=*/nullptr, &instance))
<< "Unable to create Vulkan instance";
IREE_ENABLE_LEAK_CHECKS();
// TODO(benvanik): enable validation layers if needed.
// Now that the instance has been created we can fetch all of the instance
// symbols.
IREE_RETURN_IF_ERROR(syms->LoadFromInstance(instance));
// The real debug messenger (not just the static one used above) can now be
// created as we've loaded all the required symbols.
// TODO(benvanik): strip in release builds.
std::unique_ptr<DebugReporter> debug_reporter;
if (instance_extensions.debug_utils) {
IREE_ASSIGN_OR_RETURN(debug_reporter,
DebugReporter::CreateDebugUtilsMessenger(
instance, syms,
/*allocation_callbacks=*/nullptr));
} else if (instance_extensions.debug_report) {
IREE_ASSIGN_OR_RETURN(
debug_reporter, DebugReporter::CreateDebugReportCallback(
instance, syms, /*allocation_callbacks=*/nullptr));
}
return assign_ref(new VulkanDriver(
std::move(syms), instance,
/*owns_instance=*/true, std::move(options.device_options),
options.default_device_index, std::move(debug_reporter),
std::move(renderdoc_capture_manager)));
}
// static
StatusOr<ref_ptr<VulkanDriver>> VulkanDriver::CreateUsingInstance(
Options options, ref_ptr<DynamicSymbols> syms, VkInstance instance) {
IREE_TRACE_SCOPE0("VulkanDriver::CreateUsingInstance");
if (instance == VK_NULL_HANDLE) {
return InvalidArgumentErrorBuilder(IREE_LOC)
<< "VkInstance must not be VK_NULL_HANDLE";
}
// Find the extensions we need (or want) that are also available on the
// instance. This will fail when required ones are not present.
//
// Since the instance is already created, we can't actually enable any
// extensions or query if they are really enabled - we just have to trust
// that the caller already enabled them for us (or we may fail later).
IREE_ASSIGN_OR_RETURN(
auto enabled_extension_names,
MatchAvailableInstanceExtensions(options.instance_extensibility, *syms));
auto instance_extensions =
PopulateEnabledInstanceExtensions(enabled_extension_names);
IREE_RETURN_IF_ERROR(syms->LoadFromInstance(instance));
// TODO(benvanik): strip in release builds.
std::unique_ptr<DebugReporter> debug_reporter;
if (instance_extensions.debug_utils) {
IREE_ASSIGN_OR_RETURN(debug_reporter,
DebugReporter::CreateDebugUtilsMessenger(
instance, syms,
/*allocation_callbacks=*/nullptr));
} else if (instance_extensions.debug_report) {
IREE_ASSIGN_OR_RETURN(
debug_reporter, DebugReporter::CreateDebugReportCallback(
instance, syms, /*allocation_callbacks=*/nullptr));
}
// Note: no RenderDocCaptureManager here since the VkInstance is already
// created externally. Applications using this function must provide their
// own RenderDoc / debugger integration as desired.
return assign_ref(
new VulkanDriver(std::move(syms), instance, /*owns_instance=*/false,
std::move(options.device_options),
options.default_device_index, std::move(debug_reporter),
/*debug_capture_manager=*/nullptr));
}
VulkanDriver::VulkanDriver(
ref_ptr<DynamicSymbols> syms, VkInstance instance, bool owns_instance,
VulkanDevice::Options device_options, int default_device_index,
std::unique_ptr<DebugReporter> debug_reporter,
std::unique_ptr<RenderDocCaptureManager> renderdoc_capture_manager)
: Driver("vulkan"),
syms_(std::move(syms)),
instance_(instance),
owns_instance_(owns_instance),
device_options_(std::move(device_options)),
default_device_index_(default_device_index),
debug_reporter_(std::move(debug_reporter)),
renderdoc_capture_manager_(std::move(renderdoc_capture_manager)) {}
VulkanDriver::~VulkanDriver() {
IREE_TRACE_SCOPE0("VulkanDriver::dtor");
debug_reporter_.reset();
if (owns_instance_) {
syms()->vkDestroyInstance(instance_, /*pAllocator=*/nullptr);
}
}
StatusOr<std::vector<DeviceInfo>> VulkanDriver::EnumerateAvailableDevices() {
IREE_TRACE_SCOPE0("VulkanDriver::EnumerateAvailableDevices");
// Query all available devices (at this moment, note that this may change!).
uint32_t physical_device_count = 0;
VK_RETURN_IF_ERROR(syms()->vkEnumeratePhysicalDevices(
instance_, &physical_device_count, nullptr));
absl::InlinedVector<VkPhysicalDevice, 2> physical_devices(
physical_device_count);
VK_RETURN_IF_ERROR(syms()->vkEnumeratePhysicalDevices(
instance_, &physical_device_count, physical_devices.data()));
// Convert to our HAL structure.
std::vector<DeviceInfo> device_infos;
device_infos.reserve(physical_device_count);
for (auto physical_device : physical_devices) {
// TODO(benvanik): if we fail should we just ignore the device in the list?
IREE_ASSIGN_OR_RETURN(auto device_info,
PopulateDeviceInfo(physical_device, syms()));
device_infos.push_back(std::move(device_info));
}
return device_infos;
}
StatusOr<ref_ptr<Device>> VulkanDriver::CreateDefaultDevice() {
IREE_TRACE_SCOPE0("VulkanDriver::CreateDefaultDevice");
// Query available devices.
IREE_ASSIGN_OR_RETURN(auto available_devices, EnumerateAvailableDevices());
if (default_device_index_ < 0 ||
default_device_index_ >= available_devices.size()) {
return NotFoundErrorBuilder(IREE_LOC)
<< "Device index " << default_device_index_ << " not found "
<< "(of " << available_devices.size() << ")";
}
// Just create the first one we find.
return CreateDevice(available_devices[default_device_index_].device_id());
}
StatusOr<ref_ptr<Device>> VulkanDriver::CreateDevice(DriverDeviceID device_id) {
IREE_TRACE_SCOPE0("VulkanDriver::CreateDevice");
auto physical_device = reinterpret_cast<VkPhysicalDevice>(device_id);
IREE_ASSIGN_OR_RETURN(auto device_info,
PopulateDeviceInfo(physical_device, syms()));
// Attempt to create the device.
// This may fail if the device was enumerated but is in exclusive use,
// disabled by the system, or permission is denied.
IREE_ASSIGN_OR_RETURN(
auto device,
VulkanDevice::Create(add_ref(this), instance(), device_info,
physical_device, device_options_, syms(),
renderdoc_capture_manager_.get()));
IREE_LOG(INFO) << "Created Vulkan Device: " << device->info().name();
return device;
}
StatusOr<ref_ptr<Device>> VulkanDriver::WrapDevice(
VkPhysicalDevice physical_device, VkDevice logical_device,
const QueueSet& compute_queue_set, const QueueSet& transfer_queue_set) {
IREE_TRACE_SCOPE0("VulkanDriver::WrapDevice");
IREE_ASSIGN_OR_RETURN(auto device_info,
PopulateDeviceInfo(physical_device, syms()));
// Attempt to create the device.
// This may fail if the VkDevice does not support all necessary features.
IREE_ASSIGN_OR_RETURN(
auto device,
VulkanDevice::Wrap(add_ref(this), instance(), device_info,
physical_device, logical_device, device_options_,
compute_queue_set, transfer_queue_set, syms()));
return device;
}
} // namespace vulkan
} // namespace hal
} // namespace iree