context.cpp 24.2 KB
Newer Older
Lotrado's avatar
Lotrado committed
1
2
#include <SDL.h>
#include <SDL_vulkan.h>
3
4

#include <sf2/sf2.hpp> // has to be first so he sf2_struct define is set
5
6
7
8
9
10
11
12
13
14
15

#include <mirrage/graphic/context.hpp>
#include <mirrage/graphic/device.hpp>
#include <mirrage/graphic/window.hpp>

#include <mirrage/asset/asset_manager.hpp>
#include <mirrage/utils/log.hpp>
#include <mirrage/utils/template_utils.hpp>

#include <gsl/gsl>

16
#include <cstdio>
17
18
#include <iostream>
#include <sstream>
19
#include <unordered_set>
20
21
22


extern "C" {
23
24
VkResult vkCreateDebugUtilsMessengerEXT(VkInstance                                instance,
                                        const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
25
                                        const VkAllocationCallbacks*              pAllocator,
26
                                        VkDebugUtilsMessengerEXT*                 pMessenger)
27
{
28
29
	auto func = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
	        vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"));
30
	if(func != nullptr) {
31
		return func(instance, pCreateInfo, pAllocator, pMessenger);
32
33
	} else {
		return VK_ERROR_EXTENSION_NOT_PRESENT;
34
	}
35
}
36

37
38
void vkDestroyDebugUtilsMessengerEXT(VkInstance                   instance,
                                     VkDebugUtilsMessengerEXT     messenger,
39
40
                                     const VkAllocationCallbacks* pAllocator)
{
41
42
	auto func = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
	        vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"));
43
	if(func != nullptr) {
44
		func(instance, messenger, pAllocator);
45
46
	}
}
47
}
48

49
namespace mirrage::graphic {
50
51
52
53

	using namespace util::unit_literals;

	namespace {
54
55
		void sdl_error_check()
		{
56
			const char* err = SDL_GetError();
57
58
59
			if(*err != '\0') {
				std::string errorStr(err);
				SDL_ClearError();
60
				MIRRAGE_FAIL("SDL: " << errorStr);
61
62
63
			}
		}

64
65
		void add_presnet_extensions(std::vector<const char*>& extensions, SDL_Window* window)
		{
66
67
68
69
70
71
72
73
74
			auto count = static_cast<unsigned int>(0);
			if(!SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr)) {
				MIRRAGE_FAIL("Unable to determine present extensions: " << SDL_GetError());
			}

			auto begin = extensions.size();
			extensions.resize(begin + count);

			if(!SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data() + begin)) {
75
				MIRRAGE_FAIL("Unable to determine present extensions: " << SDL_GetError());
76
			}
77
78
79
		}

		void add_present_extensions(std::vector<const char*>&                          extensions,
80
81
		                            const std::unordered_map<std::string, Window_ptr>& windows)
		{
82

83
			extensions.reserve(extensions.size() + windows.size() * 4);
84

85
			for(auto&& [_, window] : windows) {
86
87
				(void) _;
				add_presnet_extensions(extensions, window->window_handle());
88
89
90
			}
		}

91
92
		void sort_and_unique(std::vector<const char*>& extensions)
		{
93
94
95
			std::sort(extensions.begin(), extensions.end(), [](auto lhs, auto rhs) {
				return std::strcmp(lhs, rhs) < 0;
			});
96
97
98
			auto new_end = std::unique(extensions.begin(), extensions.end(), [](auto lhs, auto rhs) {
				return std::strcmp(lhs, rhs) == 0;
			});
99
100
101
102

			extensions.erase(new_end, extensions.end());
		}

103
		auto check_extensions(const std::vector<const char*>& required,
104
105
		                      const std::vector<const char*>& optional) -> std::vector<const char*>
		{
106
107
108
109
110
111
112
113
			auto extensions = std::vector<const char*>();
			extensions.reserve(required.size() + optional.size());

			auto supported_extensions = vk::enumerateInstanceExtensionProperties();

			auto support_confirmed = std::vector<bool>(required.size());

			for(auto& e : supported_extensions) {
114
				for(auto i = 0u; i < required.size(); i++) {
115
116
117
118
119
120
121
					if(!strcmp(e.extensionName, required[i])) {
						support_confirmed[i] = true;
						extensions.push_back(required[i]);
						break;
					}
				}

122
				for(auto i = 0u; i < optional.size(); i++) {
123
124
125
126
127
128
129
130
					if(!strcmp(e.extensionName, optional[i])) {
						extensions.push_back(optional[i]);
						break;
					}
				}
			}

			bool all_supported = true;
131
			for(auto i = 0u; i < support_confirmed.size(); i++) {
132
				if(!support_confirmed[i]) {
133
					LOG(plog::warning) << "Unsupported extension \"" << required[i] << "\"!";
134
135
136
137
138
					all_supported = false;
				}
			}

			if(!all_supported) {
139
				MIRRAGE_FAIL("At least one required extension is not supported (see log for details)!");
140
141
142
143
144
			}

			return extensions;
		}

145
146
		auto check_layers(const std::vector<const char*>& requested) -> std::vector<const char*>
		{
147
148
149
150
151
152
153
			auto validation_layers = std::vector<const char*>();
			validation_layers.reserve(requested.size());

			auto supported_layers = vk::enumerateInstanceLayerProperties();
			for(auto& l : supported_layers) {
				bool layer_requested = false;
				for(auto& req_layer : requested) {
154
					if(0 == strcmp(l.layerName, req_layer)) {
155
156
157
158
159
160
161
						validation_layers.push_back(req_layer);
						layer_requested = true;
						break;
					}
				}

				if(!layer_requested) {
162
163
164
					LOG(plog::debug) << "Additional validation layer is available, that hasn't been "
					                    "requested: "
					                 << l.layerName;
165
166
167
168
				}
			}

			if(validation_layers.size() != requested.size()) {
169
170
				IF_LOG(plog::error)
				{
171
172
173
174
175
176
177
					auto msg = std::stringstream{};
					msg << "Some requested validation layers are not supported: \n";
					for(auto& l : validation_layers) {
						msg << "  - " << l << "\n";
					}

					LOG(plog::error) << msg.str();
178
179
180
181
182
183
				}
			}

			return validation_layers;
		}

184
185
186
187
		VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
		                                             VkDebugUtilsMessageTypeFlagsEXT        messageType,
		                                             const VkDebugUtilsMessengerCallbackDataEXT* data,
		                                             void*)
188
		{
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
			auto level = [&] {
				if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
					return plog::verbose;
				} else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
					return plog::info;
				} else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
					return plog::warning;
				} else {
					return plog::error;
				}
			}();

			auto type = [&] {
				if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
					return " GENERAL";
				} else if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
					return " SPEC";
				} else if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
					return " PERF";
				} else {
					return "";
				}
			}();

			auto details = std::stringstream();
			if(data->objectCount > 0) {
				// TODO
216
			}
217
218
219
220
			if(data->cmdBufLabelCount > 0) {
				// TODO
			}

221
222
			auto stacktrace = level == plog::error ? "\nAt:\n" + util::print_stacktrace() : "";

223
			LOG(level) << "[VK" << type << "|" << data->pMessageIdName << "] " << data->pMessage
224
			           << details.str() << stacktrace;
225
226
227

			return VK_FALSE;
		}
228
	} // namespace
229
230


231
232
233
234
235
236
	Context::Context(const std::string&    appName,
	                 uint32_t              appVersion,
	                 const std::string&    engineName,
	                 uint32_t              engineVersion,
	                 bool                  debug,
	                 asset::Asset_manager& assets)
237
	  : _debug(debug), _assets(assets), _name(appName)
238
	{
239
240
241
242

		auto maybe_settings = assets.load_maybe<Graphics_settings>("cfg:graphics"_aid);
		if(maybe_settings.is_nothing()) {
			if(!settings(default_settings())) {
243
				MIRRAGE_FAIL("Invalid graphics settings");
244
245
			}
		} else {
246
			_settings = maybe_settings.get_or_throw();
247
248
249
		}

		if(!settings(*_settings)) { //< apply actual size/settings
250
			MIRRAGE_FAIL("Couldn't apply graphics settings");
251
252
253
254
		}

		sdl_error_check();

255
		for(auto&& [title, win] : _settings->windows) {
256
257
258
259
260
261
262
263
264
			_windows.emplace(title,
			                 std::make_unique<Window>(title,
			                                          app_name() + " | " + title,
			                                          win.display,
			                                          win.width,
			                                          win.height,
			                                          win.fullscreen));
		}

265
		auto instanceCreateInfo = vk::InstanceCreateInfo{};
266
		auto appInfo            = vk::ApplicationInfo{
267
                appName.c_str(), appVersion, engineName.c_str(), engineVersion, VK_API_VERSION_1_0};
268

269
		auto required_extensions = std::vector<const char*>{VK_KHR_SURFACE_EXTENSION_NAME};
270
		add_present_extensions(required_extensions, _windows);
271
		auto optional_extensions = std::vector<const char*>{};
272
273

		if(debug) {
274
275
			required_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

Florian Oetke's avatar
Florian Oetke committed
276
			_enabled_layers = check_layers({"VK_LAYER_LUNARG_image",
277
			                                "VK_LAYER_LUNARG_parameter_validation",
278
			                                "VK_LAYER_LUNARG_standard_validation",
Florian Oetke's avatar
Florian Oetke committed
279
280
			                                "VK_LAYER_LUNARG_swapchain",
			                                "VK_LAYER_GOOGLE_unique_objects",
281
282
			                                "VK_LAYER_GOOGLE_threading",
			                                "VK_LAYER_KHRONOS_validation"});
283
284
		}

285
286
		sort_and_unique(required_extensions);
		sort_and_unique(optional_extensions);
287
288
		auto extensions = check_extensions(required_extensions, optional_extensions);

289
290
		IF_LOG(plog::info)
		{
291
292
293
294
295
296
297
298
299
300
301
302
			auto msg = std::stringstream{};
			msg << "Initializing vulkan...\n";
			msg << "Enabled extensions:\n";
			for(auto e : extensions) {
				msg << "  - " << e << "\n";
			}
			msg << "Enabled validation layers:\n";
			for(auto l : _enabled_layers) {
				msg << "  - " << l << "\n";
			}

			LOG(plog::info) << msg.str();
303
304
305
306
307
308
309
310
311
312
		}

		instanceCreateInfo.setPApplicationInfo(&appInfo);
		instanceCreateInfo.setEnabledExtensionCount(gsl::narrow<uint32_t>(extensions.size()));
		instanceCreateInfo.setPpEnabledExtensionNames(extensions.data());
		instanceCreateInfo.setEnabledLayerCount(gsl::narrow<uint32_t>(_enabled_layers.size()));
		instanceCreateInfo.setPpEnabledLayerNames(_enabled_layers.data());
		_instance = vk::createInstanceUnique(instanceCreateInfo);

		if(debug) {
313
314
315
316
317
318
319
320
321
322
			auto create_info = vk::DebugUtilsMessengerCreateInfoEXT{
			        vk::DebugUtilsMessengerCreateFlagsEXT{},
			        vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo
			                | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning
			                | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
			        vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
			                | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation
			                | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
			        &debugCallback};
			_debug_callback = _instance->createDebugUtilsMessengerEXTUnique(create_info);
323

324
#ifndef MIRRAGE_IGNORE_VULKAN_LABELS
325
326
327
328
329
330
331
332
333
334
335
			_vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(
			        vkGetInstanceProcAddr(*_instance, "vkCmdBeginDebugUtilsLabelEXT"));
			_vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(
			        vkGetInstanceProcAddr(*_instance, "vkCmdEndDebugUtilsLabelEXT"));

			if(!_vkCmdBeginDebugUtilsLabelEXT || !_vkCmdEndDebugUtilsLabelEXT) {
				_vkCmdBeginDebugUtilsLabelEXT = nullptr;
				_vkCmdEndDebugUtilsLabelEXT   = nullptr;
				LOG(plog::warning) << "vkCmdBeginDebugUtilsLabelEXT/vkCmdEndDebugUtilsLabelEXT extension "
				                      "function not found.";
			}
336
#endif
337
		}
338

339
		for(auto&& [_, window] : _windows) {
340
341
342
			(void) _;
			window->create_surface(*this);
		}
343
344
345
	}
	Context::~Context() = default;

346
347
	bool Context::settings(Graphics_settings new_settings)
	{
348
		_assets.save<Graphics_settings>("cfg:graphics"_aid, new_settings);
349
		_settings = _assets.load<Graphics_settings>("cfg:graphics"_aid);
350
351
352
353

		return true;
	}

354
355
	auto Context::find_window(std::string name) -> util::maybe<Window&>
	{
356
357
		auto iter = _windows.find(name);
		return iter == _windows.end() ? util::nothing : util::justPtr(&*iter->second);
358
	}
359
360
	auto Context::_find_window_settings(const std::string& name, int width, int height) -> Window_settings
	{
361
362
		auto& cfg = settings();

363
364
		auto win_cfg = std::find_if(
		        std::begin(cfg.windows), std::end(cfg.windows), [&](auto& w) { return w.first == name; });
365

366
		if(win_cfg == std::end(cfg.windows)) { // no config create new
367
368
			auto new_settings = cfg;
			auto win_settings = default_window_settings(0);
369
370
371
			if(width > 0)
				win_settings.width = width;
			if(height > 0)
372
				win_settings.height = height;
373
374
375
376
377
378
379
380
381
382
383
			new_settings.windows[name] = win_settings;

			settings(new_settings);

			return win_settings;
		}

		return win_cfg->second;
	}

	namespace {
384
385
		bool supports_present(vk::PhysicalDevice& gpu, const std::vector<Window*>& can_present_to)
		{
386
			auto supported_extensions = gpu.enumerateDeviceExtensionProperties();
387
388
389
			auto sc_ext = std::find_if(supported_extensions.begin(), supported_extensions.end(), [](auto& e) {
				return std::strcmp(e.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0;
			});
390

391
			if(sc_ext == supported_extensions.end()) {
392
393
394
395
396
397
398
399
400
401
402
403
404
				return false;
			}

			for(auto window : can_present_to) {
				auto formats       = gpu.getSurfaceFormatsKHR(window->surface());
				auto present_modes = gpu.getSurfacePresentModesKHR(window->surface());

				if(formats.empty() || present_modes.empty())
					return false;
			}

			return true;
		}
405

406
		auto find_graphics_queue(vk::PhysicalDevice& gpu, const std::vector<Window*>& can_present_to)
407
408
		        -> util::maybe<std::uint32_t>
		{
409
			auto i = 0u;
410
411

			for(auto& queue_family : gpu.getQueueFamilyProperties()) {
412
413
				auto can_present =
				        can_present_to.empty()
414
415
416
				        || std::all_of(can_present_to.begin(), can_present_to.end(), [&](auto window) {
					           return gpu.getSurfaceSupportKHR(i, window->surface());
				           });
417
418
419
420

				if(queue_family.queueCount > 0 && can_present
				   && queue_family.timestampValidBits
				              >= 32 // only accept queues with >=32bit timer support, for now
421
422
423
424
425
426
427
428
429
430
				   && (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
					return i;
				}

				i++;
			}

			return util::nothing;
		}

431
432
		auto find_transfer_queue(vk::PhysicalDevice& gpu) -> std::uint32_t
		{
433
			auto families = gpu.getQueueFamilyProperties();
434

435
			auto i = 0u;
436

437
438
			// check for transfer-only queue
			for(auto& queue_family : families) {
439
				if(queue_family.queueCount > 0u && (queue_family.queueFlags & vk::QueueFlagBits::eTransfer)
440
				   && !(queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
441
442
443
444
445
					return i;
				}

				i++;
			}
446

447
			i = 0u;
448
			for(auto& queue_family : families) {
449
				if(queue_family.queueCount > 0u
450
				   && ((queue_family.queueFlags & vk::QueueFlagBits::eTransfer)
451
452
				       || (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)
				       || (queue_family.queueFlags & vk::QueueFlagBits::eCompute))) {
453
454
455
456
457
					return i;
				}

				i++;
			}
458

459
			MIRRAGE_FAIL("No queue found, that supports transfer operations!");
460
461
		}

462
463
464
		auto find_surface_format(vk::PhysicalDevice& gpu,
		                         Window&             window,
		                         vk::Format          target_format,
465
466
		                         vk::ColorSpaceKHR   target_space) -> vk::SurfaceFormatKHR
		{
467
468

			auto formats = gpu.getSurfaceFormatsKHR(window.surface());
469
			if(formats.size() == 1 && formats.front().format == vk::Format::eUndefined) {
470
471
472
473
				auto surface_format       = vk::SurfaceFormatKHR{};
				surface_format.format     = target_format;
				surface_format.colorSpace = target_space;
				return surface_format;
474
475
476
			}

			auto opt_found = std::find_if(formats.begin(), formats.end(), [&](auto& f) {
477
				return f.format == target_format && f.colorSpace == target_space;
478
			});
479
			if(opt_found != formats.end()) {
480
481
482
483
				auto surface_format       = vk::SurfaceFormatKHR{};
				surface_format.format     = target_format;
				surface_format.colorSpace = target_space;
				return surface_format;
484
485
			}

486
487
			opt_found = std::find_if(
			        formats.begin(), formats.end(), [&](auto& f) { return f.format == target_format; });
488
			if(opt_found != formats.end()) {
489
490
491
492
				return *opt_found;
			}

			if(!formats.empty()) {
493
494
				LOG(plog::warning) << "Requested format is not supported by the device, fallback to first "
				                      "reported format";
495
496
				return formats.front();
			} else {
497
				MIRRAGE_FAIL("The device doesn't support any surface formats!");
498
499
500
			}
		}

501
502
		auto create_swapchain_create_info(vk::PhysicalDevice          gpu,
		                                  bool                        srgb,
503
504
		                                  const std::vector<Window*>& can_present_to)
		{
505
			auto swapchains = Swapchain_create_infos();
506

507
508
			for(auto window : can_present_to) {
				auto capabilities = gpu.getSurfaceCapabilitiesKHR(window->surface());
509
510


511
512
513
514
515
				auto sc_info = vk::SwapchainCreateInfoKHR{};
				sc_info.setSurface(window->surface());
				sc_info.setClipped(true);
				sc_info.setPreTransform(capabilities.currentTransform);
				sc_info.setImageSharingMode(vk::SharingMode::eExclusive);
516
517
				sc_info.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment
				                      | vk::ImageUsageFlagBits::eTransferDst);
518
				sc_info.setImageArrayLayers(1);
Florian Oetke's avatar
Florian Oetke committed
519
				sc_info.setMinImageCount(std::max(2u, capabilities.minImageCount));
520
				if(capabilities.maxImageCount > 0 && capabilities.maxImageCount < sc_info.minImageCount) {
521
522
					sc_info.setMinImageCount(capabilities.maxImageCount);
				}
523

524
525
526
527
				auto present_modes = gpu.getSurfacePresentModesKHR(window->surface());
				auto mailbox_supported =
				        std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eMailbox)
				        != present_modes.end();
528
529
530
531
532
				if(mailbox_supported) {
					sc_info.setPresentMode(vk::PresentModeKHR::eMailbox);
				} else {
					sc_info.setPresentMode(vk::PresentModeKHR::eFifo);
				}
533

534
				if(capabilities.currentExtent.width == std::numeric_limits<uint32_t>::max()) {
535
536
537
538
539
540
541
542
543
					capabilities.currentExtent.width =
					        std::min(std::max(gsl::narrow<std::uint32_t>(window->width()),
					                          capabilities.minImageExtent.width),
					                 capabilities.maxImageExtent.width);

					capabilities.currentExtent.height =
					        std::min(std::max(gsl::narrow<std::uint32_t>(window->height()),
					                          capabilities.minImageExtent.height),
					                 capabilities.maxImageExtent.height);
544
				}
545

546
				sc_info.setImageExtent(capabilities.currentExtent);
547

548
549
550
551
552
				auto format =
				        find_surface_format(gpu,
				                            *window,
				                            srgb ? vk::Format::eB8G8R8A8Srgb : vk::Format::eB8G8R8A8Unorm,
				                            vk::ColorSpaceKHR::eSrgbNonlinear);
553

554
555
				sc_info.setImageFormat(format.format);
				sc_info.setImageColorSpace(format.colorSpace);
556

557
558
				swapchains.emplace(window->name(), std::make_tuple(window, sc_info));
			}
559

560
561
			return swapchains;
		}
562
	} // namespace
563
564
565
	auto Context::instantiate_device(Device_selector             selector,
	                                 Device_factory              factory,
	                                 const std::vector<Window*>& can_present_to,
566
567
	                                 bool                        srgb) -> Device_ptr
	{
568
569
570
571
572
573
574
		auto top_score = std::numeric_limits<int>::min();
		auto top_gpu   = vk::PhysicalDevice{};

		for(auto& gpu : _instance->enumeratePhysicalDevices()) {
			if(!can_present_to.empty() && !supports_present(gpu, can_present_to)) {
				continue;
			}
575

576
577
578
			auto graphics_queue = find_graphics_queue(gpu, can_present_to);

			auto score = selector(gpu, graphics_queue);
Florian Oetke's avatar
Florian Oetke committed
579
580

			auto gpu_name = std::string(gpu.getProperties().deviceName);
581
			LOG(plog::info) << "Detected GPU: " << gpu_name;
Florian Oetke's avatar
Florian Oetke committed
582
583
584
585
586

			if(!_settings->gpu_preference.empty() && _settings->gpu_preference == gpu_name) {
				score = std::numeric_limits<int>::max();
			}

587
			if(score > top_score) {
588
				top_score = score;
589
				top_gpu   = gpu;
590
591
592
593
			}
		}

		if(!top_gpu) {
594
			MIRRAGE_FAIL("Couldn't find a GPU that supports vulkan and all required features.");
595
596
		}

597
		LOG(plog::info) << "Selected GPU: " << top_gpu.getProperties().deviceName;
Florian Oetke's avatar
Florian Oetke committed
598

599
600
601
602
		auto cfg = vk::DeviceCreateInfo{};
		cfg.setEnabledLayerCount(gsl::narrow<uint32_t>(_enabled_layers.size()));
		cfg.setPpEnabledLayerNames(_enabled_layers.data());

603
		auto extensions           = std::vector<const char*>();
604
		auto supported_extensions = top_gpu.enumerateDeviceExtensionProperties();
605
		auto extension_supported  = [&](const char* e) {
606
            return supported_extensions.end()
607
608
609
                   != std::find_if(supported_extensions.begin(), supported_extensions.end(), [&](auto& se) {
                          return !strcmp(se.extensionName, e);
                      });
610
		};
611

612
		auto dedicated_alloc_supported = false;
613

614
615
616
617
618
619
#ifdef VK_NV_dedicated_allocation
		if(extension_supported(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
			extensions.emplace_back(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME);
			dedicated_alloc_supported = true;
		}
#endif
620

621
622
623
		if(!can_present_to.empty()) {
			extensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
		}
624

625
		cfg.setEnabledExtensionCount(gsl::narrow<std::uint32_t>(extensions.size()));
626
		cfg.setPpEnabledExtensionNames(extensions.data());
627

628
629
630
		auto create_info = factory(top_gpu, find_graphics_queue(top_gpu, can_present_to));

		cfg.setPEnabledFeatures(&create_info.features);
631

632
		// familyId => (count, priorities)
633
634
635
		auto queue_families =
		        std::unordered_map<std::uint32_t, std::tuple<std::uint32_t, std::vector<float>>>();

636
		auto available_families = top_gpu.getQueueFamilyProperties();
637

638
639
640
		auto queue_mapping = Queue_family_mapping();

		auto transfer_queue_tag = "_transfer"_strid;
641
642
		create_info.queue_families.emplace(transfer_queue_tag,
		                                   Queue_create_info{find_transfer_queue(top_gpu), 0.2f});
643

644
645
		auto draw_queue_tag = util::maybe<util::Str_id>(util::nothing);

646
		for(auto& qf : create_info.queue_families) {
647
648
			auto tag      = qf.first;
			auto family   = qf.second.family_id;
649
			auto priority = qf.second.priority;
650

651
			auto& entry = queue_families[family];
652

653
654
655
656
657
658
			if(available_families[family].queueFlags & vk::QueueFlagBits::eGraphics) {
				draw_queue_tag = qf.first;
			} else if(draw_queue_tag.is_nothing()
			          && (available_families[family].queueFlags & vk::QueueFlagBits::eCompute))
				draw_queue_tag = qf.first;

659
660
661
662
663
			if(available_families[family].queueCount > 0) {
				available_families[family].queueCount--;
				std::get<0>(entry) += 1;
				std::get<1>(entry).emplace_back(priority);
			} else {
664
665
				LOG(plog::warning) << "More queues requested than are availbalbe from family " << family
				                   << ". Collapsed with previous queue!";
666
667
			}

668
669
			queue_mapping.emplace(
			        tag, std::make_tuple(family, gsl::narrow<uint32_t>(std::get<1>(entry).size() - 1)));
670
671
672
673
674
		}

		auto used_queues = std::vector<vk::DeviceQueueCreateInfo>{};
		used_queues.reserve(queue_families.size());
		for(auto& qf : queue_families) {
675
			MIRRAGE_INVARIANT(std::get<1>(qf.second).size() == std::get<0>(qf.second), "Size mismatch");
676

677
678
679
680
681
			used_queues.emplace_back(vk::DeviceQueueCreateFlags(),
			                         qf.first,
			                         std::get<0>(qf.second),
			                         std::get<1>(qf.second).data());
		}
682

683
684
		cfg.setQueueCreateInfoCount(gsl::narrow<std::uint32_t>(used_queues.size()));
		cfg.setPQueueCreateInfos(used_queues.data());
685

686
687
		auto swapchains = create_swapchain_create_info(top_gpu, srgb, can_present_to);

688
689
690
691
692
693
694
695
696
		return std::make_unique<Device>(
		        *this,
		        _assets,
		        top_gpu.createDeviceUnique(cfg),
		        top_gpu,
		        transfer_queue_tag,
		        draw_queue_tag.get_or_throw("No draw or compute queue! That doesn't seem right."),
		        std::move(queue_mapping),
		        std::move(swapchains),
Florian Oetke's avatar
Florian Oetke committed
697
698
		        dedicated_alloc_supported,
		        create_info.features);
699
	}
700
701
702
703

	void Context::vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer             commandBuffer,
	                                           const VkDebugUtilsLabelEXT* pLabelInfo)
	{
704
		if(_debug && _vkCmdBeginDebugUtilsLabelEXT)
705
706
707
708
			_vkCmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo);
	}
	void Context::vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer)
	{
709
		if(_debug && _vkCmdEndDebugUtilsLabelEXT)
710
711
712
713
714
715
716
717
718
719
720
721
			_vkCmdEndDebugUtilsLabelEXT(commandBuffer);
	}

	Queue_debug_label::Queue_debug_label(Context& ctx, vk::CommandBuffer cmds, const char* name)
	  : _ctx(&ctx), _cmds(cmds)
	{
		auto label = VkDebugUtilsLabelEXT{
		        VkStructureType::VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, nullptr, name, {0, 0, 0, 0}};
		_ctx->vkCmdBeginDebugUtilsLabelEXT(cmds, &label);
	}
	Queue_debug_label::~Queue_debug_label() { _ctx->vkCmdEndDebugUtilsLabelEXT(_cmds); }

722
} // namespace mirrage::graphic