HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
gfx_system_vulkan.hpp
1// Copyright Take Vos 2020-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "gfx_device_vulkan.hpp"
8#include "../macros.hpp"
9#include <vulkan/vulkan.hpp>
10#include <unordered_set>
11
12hi_export_module(hikogui.GUI : gfx_system);
13
14namespace hi::inline v1 {
15
20public:
22 vk::Instance intrinsic;
23
26
29
31 vk::PhysicalDeviceFeatures requiredFeatures;
32
34 vk::PhysicalDeviceLimits requiredLimits;
35
37 vk::ApplicationInfo applicationInfo;
38
46 {
47#if HI_OPERATING_SYSTEM == HI_OS_WINDOWS
48 requiredExtensions = {VK_KHR_WIN32_SURFACE_EXTENSION_NAME};
49#else
50#error "Not Implemented"
51#endif
52
53 applicationInfo = vk::ApplicationInfo(
54 get_application_name().c_str(),
55 VK_MAKE_VERSION(get_application_version().major, get_application_version().minor, get_application_version().patch),
56 get_library_name().c_str(),
57 VK_MAKE_VERSION(get_library_version().major, get_library_version().minor, get_library_version().patch),
58 VK_API_VERSION_1_2);
59
60 // VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2 extension is needed to retrieve unique identifiers for
61 // each GPU in the system, so that we can select the same one on each startup and so that the
62 // user could select a different one.
63 requiredExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
64
65 // VK_KHR_SURFACE extension is needed to draw in a window.
66 requiredExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67
68#ifndef NDEBUG
69 requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
70#endif
71
72 if (!has_foundation_extensions(requiredExtensions)) {
73 throw gui_error("Vulkan instance does not have the required extensions");
74 }
75
76 auto instanceCreateInfo = vk::InstanceCreateInfo(vk::InstanceCreateFlags(), &applicationInfo);
77 instanceCreateInfo.setEnabledExtensionCount(narrow_cast<uint32_t>(requiredExtensions.size()));
78 instanceCreateInfo.setPpEnabledExtensionNames(requiredExtensions.data());
79
80#ifndef NDEBUG
81 requiredFeatures.robustBufferAccess = VK_TRUE;
82#endif
83
84#ifndef NDEBUG
85 hilet requested_layers = std::vector<char const *>{
86 "VK_LAYER_KHRONOS_validation", "VK_LAYER_KHRONOS_synchronization2"
87 //"VK_LAYER_LUNARG_api_dump"
88 };
89
90 requiredLayers = filter_available_layers(requested_layers);
91#endif
92
93 instanceCreateInfo.setEnabledLayerCount(narrow_cast<uint32_t>(requiredLayers.size()));
94 instanceCreateInfo.setPpEnabledLayerNames(requiredLayers.data());
95
96 hi_log_info("Creating Vulkan instance.");
97 intrinsic = vk_create_instance_no_asan(instanceCreateInfo);
98
99#if (VK_HEADER_VERSION == 97)
100 _loader = vk::DispatchLoaderDynamic(intrinsic);
101#else
102 _loader = vk::DispatchLoaderDynamic(intrinsic, vkGetInstanceProcAddr);
103#endif
104
105#ifndef NDEBUG
106 debugUtilsMessager = intrinsic.createDebugUtilsMessengerEXT(
107 {vk::DebugUtilsMessengerCreateFlagsEXT(),
108 vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
109 // vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
110 vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
111 vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
112 vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
113 debug_utils_message_callback,
114 this},
115 nullptr,
116 loader());
117#endif
118 }
119
120 ~gfx_system();
121 gfx_system(const gfx_system&) = delete;
122 gfx_system& operator=(const gfx_system&) = delete;
123 gfx_system(gfx_system&&) = delete;
124 gfx_system& operator=(gfx_system&&) = delete;
125
126 [[nodiscard]] static gfx_system &global();
127
128 void log_memory_usage() const noexcept
129 {
130 for (hilet& device : devices) {
131 device->log_memory_usage();
132 }
133 }
134
135
136 [[nodiscard]] gfx_device *find_best_device_for_surface(vk::SurfaceKHR surface)
137 {
138 enumerate_devices();
139
140 hilet lock = std::scoped_lock(gfx_system_mutex);
141
142 int best_score = -1;
143 gfx_device *best_device = nullptr;
144
145 for (hilet& device : devices) {
146 hilet score = device->score(surface);
147 if (score >= best_score) {
148 best_score = score;
149 best_device = device.get();
150 }
151 }
152
153 if (best_score <= 0) {
154 hi_log_fatal("Could not find a graphics device suitable for presenting this window.");
155 }
156 return best_device;
157 }
158
159 vk::DispatchLoaderDynamic loader() const noexcept
160 {
161 hi_axiom(gfx_system_mutex.recurse_lock_count());
162 return _loader;
163 }
164
165 void destroySurfaceKHR(vk::SurfaceKHR surface)
166 {
167 hi_axiom(gfx_system_mutex.recurse_lock_count());
168 intrinsic.destroySurfaceKHR(surface);
169 }
170
171private:
172 inline static std::unique_ptr<gfx_system> _global = {};
173
176
178 vk::DispatchLoaderDynamic _loader;
179
180 vk::DebugUtilsMessengerEXT debugUtilsMessager;
181
182 void enumerate_devices() noexcept
183 {
184 hilet lock = std::scoped_lock(gfx_system_mutex);
185
186 if (not devices.empty()) {
187 return;
188 }
189
190 for (auto physical_device : intrinsic.enumeratePhysicalDevices()) {
191 devices.push_back(std::make_shared<gfx_device>(physical_device));
192 }
193 }
194
195 [[nodiscard]] static VkBool32 debug_utils_message_callback(
196 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
197 VkDebugUtilsMessageTypeFlagsEXT messageType,
198 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
199 void *pUserData)
200 {
201 auto message = std::string_view(pCallbackData->pMessage);
202
203 if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
204 hi_log_info("Vulkan: {}", message);
205
206 } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
207 hi_log_warning("Vulkan: {}", message);
208
209 } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
210 if (message.starts_with("Failed to open dynamic library")) {
211 // Steelseries mouse driver will inject:
212 // C:\ProgramData\obs-studio-hook\graphics-hook{32,64}.dll
213 // One of them will always fail to load.
214 hi_log_warning("Vulkan: {}", pCallbackData->pMessage);
215
216 } else {
217 hi_log_error("Vulkan: {}", pCallbackData->pMessage);
218 }
219 }
220
221 return VK_FALSE;
222 }
223
224 [[nodiscard]] static bool has_foundation_extensions(const std::vector<const char *>& requiredExtensions)
225 {
226 auto availableExtensions = std::unordered_set<std::string>();
227 for (auto availableExtensionProperties : vk::enumerateInstanceExtensionProperties()) {
228 availableExtensions.insert(std::string(availableExtensionProperties.extensionName.data()));
229 }
230
231 for (auto requiredExtension : requiredExtensions) {
232 if (availableExtensions.count(requiredExtension) == 0) {
233 return false;
234 }
235 }
236 return true;
237 }
238
239 [[nodiscard]] static std::vector<const char *> filter_available_layers(std::vector<const char *> const& requested_layers)
240 {
241 auto available_layers = vk::enumerateInstanceLayerProperties();
242
243 hi_log_info("Available vulkan layers:");
244 auto r = std::vector<const char *>{};
245 for (hilet& available_layer : available_layers) {
246 hilet layer_name = std::string{available_layer.layerName.data()};
247
248 hilet it = std::find(begin(requested_layers), end(requested_layers), layer_name);
249
250 if (it != end(requested_layers)) {
251 // Use the *it, because the lifetime of its `char const *` is still available after the function call.
252 r.push_back(*it);
253 hi_log_info(" * {}", layer_name);
254 } else {
255 hi_log_info(" {}", layer_name);
256 }
257 }
258 return r;
259 }
260
261 [[nodiscard]] __declspec(no_sanitize_address) static vk::Instance vk_create_instance_no_asan(vk::InstanceCreateInfo instance_create_info)
262 {
263 return vk::createInstance(instance_create_info);
264 }
265};
266
267inline vk::Instance vulkan_instance() noexcept
268{
269 return gfx_system::global().intrinsic;
270}
271
272inline vk::DispatchLoaderDynamic vulkan_loader() noexcept
273{
274 return gfx_system::global().loader();
275}
276
277[[nodiscard]] inline gfx_device *find_best_device_for_surface(vk::SurfaceKHR surface)
278{
279 return gfx_system::global().find_best_device_for_surface(surface);
280}
281
282} // namespace hi::inline v1
DOXYGEN BUG.
Definition algorithm.hpp:16
unfair_recursive_mutex gfx_system_mutex
Global mutex for GUI elements, like gfx_system, gfx_device, Windows and Widgets.
Definition gfx_system_globals.hpp:18
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Vulkan gfx_device controller.
Definition gfx_system_vulkan.hpp:19
std::vector< const char * > requiredExtensions
List of extension that where requested when the instance was created.
Definition gfx_system_vulkan.hpp:25
std::vector< const char * > requiredLayers
List of extension that where requested when the instance was created.
Definition gfx_system_vulkan.hpp:28
vk::Instance intrinsic
Vulkan instance.
Definition gfx_system_vulkan.hpp:22
vk::PhysicalDeviceFeatures requiredFeatures
List of required features for each device.
Definition gfx_system_vulkan.hpp:31
gfx_system()
Create an instance of a gfx_device.
Definition gfx_system_vulkan.hpp:45
vk::ApplicationInfo applicationInfo
application info passed when the instance was created.
Definition gfx_system_vulkan.hpp:37
vk::PhysicalDeviceLimits requiredLimits
List of required limits for each device.
Definition gfx_system_vulkan.hpp:34
T begin(T... args)
T data(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
T lock(T... args)
T push_back(T... args)
T size(T... args)