device/instance.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file instance.cpp | ||
| 3 | /// @brief Creating the instance, with validation configured through layer settings. | ||
| 4 | #include "ernie/device/instance.h" | ||
| 5 | |||
| 6 | #include "ernie/util/algorithm.h" | ||
| 7 | #include "ernie/util/on_failure.h" | ||
| 8 | |||
| 9 | #include <algorithm> | ||
| 10 | #include <array> | ||
| 11 | #include <string_view> | ||
| 12 | #include <utility> | ||
| 13 | |||
| 14 | namespace ernie { | ||
| 15 | namespace { | ||
| 16 | |||
| 17 | constexpr const char* kValidationLayer = "VK_LAYER_KHRONOS_validation"; | ||
| 18 | |||
| 19 | template <typename Properties, typename Name> | ||
| 20 | 158 | bool Has(const std::vector<Properties>& list, std::string_view name, Name Properties::* field) { | |
| 21 | 158 | return std::ranges::any_of(list, [&](const Properties& p) { return name == p.*field; }); | |
| 22 | } | ||
| 23 | |||
| 24 | 82 | std::vector<VkExtensionProperties> Extensions(const char* layer = nullptr) { | |
| 25 | 82 | std::uint32_t count = 0; | |
| 26 | 82 | vkEnumerateInstanceExtensionProperties(layer, &count, nullptr); | |
| 27 | 82 | std::vector<VkExtensionProperties> extensions(count); | |
| 28 | 82 | vkEnumerateInstanceExtensionProperties(layer, &count, extensions.data()); | |
| 29 | 82 | return extensions; | |
| 30 | ✗ | } | |
| 31 | |||
| 32 | 76 | bool HasValidationLayer() { | |
| 33 | 76 | std::uint32_t count = 0; | |
| 34 | 76 | vkEnumerateInstanceLayerProperties(&count, nullptr); | |
| 35 | 76 | std::vector<VkLayerProperties> layers(count); | |
| 36 | 76 | vkEnumerateInstanceLayerProperties(&count, layers.data()); | |
| 37 | 76 | return Has(layers, kValidationLayer, &VkLayerProperties::layerName); | |
| 38 | 76 | } | |
| 39 | |||
| 40 | } // namespace | ||
| 41 | |||
| 42 | 82 | Instance::Instance(const InstanceOptions& options) { | |
| 43 | 82 | if (volkInitialize() != VK_SUCCESS) { | |
| 44 | ✗ | throw DeviceError(DeviceErrorKind::Unsupported, "No Vulkan loader is installed"); | |
| 45 | } | ||
| 46 | 82 | std::vector<const char*> extensions = | |
| 47 | 82 | Map(options.extensions, [](const std::string& name) { return name.c_str(); }); | |
| 48 | 82 | const std::vector<VkExtensionProperties> available = Extensions(); | |
| 49 | 82 | VkInstanceCreateFlags flags = 0; | |
| 50 | // Drivers layered over Metal (MoltenVK, Mesa on macOS) are listed only with portability | ||
| 51 | // enumeration. | ||
| 52 | 82 | if (Has(available, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, | |
| 53 | &VkExtensionProperties::extensionName)) { | ||
| 54 | 82 | extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); | |
| 55 | 82 | flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; | |
| 56 | } | ||
| 57 | 82 | const bool validation = options.validation && HasValidationLayer(); | |
| 58 | ✗ | const bool settings = | |
| 59 | ✗ | validation && Has(Extensions(kValidationLayer), VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, | |
| 60 | &VkExtensionProperties::extensionName); | ||
| 61 | 82 | if (validation) { | |
| 62 | ✗ | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | |
| 63 | } | ||
| 64 | 82 | if (settings) { | |
| 65 | ✗ | extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); | |
| 66 | } | ||
| 67 | // Synchronization validation catches missing barriers, which ordinary validation does not. | ||
| 68 | 82 | const VkBool32 on = VK_TRUE; | |
| 69 | 82 | const std::array layerSettings{ | |
| 70 | VkLayerSettingEXT{kValidationLayer, "validate_sync", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, | ||
| 71 | &on}, | ||
| 72 | 82 | }; | |
| 73 | 82 | const VkLayerSettingsCreateInfoEXT layerSettingsInfo{ | |
| 74 | .sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, | ||
| 75 | .settingCount = static_cast<std::uint32_t>(layerSettings.size()), | ||
| 76 | 82 | .pSettings = layerSettings.data()}; | |
| 77 | 82 | const VkApplicationInfo application{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
| 78 | .pApplicationName = "ernie", | ||
| 79 | .pEngineName = "ernie", | ||
| 80 | .apiVersion = VK_API_VERSION_1_3}; | ||
| 81 | 82 | const VkInstanceCreateInfo info{.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
| 82 | 82 | .pNext = settings ? &layerSettingsInfo : nullptr, | |
| 83 | .flags = flags, | ||
| 84 | .pApplicationInfo = &application, | ||
| 85 | 82 | .enabledLayerCount = validation ? 1u : 0u, | |
| 86 | 82 | .ppEnabledLayerNames = validation ? &kValidationLayer : nullptr, | |
| 87 | .enabledExtensionCount = | ||
| 88 | 82 | static_cast<std::uint32_t>(extensions.size()), | |
| 89 | 246 | .ppEnabledExtensionNames = extensions.data()}; | |
| 90 | 82 | CheckVk(vkCreateInstance(&info, nullptr, &instance_), "Creating a Vulkan 1.3 instance"); | |
| 91 | 82 | const OnFailure freeInstance([&] { vkDestroyInstance(instance_, nullptr); }); | |
| 92 | // Device functions too, through the loader's dispatch: they then work for every device, where | ||
| 93 | // volkLoadDevice would tie the process to one. | ||
| 94 | 82 | volkLoadInstance(instance_); | |
| 95 | 82 | if (validation) { | |
| 96 | ✗ | const auto load = [&](const char* name) { return vkGetInstanceProcAddr(instance_, name); }; | |
| 97 | ✗ | const auto createMessenger = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>( | |
| 98 | ✗ | load("vkCreateDebugUtilsMessengerEXT")); | |
| 99 | ✗ | destroyMessenger_ = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>( | |
| 100 | ✗ | load("vkDestroyDebugUtilsMessengerEXT")); | |
| 101 | ✗ | setName_ = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>( | |
| 102 | ✗ | load("vkSetDebugUtilsObjectNameEXT")); | |
| 103 | ✗ | beginLabel_ = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>( | |
| 104 | ✗ | load("vkCmdBeginDebugUtilsLabelEXT")); | |
| 105 | ✗ | endLabel_ = | |
| 106 | ✗ | reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(load("vkCmdEndDebugUtilsLabelEXT")); | |
| 107 | ✗ | const VkDebugUtilsMessengerCreateInfoEXT messenger{ | |
| 108 | .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, | ||
| 109 | .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | | ||
| 110 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, | ||
| 111 | .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | | ||
| 112 | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, | ||
| 113 | .pfnUserCallback = &Instance::Report, | ||
| 114 | ✗ | .pUserData = this}; | |
| 115 | ✗ | CheckVk(createMessenger(instance_, &messenger, nullptr, &messenger_), | |
| 116 | "Creating the validation messenger"); | ||
| 117 | } | ||
| 118 | 82 | } | |
| 119 | |||
| 120 | 82 | Instance::~Instance() { | |
| 121 | 82 | if (messenger_ != VK_NULL_HANDLE) { | |
| 122 | ✗ | destroyMessenger_(instance_, messenger_, nullptr); | |
| 123 | } | ||
| 124 | 82 | vkDestroyInstance(instance_, nullptr); | |
| 125 | 82 | } | |
| 126 | |||
| 127 | 689 | void Instance::Name(VkDevice device, VkObjectType type, std::uint64_t handle, | |
| 128 | std::string_view name) const { | ||
| 129 | 689 | if (setName_ == nullptr) { | |
| 130 | 689 | return; | |
| 131 | } | ||
| 132 | ✗ | const std::string text(name); | |
| 133 | ✗ | const VkDebugUtilsObjectNameInfoEXT info{.sType = | |
| 134 | VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, | ||
| 135 | .objectType = type, | ||
| 136 | .objectHandle = handle, | ||
| 137 | ✗ | .pObjectName = text.c_str()}; | |
| 138 | ✗ | setName_(device, &info); | |
| 139 | ✗ | } | |
| 140 | |||
| 141 | 450 | void Instance::BeginLabel(VkCommandBuffer commands, const char* name) const { | |
| 142 | 450 | if (beginLabel_ != nullptr) { | |
| 143 | ✗ | const VkDebugUtilsLabelEXT label{.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, | |
| 144 | ✗ | .pLabelName = name}; | |
| 145 | ✗ | beginLabel_(commands, &label); | |
| 146 | } | ||
| 147 | 450 | } | |
| 148 | |||
| 149 | 450 | void Instance::EndLabel(VkCommandBuffer commands) const { | |
| 150 | 450 | if (endLabel_ != nullptr) { | |
| 151 | ✗ | endLabel_(commands); | |
| 152 | } | ||
| 153 | 450 | } | |
| 154 | |||
| 155 | 82 | std::vector<VkPhysicalDevice> Instance::PhysicalDevices() const { | |
| 156 | 82 | std::uint32_t count = 0; | |
| 157 | 82 | vkEnumeratePhysicalDevices(instance_, &count, nullptr); | |
| 158 | 82 | std::vector<VkPhysicalDevice> devices(count); | |
| 159 | 82 | vkEnumeratePhysicalDevices(instance_, &count, devices.data()); | |
| 160 | 82 | return devices; | |
| 161 | ✗ | } | |
| 162 | |||
| 163 | 67 | std::vector<std::string> Instance::TakeMessages() { | |
| 164 | 67 | const std::lock_guard lock(mutex_); | |
| 165 | 67 | return std::exchange(messages_, {}); | |
| 166 | 67 | } | |
| 167 | |||
| 168 | ✗ | VkBool32 Instance::Report(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, | |
| 169 | const VkDebugUtilsMessengerCallbackDataEXT* data, void* self) { | ||
| 170 | ✗ | auto& instance = *static_cast<Instance*>(self); | |
| 171 | ✗ | const std::lock_guard lock(instance.mutex_); | |
| 172 | ✗ | instance.messages_.emplace_back(data->pMessage); | |
| 173 | ✗ | return VK_FALSE; | |
| 174 | ✗ | } | |
| 175 | |||
| 176 | } // namespace ernie | ||
| 177 |