| // Copyright 2020 The Fuchsia 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 "vulkan_context.h" |
| |
| #include <stddef.h> |
| |
| #include <utility> |
| |
| #include "src/graphics/tests/common/utils.h" |
| |
| #include "vulkan/vulkan.hpp" |
| |
| vk::DebugUtilsMessengerCreateInfoEXT VulkanContext::default_debug_info_s_( |
| {} /* create flags */, vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, |
| vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | |
| vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation, |
| DebugUtilsTestCallback); |
| |
| VulkanContext::ContextWithUserData VulkanContext::default_debug_callback_user_data_s_; |
| |
| VulkanContext::VulkanContext(const vk::InstanceCreateInfo &instance_info, |
| size_t physical_device_index, const vk::DeviceCreateInfo &device_info, |
| const vk::DeviceQueueCreateInfo &queue_info, |
| const vk::QueueFlagBits &queue_flag_bits, |
| const vk::DebugUtilsMessengerCreateInfoEXT &debug_info, |
| ContextWithUserData debug_callback_user_data, |
| vk::Optional<const vk::AllocationCallbacks> allocator, |
| bool validation_layers_enabled, bool validation_errors_ignored) |
| : instance_info_(instance_info), |
| physical_device_index_(physical_device_index), |
| queue_family_index_(kInvalidQueueFamily), |
| queue_info_(queue_info), |
| device_info_(device_info), |
| debug_callback_user_data_(std::move(debug_callback_user_data)), |
| debug_info_(debug_info), |
| queue_flag_bits_(queue_flag_bits), |
| allocator_(allocator), |
| validation_layers_enabled_(validation_layers_enabled), |
| validation_errors_ignored_(validation_errors_ignored) { |
| assert(debug_info_.pUserData == nullptr && |
| "Debug callback user data must be only set in |debug_callback_user_data|."); |
| } |
| |
| VulkanContext::VulkanContext(size_t physical_device_index, const vk::QueueFlagBits &queue_flag_bits, |
| vk::Optional<const vk::AllocationCallbacks> allocator) |
| : physical_device_index_(physical_device_index), |
| queue_family_index_(kInvalidQueueFamily), |
| queue_info_(vk::DeviceQueueCreateFlags(), queue_family_index_, 1 /* queueCount */, |
| &queue_priority_), |
| debug_info_(default_debug_info_s_), |
| queue_flag_bits_(queue_flag_bits), |
| allocator_(allocator) {} |
| |
| bool VulkanContext::InitInstance() { |
| if (instance_initialized_) { |
| RTN_MSG(false, "Instance is already initialized.\n"); |
| } |
| vk::ResultValue<vk::UniqueInstance> rv_instance(vk::Result::eNotReady, vk::UniqueInstance{}); |
| |
| if (validation_layers_enabled_) { |
| // Copy and modify the input lists of layers and extensions to add the validation layer and the |
| // debug utils extension (so we can check for validation errors). |
| std::copy(instance_info_.ppEnabledLayerNames, |
| instance_info_.ppEnabledLayerNames + instance_info_.enabledLayerCount, |
| std::back_inserter(layers_)); |
| layers_.push_back("VK_LAYER_KHRONOS_validation"); |
| instance_info_.ppEnabledLayerNames = layers_.data(); |
| instance_info_.enabledLayerCount = layers_.size(); |
| std::copy(instance_info_.ppEnabledExtensionNames, |
| instance_info_.ppEnabledExtensionNames + instance_info_.enabledExtensionCount, |
| std::back_inserter(extensions_)); |
| extensions_.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| instance_info_.ppEnabledExtensionNames = extensions_.data(); |
| instance_info_.enabledExtensionCount = extensions_.size(); |
| } |
| if (allocator_) { |
| // Verify valid use of callbacks. |
| if (!(allocator_->pfnAllocation && allocator_->pfnReallocation && allocator_->pfnFree)) { |
| RTN_MSG(false, "Required allocator function is missing.\n"); |
| } |
| if (allocator_->pfnInternalAllocation && !allocator_->pfnInternalFree) { |
| RTN_MSG(false, "pfnInternalAllocation defined without pfnInternalFree.\n"); |
| } |
| if (allocator_->pfnInternalFree && !allocator_->pfnInternalAllocation) { |
| RTN_MSG(false, "pfnInternalFree defined without pfnInternalAllocation.\n"); |
| } |
| rv_instance = vk::createInstanceUnique(instance_info_, allocator_); |
| } else { |
| rv_instance = vk::createInstanceUnique(instance_info_); |
| } |
| |
| if (vk::Result::eSuccess != rv_instance.result) { |
| RTN_MSG(false, "VK Error: 0x%x - Create instance.\n", rv_instance.result); |
| } |
| |
| if (rv_instance.value && validation_layers_enabled_) { |
| loader_.init(*rv_instance.value, vkGetInstanceProcAddr); |
| debug_callback_user_data_.context_ = this; |
| debug_info_.pUserData = &debug_callback_user_data_; |
| auto rv_messenger = |
| rv_instance.value->createDebugUtilsMessengerEXTUnique(debug_info_, nullptr, loader_); |
| if (rv_messenger.result != vk::Result::eSuccess) { |
| RTN_MSG(false, "VK Error: 0x%x - CreateDebugUtilsMessengeEXT\n", rv_messenger.result); |
| } |
| messenger_ = std::move(rv_messenger.value); |
| } |
| |
| instance_ = std::move(rv_instance.value); |
| instance_initialized_ = true; |
| return true; |
| } |
| |
| bool VulkanContext::InitQueueFamily() { |
| if (!instance_initialized_) { |
| RTN_MSG(false, "Instance must be initialized before queue family.\n"); |
| } |
| if (queue_family_initialized_) { |
| RTN_MSG(false, "Queue family is already initialized.\n"); |
| } |
| auto [r_physical_devices, physical_devices] = instance_->enumeratePhysicalDevices(); |
| if (vk::Result::eSuccess != r_physical_devices || physical_devices.empty()) { |
| RTN_MSG(false, "VK Error: 0x%x - No physical device found.\n", r_physical_devices); |
| } |
| physical_device_ = physical_devices[physical_device_index_]; |
| |
| const auto queue_families = physical_device_.getQueueFamilyProperties(); |
| queue_family_index_ = queue_families.size(); |
| for (size_t i = 0; i < queue_families.size(); ++i) { |
| if (queue_families[i].queueFlags & queue_flag_bits_) { |
| queue_family_index_ = i; |
| break; |
| } |
| } |
| |
| if (static_cast<size_t>(queue_family_index_) == queue_families.size()) { |
| queue_family_index_ = kInvalidQueueFamily; |
| RTN_MSG(false, "Couldn't find an appropriate queue family.\n"); |
| } |
| queue_info_.queueFamilyIndex = queue_family_index_; |
| queue_family_initialized_ = true; |
| return true; |
| } |
| |
| bool VulkanContext::InitDevice() { |
| if (!queue_family_initialized_) { |
| RTN_MSG(false, "Queue family must be initialized before device.\n"); |
| } |
| if (device_initialized_) { |
| RTN_MSG(false, "Device is already initialized.\n"); |
| } |
| vk::ResultValue<vk::UniqueDevice> rv_device(vk::Result::eNotReady, vk::UniqueDevice{}); |
| if (allocator_) { |
| rv_device = physical_device_.createDeviceUnique(device_info_, allocator_); |
| } else { |
| rv_device = physical_device_.createDeviceUnique(device_info_); |
| } |
| if (vk::Result::eSuccess != rv_device.result) { |
| RTN_MSG(false, "VK Error: 0x%x - Create device.\n", rv_device.result); |
| } |
| device_ = std::move(rv_device.value); |
| queue_ = device_->getQueue(queue_family_index_, 0); |
| device_initialized_ = true; |
| return true; |
| } |
| |
| bool VulkanContext::Init() { |
| if (initialized_ || instance_initialized_ || device_initialized_ || queue_family_initialized_) { |
| RTN_MSG(false, "Attempt to re-initialize VulkanContext.\n"); |
| } |
| if (!InitInstance()) { |
| RTN_MSG(false, "Unable to initialize instance.\n"); |
| } |
| if (!InitQueueFamily()) { |
| RTN_MSG(false, "Unable to initialize queue family.\n"); |
| } |
| if (!InitDevice()) { |
| RTN_MSG(false, "Unable to initialize device.\n"); |
| } |
| initialized_ = true; |
| return true; |
| } |
| |
| bool VulkanContext::set_instance_info(const vk::InstanceCreateInfo &v) { |
| if (instance_initialized_) { |
| RTN_MSG(false, "set_instance_info ignored. Instance is already initialized.\n"); |
| } |
| instance_info_ = v; |
| return true; |
| } |
| |
| bool VulkanContext::set_device_info(const vk::DeviceCreateInfo &v) { |
| if (device_initialized_) { |
| RTN_MSG(false, "set_device_info ignored. Device is already initialized.\n"); |
| } |
| device_info_ = v; |
| return true; |
| } |
| |
| bool VulkanContext::set_queue_info(const vk::DeviceQueueCreateInfo &v) { |
| if (queue_family_initialized_) { |
| RTN_MSG(false, "set_queue_info ignored. Queue family is already initialized.\n"); |
| } |
| queue_info_ = v; |
| return true; |
| } |
| |
| bool VulkanContext::set_queue_flag_bits(const vk::QueueFlagBits &v) { |
| if (queue_family_initialized_) { |
| RTN_MSG(false, "set_queue_flag_bits ignored. Queue family is already initialized.\n"); |
| } |
| queue_flag_bits_ = v; |
| return true; |
| } |
| |
| void VulkanContext::set_debug_utils_messenger( |
| const vk::DebugUtilsMessengerCreateInfoEXT &debug_info, const ContextWithUserData &user_data) { |
| assert(debug_info.pUserData == nullptr && |
| "User data must only be set in |user_data| as it will be overwritten."); |
| debug_info_ = debug_info; |
| debug_callback_user_data_ = user_data; |
| } |
| |
| const vk::UniqueInstance &VulkanContext::instance() const { |
| if (!instance_initialized_) { |
| assert(false && "Instance is not initialized.\n"); |
| } |
| return instance_; |
| } |
| |
| const vk::PhysicalDevice &VulkanContext::physical_device() const { |
| if (!queue_family_initialized_) { |
| assert(false && "Queue family is not initialized.\n"); |
| } |
| return physical_device_; |
| } |
| |
| int VulkanContext::queue_family_index() const { |
| if (!queue_family_initialized_) { |
| assert(false && "Queue family is not initialized.\n"); |
| } |
| return queue_family_index_; |
| } |
| |
| const vk::UniqueDevice &VulkanContext::device() const { |
| if (!device_initialized_) { |
| assert(false && "Device is not initialized.\n"); |
| } |
| return device_; |
| } |
| |
| const vk::Queue &VulkanContext::queue() const { |
| if (!device_initialized_) { |
| assert(false && "Device is not initialized.\n"); |
| } |
| return queue_; |
| } |
| |
| VulkanContext::Builder::Builder() |
| : physical_device_index_(0), |
| queue_priority_(0.0f), |
| queue_info_(vk::DeviceQueueCreateFlags(), physical_device_index_, 1 /* queueCount */, |
| &queue_priority_), |
| device_info_(vk::DeviceCreateFlags(), 1 /* queueCreateInfoCount */, &queue_info_), |
| queue_flag_bits_(vk::QueueFlagBits::eGraphics), |
| allocator_(nullptr), |
| debug_info_(VulkanContext::default_debug_info_s_) {} |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_allocator( |
| vk::Optional<const vk::AllocationCallbacks> v) { |
| allocator_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_instance_info(const vk::InstanceCreateInfo &v) { |
| instance_info_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_physical_device_index(const size_t v) { |
| physical_device_index_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_device_info(const vk::DeviceCreateInfo &v) { |
| device_info_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_queue_info(const vk::DeviceQueueCreateInfo &v) { |
| queue_info_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_queue_flag_bits(const vk::QueueFlagBits &v) { |
| queue_flag_bits_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_validation_layers_enabled(bool v) { |
| validation_layers_enabled_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_validation_errors_ignored(bool v) { |
| validation_errors_ignored_ = v; |
| return *this; |
| } |
| |
| VulkanContext::Builder &VulkanContext::Builder::set_debug_utils_messenger( |
| const vk::DebugUtilsMessengerCreateInfoEXT &debug_info, const ContextWithUserData &user_data) { |
| assert(debug_info.pUserData == nullptr && |
| "User data must only be set in |user_data| as it will be overwritten."); |
| debug_info_ = debug_info; |
| debug_callback_user_data_ = user_data; |
| |
| return *this; |
| } |
| |
| std::unique_ptr<VulkanContext> VulkanContext::Builder::Unique() const { |
| auto context = std::make_unique<VulkanContext>( |
| instance_info_, physical_device_index_, device_info_, queue_info_, queue_flag_bits_, |
| debug_info_, debug_callback_user_data_, allocator_, validation_layers_enabled_, |
| validation_errors_ignored_); |
| if (!context->Init()) { |
| RTN_MSG(nullptr, "Failed to initialize VulkanContext.\n") |
| } |
| return context; |
| } |