MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKDevice.cpp
Go to the documentation of this file.
1#include "VKDevice.hpp"
4
5#include "set"
6
7#if defined(WIN32_BACKEND)
8#include <windows.h>
9
10#include <vulkan/vulkan_win32.h>
11#endif
12
13#if defined(WAYLAND_BACKEND)
14#include <vulkan/vulkan_wayland.h>
15#include <wayland-client.h>
16#endif
17
18namespace MayaFlux::Core {
19
20namespace {
21 /**
22 * @brief Scratch native connection plus surfaceless presentation query.
23 *
24 * Device selection runs before any window exists, so
25 * vkGetPhysicalDeviceSurfaceSupportKHR is unavailable: it needs a surface,
26 * which needs a window. The platform entry points answer the same question
27 * from a native display connection instead.
28 *
29 * On Wayland no compositor connection exists at selection time either, since
30 * each WaylandWindow calls wl_display_connect in its own constructor. One
31 * scratch connection is opened for the selection pass and closed after; the
32 * query does not care which connection it is given.
33 *
34 * When the platform has no entry point, or the connection fails, available is
35 * false and supports() returns true for every family so selection proceeds
36 * unverified rather than rejecting every device.
37 */
38 struct PresentationProbe {
39 vk::Instance instance;
41 bool available {};
42 const char* mechanism { "unavailable" };
43
44 explicit PresentationProbe(vk::Instance inst)
45 : instance(inst)
46 {
47#if defined(WIN32_BACKEND)
48 available = true;
49 mechanism = "win32";
50#elif defined(WAYLAND_BACKEND)
51 native_display = wl_display_connect(nullptr);
52 available = native_display != nullptr;
53 mechanism = available ? "wayland" : "unavailable";
54#endif
55 }
56
57 ~PresentationProbe()
58 {
59#if defined(WAYLAND_BACKEND)
60 if (native_display)
61 wl_display_disconnect(static_cast<wl_display*>(native_display));
62#endif
63 }
64
65 PresentationProbe(const PresentationProbe&) = delete;
66 PresentationProbe& operator=(const PresentationProbe&) = delete;
67 PresentationProbe(PresentationProbe&&) = delete;
68 PresentationProbe& operator=(PresentationProbe&&) = delete;
69
70 [[nodiscard]] bool supports([[maybe_unused]] vk::PhysicalDevice device, [[maybe_unused]] uint32_t family_index) const
71 {
72 if (!available)
73 return true;
74
75#if defined(WIN32_BACKEND)
76 return vkGetPhysicalDeviceWin32PresentationSupportKHR(
77 static_cast<VkPhysicalDevice>(device), family_index)
78 == VK_TRUE;
79#elif defined(WAYLAND_BACKEND)
80 return vkGetPhysicalDeviceWaylandPresentationSupportKHR(
81 static_cast<VkPhysicalDevice>(device), family_index,
82 static_cast<wl_display*>(native_display))
83 == VK_TRUE;
84#else
85 return true;
86#endif
87 }
88 };
89
90 /** @brief Format a Vulkan UUID as 32 lowercase hex characters, no separators. */
91 std::string uuid_to_hex(const uint8_t* uuid)
92 {
93 std::string out;
94 out.reserve(VK_UUID_SIZE * 2);
95 for (size_t i = 0; i < VK_UUID_SIZE; ++i) {
96 out += std::format("{:02x}", uuid[i]);
97 }
98 return out;
99 }
100
101 /** @brief Case-insensitive substring test. */
102 bool contains_nocase(std::string_view haystack, std::string_view needle)
103 {
104 if (needle.empty() || needle.size() > haystack.size())
105 return false;
106
107 auto it = std::search(haystack.begin(), haystack.end(),
108 needle.begin(), needle.end(),
109 [](char a, char b) { return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)); });
110
111 return it != haystack.end();
112 }
113
114 /**
115 * @brief Everything selection needs to know about one enumerated device.
116 *
117 * Gathered in a single pass so that scoring, selector matching, and the
118 * candidate log all read the same values.
119 */
120 struct DeviceCandidate {
121 vk::PhysicalDevice device;
122 uint32_t index {};
123 std::string name;
124 std::string uuid_hex;
125 std::array<uint8_t, VK_UUID_SIZE> uuid {};
126 vk::PhysicalDeviceType type {};
127 uint32_t api_version {};
128 vk::DriverId driver_id {};
129 std::string driver_name;
130 QueueFamilyIndices families;
136 uint32_t pci_bus {};
138 int64_t score {};
139 const char* reject_reason {};
140
141 [[nodiscard]] bool viable() const { return reject_reason == nullptr; }
142 };
143
144 /** @brief Base score by device class, before preference and capability bonuses. */
145 int64_t type_base_score(vk::PhysicalDeviceType type)
146 {
147 switch (type) {
148 case vk::PhysicalDeviceType::eDiscreteGpu:
149 return 4000;
150 case vk::PhysicalDeviceType::eIntegratedGpu:
151 return 2000;
152 case vk::PhysicalDeviceType::eVirtualGpu:
153 return 1000;
154 case vk::PhysicalDeviceType::eCpu:
155 return 100;
156 default:
157 return 500;
158 }
159 }
160
161 /**
162 * @brief Read MAYAFLUX_GPU, an all-digits value being an index and anything
163 * else a name substring.
164 * @param out_index Receives the parsed index, or -1.
165 * @param out_name Receives the name substring, or empty.
166 * @return true if the variable was set and non-empty.
167 */
168 bool read_gpu_env(int32_t& out_index, std::string& out_name)
169 {
170 const char* raw = std::getenv("MAYAFLUX_GPU");
171 if (!raw || *raw == '\0')
172 return false;
173
174 std::string_view value(raw);
175 if (std::ranges::all_of(value, [](char c) { return std::isdigit(static_cast<unsigned char>(c)) != 0; })) {
176 out_index = static_cast<int32_t>(std::strtol(raw, nullptr, 10));
177 out_name.clear();
178 } else {
179 out_index = -1;
180 out_name = value;
181 }
182 return true;
183 }
184
185} // namespace
186
188{
189 cleanup();
190}
191
193 : m_physical_device(other.m_physical_device)
194 , m_logical_device(other.m_logical_device)
195 , m_graphics_queue(other.m_graphics_queue)
196 , m_compute_queue(other.m_compute_queue)
197 , m_transfer_queue(other.m_transfer_queue)
198 , m_queue_families(other.m_queue_families)
199 , m_graphics_presents(other.m_graphics_presents)
200 , m_present_queues(std::move(other.m_present_queues))
201 , m_device_name(std::move(other.m_device_name))
202 , m_device_uuid(other.m_device_uuid)
203 , m_supports_mesh_shaders(other.m_supports_mesh_shaders)
204{
205 other.m_physical_device = VK_NULL_HANDLE;
206 other.m_logical_device = VK_NULL_HANDLE;
207 other.m_graphics_queue = VK_NULL_HANDLE;
208 other.m_compute_queue = VK_NULL_HANDLE;
209 other.m_transfer_queue = VK_NULL_HANDLE;
210}
211
213{
214 if (this != &other) {
215 cleanup();
216 m_physical_device = other.m_physical_device;
217 m_logical_device = other.m_logical_device;
218 m_graphics_queue = other.m_graphics_queue;
219 m_compute_queue = other.m_compute_queue;
220 m_transfer_queue = other.m_transfer_queue;
221 m_queue_families = other.m_queue_families;
222 m_graphics_presents = other.m_graphics_presents;
223 m_present_queues = std::move(other.m_present_queues);
224 m_device_name = std::move(other.m_device_name);
225 m_device_uuid = other.m_device_uuid;
226 m_supports_mesh_shaders = other.m_supports_mesh_shaders;
227
228 other.m_physical_device = VK_NULL_HANDLE;
229 other.m_logical_device = VK_NULL_HANDLE;
230 other.m_graphics_queue = VK_NULL_HANDLE;
231 other.m_compute_queue = VK_NULL_HANDLE;
232 other.m_transfer_queue = VK_NULL_HANDLE;
233 }
234 return *this;
235}
236
237bool VKDevice::initialize(vk::Instance instance, const GraphicsBackendInfo& backend_info)
238{
239 if (!pick_physical_device(instance, backend_info)) {
240 return false;
241 }
242
243 return create_logical_device(instance, backend_info);
244}
245
247{
248 if (m_logical_device) {
249 m_logical_device.destroy();
250 m_logical_device = nullptr;
251 MF_INFO(Journal::Component::Core, Journal::Context::GraphicsBackend, "Vulkan logical device destroyed.");
252 }
253 m_physical_device = nullptr;
254 m_graphics_queue = nullptr;
255 m_compute_queue = nullptr;
256 m_transfer_queue = nullptr;
257 m_queue_families = {};
258 m_present_queues.clear();
259 m_device_name.clear();
260 m_device_uuid = {};
261 m_graphics_presents = false;
263}
264
265bool VKDevice::pick_physical_device(vk::Instance instance, const GraphicsBackendInfo& backend_info)
266{
267 auto devices = instance.enumeratePhysicalDevices();
268
269 if (devices.empty()) {
271 std::source_location::current(),
272 "Failed to find GPUs with Vulkan support!");
273 }
274
275 const PresentationProbe probe(instance);
276
277 if (backend_info.require_presentation && !probe.available) {
279 "Presentation support cannot be verified on this platform; "
280 "device selection will not filter on it");
281 }
282
283 std::vector<DeviceCandidate> candidates;
284 candidates.reserve(devices.size());
285
286 for (uint32_t i = 0; i < devices.size(); ++i) {
287 const auto& device = devices[i];
288
289 DeviceCandidate cand;
290 cand.device = device;
291 cand.index = i;
292
293 auto available_extensions = device.enumerateDeviceExtensionProperties();
294 bool has_pci_ext = false;
295
296 for (const auto& ext : available_extensions) {
297 if (strcmp(ext.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
298 cand.has_swapchain = true;
299 if (strcmp(ext.extensionName, VK_EXT_MESH_SHADER_EXTENSION_NAME) == 0)
300 cand.has_mesh_shader = true;
301 if (strcmp(ext.extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME) == 0)
302 has_pci_ext = true;
303 }
304
305 auto prop_chain = vk::StructureChain {
306 vk::PhysicalDeviceProperties2 {},
307 vk::PhysicalDeviceVulkan11Properties {},
308 vk::PhysicalDeviceVulkan12Properties {},
309 vk::PhysicalDevicePCIBusInfoPropertiesEXT {}
310 };
311
312 if (!has_pci_ext) {
313 prop_chain.unlink<vk::PhysicalDevicePCIBusInfoPropertiesEXT>();
314 }
315
316 device.getProperties2(&prop_chain.get<vk::PhysicalDeviceProperties2>());
317
318 const auto& props = prop_chain.get<vk::PhysicalDeviceProperties2>().properties;
319 const auto& props11 = prop_chain.get<vk::PhysicalDeviceVulkan11Properties>();
320 const auto& props12 = prop_chain.get<vk::PhysicalDeviceVulkan12Properties>();
321
322 cand.name = props.deviceName.data();
323 cand.type = props.deviceType;
324 cand.api_version = props.apiVersion;
325 cand.driver_id = props12.driverID;
326 cand.driver_name = props12.driverName.data();
327 std::copy_n(props11.deviceUUID.data(), VK_UUID_SIZE, cand.uuid.begin());
328 cand.uuid_hex = uuid_to_hex(cand.uuid.data());
329
330 if (has_pci_ext) {
331 cand.has_pci_info = true;
332 cand.pci_bus = prop_chain.get<vk::PhysicalDevicePCIBusInfoPropertiesEXT>().pciBus;
333 }
334
335 auto memory_props = device.getMemoryProperties();
336 for (uint32_t h = 0; h < memory_props.memoryHeapCount; ++h) {
337 if (memory_props.memoryHeaps[h].flags & vk::MemoryHeapFlagBits::eDeviceLocal) {
338 cand.device_local_bytes = std::max(cand.device_local_bytes,
339 static_cast<uint64_t>(memory_props.memoryHeaps[h].size));
340 }
341 }
342
343 cand.families = find_queue_families(device);
344
345 if (cand.has_mesh_shader) {
346 vk::PhysicalDeviceMeshShaderFeaturesEXT mesh_features;
347 vk::PhysicalDeviceFeatures2 features;
348 features.pNext = &mesh_features;
349 device.getFeatures2(&features);
350
351 cand.has_mesh_shader = mesh_features.meshShader == VK_TRUE
352 && mesh_features.taskShader == VK_TRUE;
353 }
354
355 {
356 auto family_props = device.getQueueFamilyProperties();
357 const uint32_t probe_count = std::min<uint32_t>(static_cast<uint32_t>(family_props.size()), QueueFamilyIndices::MAX_TRACKED_FAMILIES);
358
359 for (uint32_t f = 0; f < probe_count; ++f) {
360 if (probe.supports(device, f))
361 cand.present_family_mask |= (1U << f);
362 }
363
364 cand.families.present_family_mask = cand.present_family_mask;
365
366 if (cand.families.graphics_family.has_value())
367 cand.graphics_presents = cand.families.can_present(cand.families.graphics_family.value());
368 }
369
370 if (!cand.families.graphics_family.has_value()) {
371 cand.reject_reason = "no graphics queue family";
372 } else if (cand.api_version < VK_API_VERSION_1_3) {
373 cand.reject_reason = "device API version below 1.3";
374 } else if (!cand.has_swapchain) {
375 cand.reject_reason = "no VK_KHR_swapchain";
376 } else if (backend_info.require_presentation && !cand.graphics_presents) {
377 cand.reject_reason = "graphics family cannot present";
378 }
379
380 cand.score = type_base_score(cand.type);
381
382 switch (backend_info.device_preference) {
384 if (cand.type == vk::PhysicalDeviceType::eDiscreteGpu)
385 cand.score += 10000;
386 break;
388 if (cand.type == vk::PhysicalDeviceType::eIntegratedGpu)
389 cand.score += 10000;
390 break;
392 if (cand.type == vk::PhysicalDeviceType::eVirtualGpu)
393 cand.score += 10000;
394 break;
396 if (cand.type == vk::PhysicalDeviceType::eDiscreteGpu) {
397 cand.score += 10000;
398 if (cand.has_pci_info)
399 cand.score += static_cast<int64_t>(cand.pci_bus) * 4;
400 }
401 break;
403 default:
404 break;
405 }
406
407 if (cand.graphics_presents)
408 cand.score += 500;
409 if (cand.has_mesh_shader)
410 cand.score += 100;
411
412 cand.score += static_cast<int64_t>(cand.device_local_bytes >> 30U);
413
414 candidates.push_back(std::move(cand));
415 }
416
417 for (const auto& cand : candidates) {
419 "GPU [{}] {} | type={} driver={} ({}) api={}.{}.{} | uuid={} | "
420 "gfx={} compute={} transfer={} | present=gfx:{} mask:{:#x} mesh={} vram={}MB pci_bus={} | score={}{}{}",
421 cand.index, cand.name, vk::to_string(cand.type),
422 vk::to_string(cand.driver_id), cand.driver_name,
423 VK_API_VERSION_MAJOR(cand.api_version),
424 VK_API_VERSION_MINOR(cand.api_version),
425 VK_API_VERSION_PATCH(cand.api_version),
426 cand.uuid_hex,
427 cand.families.graphics_family.has_value() ? std::to_string(cand.families.graphics_family.value()) : "none",
428 cand.families.compute_family.has_value() ? std::to_string(cand.families.compute_family.value()) : "none",
429 cand.families.transfer_family.has_value() ? std::to_string(cand.families.transfer_family.value()) : "none",
430 cand.graphics_presents ? "yes" : "no",
431 cand.present_family_mask,
432 cand.has_mesh_shader ? "yes" : "no",
433 cand.device_local_bytes >> 20U,
434 cand.has_pci_info ? std::to_string(cand.pci_bus) : "n/a",
435 cand.score,
436 cand.viable() ? "" : " | REJECTED: ",
437 cand.reject_reason ? cand.reject_reason : "");
438 }
439
441 "Presentation probe mechanism: {}, require_presentation={}, device_preference={}",
442 probe.mechanism, backend_info.require_presentation ? "true" : "false",
444
445 int32_t want_index = backend_info.device_index;
446 std::string want_name = backend_info.device_name;
447 std::string want_uuid = backend_info.device_uuid;
448
449 if (int32_t env_index = -1; read_gpu_env(env_index, want_name)) {
450 want_index = env_index;
451 want_uuid.clear();
453 "MAYAFLUX_GPU override active (index={}, name='{}')", want_index, want_name);
454 }
455
456 const DeviceCandidate* selected = nullptr;
457 const char* selection_basis = "score";
458
459 if (want_index >= 0) {
460 for (const auto& cand : candidates) {
461 if (static_cast<int32_t>(cand.index) == want_index && cand.viable()) {
462 selected = &cand;
463 selection_basis = "device_index";
464 break;
465 }
466 }
467 }
468
469 if (!selected && !want_uuid.empty()) {
470 for (const auto& cand : candidates) {
471 if (cand.uuid_hex == want_uuid && cand.viable()) {
472 selected = &cand;
473 selection_basis = "device_uuid";
474 break;
475 }
476 }
477 }
478
479 if (!selected && !want_name.empty()) {
480 for (const auto& cand : candidates) {
481 if (!cand.viable() || !contains_nocase(cand.name, want_name))
482 continue;
483 if (!selected || cand.score > selected->score) {
484 selected = &cand;
485 selection_basis = "device_name";
486 }
487 }
488 }
489
490 const bool selector_requested = want_index >= 0 || !want_uuid.empty() || !want_name.empty();
491
492 if (selector_requested && !selected) {
493 if (backend_info.strict_device_selection) {
495 std::source_location::current(),
496 "No viable physical device matched the requested selector "
497 "(index={}, uuid='{}', name='{}') and strict_device_selection is set",
498 want_index, want_uuid, want_name);
499 }
500
502 "No viable physical device matched the requested selector "
503 "(index={}, uuid='{}', name='{}'); falling back to score",
504 want_index, want_uuid, want_name);
505 }
506
507 if (!selected) {
508 for (const auto& cand : candidates) {
509 if (!cand.viable())
510 continue;
511 if (!selected || cand.score > selected->score)
512 selected = &cand;
513 }
514 }
515
516 if (!selected) {
518 std::source_location::current(),
519 "No suitable GPU found among {} enumerated device(s); "
520 "see the candidate list above for rejection reasons",
521 candidates.size());
522 }
523
524 m_physical_device = selected->device;
525 m_queue_families = selected->families;
526 m_supports_mesh_shaders = selected->has_mesh_shader;
527 m_graphics_presents = selected->graphics_presents;
528 m_device_name = selected->name;
529 m_device_uuid = selected->uuid;
530
532 "Selected GPU [{}] {} by {} (uuid={}, score={})",
533 selected->index, selected->name, selection_basis, selected->uuid_hex, selected->score);
534
535 return true;
536}
537
539{
541 auto queue_families = device.getQueueFamilyProperties();
542
543 int i = 0;
544 for (const auto& queue_family : queue_families) {
545 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
547 }
548
549 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eCompute && !(queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
550 indices.compute_family = i;
551 }
552
553 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eTransfer && !(queue_family.queueFlags & vk::QueueFlagBits::eGraphics) && !(queue_family.queueFlags & vk::QueueFlagBits::eCompute)) {
554 indices.transfer_family = i;
555 }
556
557 i++;
558 }
559
560 if (indices.graphics_family.has_value()) {
561 if (!indices.compute_family.has_value()) {
562 indices.compute_family = indices.graphics_family;
563 }
564 if (!indices.transfer_family.has_value()) {
565 indices.transfer_family = indices.graphics_family;
566 }
567 }
568
569 return indices;
570}
571
572bool VKDevice::graphics_family_can_present(vk::SurfaceKHR surface) const
573{
574 if (!surface || !m_queue_families.graphics_family.has_value())
575 return false;
576
577 return m_physical_device.getSurfaceSupportKHR(
578 m_queue_families.graphics_family.value(), surface)
579 == VK_TRUE;
580}
581
582vk::Queue VKDevice::get_present_queue(uint32_t family_index) const
583{
584 auto it = m_present_queues.find(family_index);
585 return it != m_present_queues.end() ? it->second : nullptr;
586}
587
589{
591 return family.has_value() ? get_present_queue(family.value()) : nullptr;
592}
593
595{
596 std::vector<vk::ExtensionProperties> availableExtensions = m_physical_device.enumerateDeviceExtensionProperties();
597
598 MF_LOG(Journal::Component::Core, Journal::Context::GraphicsBackend, "Available physical device extensions:");
599 for (const auto& extension : availableExtensions) {
600 std::cout << "\t- " << extension.extensionName << " (Version: " << extension.specVersion << ")\n";
601 }
603}
604
605bool VKDevice::create_logical_device(vk::Instance /*instance*/, const GraphicsBackendInfo& backend_info)
606{
607 if (!m_queue_families.graphics_family.has_value()) {
609 std::source_location::current(),
610 "No graphics queue family found!");
611 }
612
613 std::set<uint32_t> unique_queue_families;
614 unique_queue_families.insert(m_queue_families.graphics_family.value());
615
616 if (m_queue_families.compute_family.has_value()) {
617 unique_queue_families.insert(m_queue_families.compute_family.value());
618 }
619
621 unique_queue_families.insert(m_queue_families.transfer_family.value());
622 }
623
624 for (uint32_t f = 0; f < QueueFamilyIndices::MAX_TRACKED_FAMILIES; ++f) {
626 unique_queue_families.insert(f);
627 }
628
629 std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
630 float queue_priority = 1.0F;
631
632 for (uint32_t queue_family : unique_queue_families) {
633 vk::DeviceQueueCreateInfo queue_create_info {};
634 queue_create_info.queueFamilyIndex = queue_family;
635 queue_create_info.queueCount = 1;
636 queue_create_info.pQueuePriorities = &queue_priority;
637 queue_create_infos.push_back(queue_create_info);
638 }
639
640 vk::PhysicalDeviceFeatures device_features {};
641 device_features.samplerAnisotropy = backend_info.required_features.sampler_anisotropy;
642 device_features.geometryShader = backend_info.required_features.geometry_shaders;
643 device_features.tessellationShader = backend_info.required_features.tessellation_shaders;
644 device_features.multiViewport = backend_info.required_features.multi_viewport;
645 device_features.fillModeNonSolid = m_physical_device.getFeatures().fillModeNonSolid;
646 device_features.shaderSampledImageArrayDynamicIndexing = m_physical_device.getFeatures().shaderSampledImageArrayDynamicIndexing;
647
648 std::vector<const char*> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
649
650 auto supported_extensions = m_physical_device.enumerateDeviceExtensionProperties();
651
652 auto is_supported = [&supported_extensions](std::string_view name) {
653 return std::ranges::any_of(supported_extensions, [name](const auto& ext) {
654 return name == ext.extensionName.data();
655 });
656 };
657
658#ifdef MAYAFLUX_PLATFORM_MACOS
659 if (is_supported("VK_KHR_portability_subset")) {
660 device_extensions.push_back("VK_KHR_portability_subset");
661 }
662
663 auto feature_chain = vk::StructureChain {
664 vk::PhysicalDeviceFeatures2 {},
665 vk::PhysicalDeviceVulkan13Features {},
666 vk::PhysicalDeviceVulkan12Features {}
667 };
668
669#else
671 device_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
672 }
673
674 auto feature_chain = vk::StructureChain {
675 vk::PhysicalDeviceFeatures2 {},
676 vk::PhysicalDeviceVulkan13Features {},
677 vk::PhysicalDeviceVulkan12Features {},
678 vk::PhysicalDeviceMeshShaderFeaturesEXT {}
679 };
680
682 feature_chain.unlink<vk::PhysicalDeviceMeshShaderFeaturesEXT>();
683 } else {
684 feature_chain.get<vk::PhysicalDeviceMeshShaderFeaturesEXT>().taskShader = VK_TRUE;
685 feature_chain.get<vk::PhysicalDeviceMeshShaderFeaturesEXT>().meshShader = VK_TRUE;
686 }
687#endif
688
689 feature_chain.get<vk::PhysicalDeviceFeatures2>().features = device_features;
690 feature_chain.get<vk::PhysicalDeviceVulkan13Features>().dynamicRendering = VK_TRUE;
691 feature_chain.get<vk::PhysicalDeviceVulkan13Features>().synchronization2 = VK_TRUE;
692 feature_chain.get<vk::PhysicalDeviceVulkan12Features>().bufferDeviceAddress = VK_TRUE;
693
694 std::vector<std::string> missing_required;
695
696 for (const auto& ext : backend_info.required_extensions) {
697 if (is_supported(ext)) {
698 device_extensions.push_back(ext.c_str());
699 } else {
700 missing_required.push_back(ext);
701 }
702 }
703
704 if (!missing_required.empty()) {
705 std::string names;
706 for (const auto& ext : missing_required) {
707 if (!names.empty())
708 names += ", ";
709 names += ext;
710 }
711
713 std::source_location::current(),
714 "Device '{}' does not support required extension(s): {}",
715 m_device_name, names);
716 }
717
718 for (const auto& ext : backend_info.optional_extensions) {
719 if (is_supported(ext)) {
720 device_extensions.push_back(ext.c_str());
721 } else {
723 "Device '{}' does not support optional extension '{}'; skipping",
724 m_device_name, ext);
725 }
726 }
727
728 vk::DeviceCreateInfo create_info {};
729 create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size());
730 create_info.pQueueCreateInfos = queue_create_infos.data();
731 create_info.pNext = &feature_chain.get<vk::PhysicalDeviceFeatures2>();
732 create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
733 create_info.ppEnabledExtensionNames = device_extensions.data();
734
735 try {
736 m_logical_device = m_physical_device.createDevice(create_info);
737 VULKAN_HPP_DEFAULT_DISPATCHER.init(m_logical_device);
738
739 } catch (const std::exception& e) {
741 std::source_location::current(),
742 "Failed to create logical device: {}", e.what());
743 }
744
746
747 if (backend_info.enable_compute_queue && m_queue_families.compute_family.has_value()) {
749 } else {
751 }
752
753 if (backend_info.enable_transfer_queue && m_queue_families.transfer_family.has_value()) {
755 } else {
757 }
758
759 for (uint32_t f = 0; f < QueueFamilyIndices::MAX_TRACKED_FAMILIES; ++f) {
761 m_present_queues[f] = m_logical_device.getQueue(f, 0);
762 }
763
764 return true;
765}
766
767}
#define MF_INFO(comp, ctx,...)
#define MF_LOG(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
std::vector< uint32_t > indices
uint32_t h
Definition InkPress.cpp:29
size_t a
size_t b
std::vector< float > * out
glm::vec3 value
bool has_mesh_shader
Definition VKDevice.cpp:132
bool graphics_presents
Definition VKDevice.cpp:134
vk::PhysicalDevice device
Definition VKDevice.cpp:121
bool has_swapchain
Definition VKDevice.cpp:131
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
vk::DriverId driver_id
Definition VKDevice.cpp:128
const char * reject_reason
Definition VKDevice.cpp:139
uint32_t api_version
Definition VKDevice.cpp:127
std::array< uint8_t, VK_UUID_SIZE > uuid
Definition VKDevice.cpp:125
vk::Instance instance
Definition VKDevice.cpp:39
uint64_t device_local_bytes
Definition VKDevice.cpp:137
QueueFamilyIndices families
Definition VKDevice.cpp:130
bool has_pci_info
Definition VKDevice.cpp:135
uint32_t pci_bus
Definition VKDevice.cpp:136
bool available
Definition VKDevice.cpp:41
std::string name
Definition VKDevice.cpp:123
std::string uuid_hex
Definition VKDevice.cpp:124
uint32_t index
Definition VKDevice.cpp:122
int64_t score
Definition VKDevice.cpp:138
std::string driver_name
Definition VKDevice.cpp:129
const char * mechanism
Definition VKDevice.cpp:42
uint32_t present_family_mask
Definition VKDevice.cpp:133
void * native_display
Definition VKDevice.cpp:40
static QueueFamilyIndices find_queue_families(vk::PhysicalDevice device)
Find queue families on the given physical device.
Definition VKDevice.cpp:538
void query_supported_extensions()
Query and log supported device extensions.
Definition VKDevice.cpp:594
std::array< uint8_t, VK_UUID_SIZE > m_device_uuid
Selected device UUID.
Definition VKDevice.hpp:208
bool graphics_family_can_present(vk::SurfaceKHR surface) const
Confirm the graphics family presents to a concrete surface.
Definition VKDevice.cpp:572
void cleanup()
Cleanup device resources.
Definition VKDevice.cpp:246
bool m_supports_mesh_shaders
Whether the device supports mesh shaders.
Definition VKDevice.hpp:209
bool pick_physical_device(vk::Instance instance, const GraphicsBackendInfo &backend_info)
Select a physical device by config selector or score.
Definition VKDevice.cpp:265
VKDevice & operator=(const VKDevice &)=delete
bool initialize(vk::Instance instance, const GraphicsBackendInfo &backend_info)
Select a physical device and create the logical device.
Definition VKDevice.cpp:237
vk::PhysicalDevice m_physical_device
Selected physical device (GPU)
Definition VKDevice.hpp:172
vk::Queue get_preferred_present_queue() const
Queue for the preferred presentation family.
Definition VKDevice.cpp:588
vk::Queue m_compute_queue
Compute queue handle.
Definition VKDevice.hpp:176
bool create_logical_device(vk::Instance instance, const GraphicsBackendInfo &backend_info)
Create the logical device and retrieve queue handles.
Definition VKDevice.cpp:605
vk::Queue m_transfer_queue
Transfer queue handle.
Definition VKDevice.hpp:177
vk::Device m_logical_device
Logical device handle.
Definition VKDevice.hpp:173
bool m_graphics_presents
Graphics family passed the surfaceless presentation probe.
Definition VKDevice.hpp:205
std::unordered_map< uint32_t, vk::Queue > m_present_queues
One queue per presentation-capable family.
Definition VKDevice.hpp:206
std::string m_device_name
Selected device name, cached for logging.
Definition VKDevice.hpp:207
vk::Queue get_present_queue(uint32_t family_index) const
Queue for a presentation-capable family.
Definition VKDevice.cpp:582
vk::Queue m_graphics_queue
Graphics queue handle.
Definition VKDevice.hpp:175
QueueFamilyIndices m_queue_families
Indices of required queue families.
Definition VKDevice.hpp:179
Manages Vulkan physical device selection and logical device creation.
Definition VKDevice.hpp:63
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)
enum MayaFlux::Core::GraphicsBackendInfo::DevicePreference device_preference
bool enable_compute_queue
Enable compute queue (separate from graphics)
std::vector< std::string > required_extensions
Backend-specific extensions to request.
bool enable_transfer_queue
Enable transfer queue (separate from graphics)
struct MayaFlux::Core::GraphicsBackendInfo::@0 required_features
Required device features (Vulkan-specific)
std::vector< std::string > optional_extensions
std::string device_name
Case-insensitive substring of the device name; empty disables.
int32_t device_index
Index into enumeration order; negative disables.
bool require_presentation
Require the graphics queue family to support presentation.
bool strict_device_selection
Treat an unmatched selector as fatal rather than falling back to scoring.
std::string device_uuid
Device UUID as 32 lowercase hex chars, no separators; empty disables.
Configuration for graphics API backend (Vulkan/OpenGL/etc.)
static constexpr uint32_t MAX_TRACKED_FAMILIES
Width of present_family_mask.
Definition VKDevice.hpp:18
bool can_present(uint32_t index) const
Whether family index can present.
Definition VKDevice.hpp:33
std::optional< uint32_t > transfer_family
Definition VKDevice.hpp:22
std::optional< uint32_t > graphics_family
Definition VKDevice.hpp:20
std::optional< uint32_t > preferred_present_family() const
Family the engine presents on by default.
Definition VKDevice.hpp:43
std::optional< uint32_t > compute_family
Definition VKDevice.hpp:21
Stores indices of queue families we need.
Definition VKDevice.hpp:17