HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
gfx_system_vulkan_intf.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_intf.hpp"
8#include "../macros.hpp"
9#include <vulkan/vulkan.hpp>
10#include <unordered_set>
11
12hi_export_module(hikogui.GFX : gfx_system_intf);
13
14hi_export namespace 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 auto const 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 (auto const& device : devices) {
131 device->log_memory_usage();
132 }
133 }
134
135
136 [[nodiscard]] gfx_device *find_best_device(vk::SurfaceKHR surface)
137 {
138 enumerate_devices();
139
140 auto const lock = std::scoped_lock(gfx_system_mutex);
141
142 int best_score = -1;
143 gfx_device *best_device = nullptr;
144
145 for (auto const& device : devices) {
146 auto const 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:
174
176 vk::DispatchLoaderDynamic _loader;
177
178 vk::DebugUtilsMessengerEXT debugUtilsMessager;
179
180 void enumerate_devices() noexcept
181 {
182 auto const lock = std::scoped_lock(gfx_system_mutex);
183
184 if (not devices.empty()) {
185 return;
186 }
187
188 for (auto physical_device : intrinsic.enumeratePhysicalDevices()) {
189 devices.push_back(std::make_shared<gfx_device>(physical_device));
190 }
191 }
192
193 [[nodiscard]] static VkBool32 debug_utils_message_callback(
194 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
195 VkDebugUtilsMessageTypeFlagsEXT messageType,
196 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
197 void *pUserData)
198 {
199 auto message = std::string_view(pCallbackData->pMessage);
200
201 if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
202 hi_log_info("Vulkan: {}", message);
203
204 } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
205 hi_log_warning("Vulkan: {}", message);
206
207 } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
208 if (message.starts_with("Failed to open dynamic library")) {
209 // Steelseries mouse driver will inject:
210 // C:\ProgramData\obs-studio-hook\graphics-hook{32,64}.dll
211 // One of them will always fail to load.
212 hi_log_warning("Vulkan: {}", pCallbackData->pMessage);
213
214 } else {
215 hi_log_error("Vulkan: {}", pCallbackData->pMessage);
216 }
217 }
218
219 return VK_FALSE;
220 }
221
222 [[nodiscard]] static bool has_foundation_extensions(const std::vector<const char *>& requiredExtensions)
223 {
224 auto availableExtensions = std::unordered_set<std::string>();
225 for (auto availableExtensionProperties : vk::enumerateInstanceExtensionProperties()) {
226 availableExtensions.insert(std::string(availableExtensionProperties.extensionName.data()));
227 }
228
229 for (auto requiredExtension : requiredExtensions) {
230 if (availableExtensions.count(requiredExtension) == 0) {
231 return false;
232 }
233 }
234 return true;
235 }
236
237 [[nodiscard]] static std::vector<const char *> filter_available_layers(std::vector<const char *> const& requested_layers)
238 {
239 auto available_layers = vk::enumerateInstanceLayerProperties();
240
241 hi_log_info("Available vulkan layers:");
242 auto r = std::vector<const char *>{};
243 for (auto const& available_layer : available_layers) {
244 auto const layer_name = std::string{available_layer.layerName.data()};
245
246 auto const it = std::find(begin(requested_layers), end(requested_layers), layer_name);
247
248 if (it != end(requested_layers)) {
249 // Use the *it, because the lifetime of its `char const *` is still available after the function call.
250 r.push_back(*it);
251 hi_log_info(" * {}", layer_name);
252 } else {
253 hi_log_info(" {}", layer_name);
254 }
255 }
256 return r;
257 }
258
259 [[nodiscard]] hi_no_sanitize_address static vk::Instance vk_create_instance_no_asan(vk::InstanceCreateInfo instance_create_info)
260 {
261 return vk::createInstance(instance_create_info);
262 }
263};
264
265namespace detail {
266inline std::unique_ptr<gfx_system> gfx_system_global = {};
267}
268
269inline vk::Instance vulkan_instance() noexcept
270{
271 return gfx_system::global().intrinsic;
272}
273
274inline vk::DispatchLoaderDynamic vulkan_loader() noexcept
275{
276 return gfx_system::global().loader();
277}
278
285[[nodiscard]] inline gfx_device *find_best_device(vk::SurfaceKHR surface)
286{
287 return gfx_system::global().find_best_device(surface);
288}
289
296[[nodiscard]] gfx_device *find_best_device(gfx_surface const &surface);
297
298} // namespace hi::inline v1
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
gfx_device * find_best_device(gfx_surface const &surface)
Find the best device for a surface.
Definition gfx_surface_vulkan_intf.hpp:191
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
Definition gfx_device_vulkan_intf.hpp:26
Vulkan gfx_device controller.
Definition gfx_system_vulkan_intf.hpp:19
std::vector< const char * > requiredExtensions
List of extension that where requested when the instance was created.
Definition gfx_system_vulkan_intf.hpp:25
std::vector< const char * > requiredLayers
List of extension that where requested when the instance was created.
Definition gfx_system_vulkan_intf.hpp:28
vk::Instance intrinsic
Vulkan instance.
Definition gfx_system_vulkan_intf.hpp:22
vk::PhysicalDeviceFeatures requiredFeatures
List of required features for each device.
Definition gfx_system_vulkan_intf.hpp:31
gfx_system()
Create an instance of a gfx_device.
Definition gfx_system_vulkan_intf.hpp:45
vk::ApplicationInfo applicationInfo
application info passed when the instance was created.
Definition gfx_system_vulkan_intf.hpp:37
vk::PhysicalDeviceLimits requiredLimits
List of required limits for each device.
Definition gfx_system_vulkan_intf.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)