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)
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;
267 auto devices =
instance.enumeratePhysicalDevices();
269 if (devices.empty()) {
271 std::source_location::current(),
272 "Failed to find GPUs with Vulkan support!");
275 const PresentationProbe probe(
instance);
279 "Presentation support cannot be verified on this platform; "
280 "device selection will not filter on it");
283 std::vector<DeviceCandidate> candidates;
284 candidates.reserve(devices.size());
286 for (uint32_t i = 0; i < devices.size(); ++i) {
287 const auto&
device = devices[i];
289 DeviceCandidate cand;
293 auto available_extensions =
device.enumerateDeviceExtensionProperties();
294 bool has_pci_ext =
false;
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)
305 auto prop_chain = vk::StructureChain {
306 vk::PhysicalDeviceProperties2 {},
307 vk::PhysicalDeviceVulkan11Properties {},
308 vk::PhysicalDeviceVulkan12Properties {},
309 vk::PhysicalDevicePCIBusInfoPropertiesEXT {}
313 prop_chain.unlink<vk::PhysicalDevicePCIBusInfoPropertiesEXT>();
316 device.getProperties2(&prop_chain.get<vk::PhysicalDeviceProperties2>());
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>();
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());
331 cand.has_pci_info =
true;
332 cand.pci_bus = prop_chain.get<vk::PhysicalDevicePCIBusInfoPropertiesEXT>().pciBus;
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));
345 if (cand.has_mesh_shader) {
346 vk::PhysicalDeviceMeshShaderFeaturesEXT mesh_features;
347 vk::PhysicalDeviceFeatures2 features;
348 features.pNext = &mesh_features;
349 device.getFeatures2(&features);
351 cand.has_mesh_shader = mesh_features.meshShader == VK_TRUE
352 && mesh_features.taskShader == VK_TRUE;
356 auto family_props =
device.getQueueFamilyProperties();
359 for (uint32_t f = 0; f < probe_count; ++f) {
360 if (probe.supports(
device, f))
361 cand.present_family_mask |= (1U << f);
364 cand.families.present_family_mask = cand.present_family_mask;
366 if (cand.families.graphics_family.has_value())
367 cand.graphics_presents = cand.families.can_present(cand.families.graphics_family.value());
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";
377 cand.reject_reason =
"graphics family cannot present";
380 cand.score = type_base_score(cand.type);
384 if (cand.type == vk::PhysicalDeviceType::eDiscreteGpu)
388 if (cand.type == vk::PhysicalDeviceType::eIntegratedGpu)
392 if (cand.type == vk::PhysicalDeviceType::eVirtualGpu)
396 if (cand.type == vk::PhysicalDeviceType::eDiscreteGpu) {
398 if (cand.has_pci_info)
399 cand.score +=
static_cast<int64_t
>(cand.pci_bus) * 4;
407 if (cand.graphics_presents)
409 if (cand.has_mesh_shader)
412 cand.score +=
static_cast<int64_t
>(cand.device_local_bytes >> 30U);
414 candidates.push_back(std::move(cand));
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),
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",
436 cand.viable() ?
"" :
" | REJECTED: ",
437 cand.reject_reason ? cand.reject_reason :
"");
441 "Presentation probe mechanism: {}, require_presentation={}, device_preference={}",
449 if (int32_t env_index = -1; read_gpu_env(env_index, want_name)) {
450 want_index = env_index;
453 "MAYAFLUX_GPU override active (index={}, name='{}')", want_index, want_name);
456 const DeviceCandidate* selected =
nullptr;
457 const char* selection_basis =
"score";
459 if (want_index >= 0) {
460 for (
const auto& cand : candidates) {
461 if (
static_cast<int32_t
>(cand.index) == want_index && cand.viable()) {
463 selection_basis =
"device_index";
469 if (!selected && !want_uuid.empty()) {
470 for (
const auto& cand : candidates) {
471 if (cand.uuid_hex == want_uuid && cand.viable()) {
473 selection_basis =
"device_uuid";
479 if (!selected && !want_name.empty()) {
480 for (
const auto& cand : candidates) {
481 if (!cand.viable() || !contains_nocase(cand.name, want_name))
483 if (!selected || cand.score > selected->score) {
485 selection_basis =
"device_name";
490 const bool selector_requested = want_index >= 0 || !want_uuid.empty() || !want_name.empty();
492 if (selector_requested && !selected) {
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);
502 "No viable physical device matched the requested selector "
503 "(index={}, uuid='{}', name='{}'); falling back to score",
504 want_index, want_uuid, want_name);
508 for (
const auto& cand : candidates) {
511 if (!selected || cand.score > selected->score)
518 std::source_location::current(),
519 "No suitable GPU found among {} enumerated device(s); "
520 "see the candidate list above for rejection reasons",
532 "Selected GPU [{}] {} by {} (uuid={}, score={})",
533 selected->index, selected->name, selection_basis, selected->uuid_hex, selected->score);
609 std::source_location::current(),
610 "No graphics queue family found!");
613 std::set<uint32_t> unique_queue_families;
626 unique_queue_families.insert(f);
629 std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
630 float queue_priority = 1.0F;
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);
640 vk::PhysicalDeviceFeatures device_features {};
645 device_features.fillModeNonSolid =
m_physical_device.getFeatures().fillModeNonSolid;
646 device_features.shaderSampledImageArrayDynamicIndexing =
m_physical_device.getFeatures().shaderSampledImageArrayDynamicIndexing;
648 std::vector<const char*> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
650 auto supported_extensions =
m_physical_device.enumerateDeviceExtensionProperties();
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();
658#ifdef MAYAFLUX_PLATFORM_MACOS
659 if (is_supported(
"VK_KHR_portability_subset")) {
660 device_extensions.push_back(
"VK_KHR_portability_subset");
663 auto feature_chain = vk::StructureChain {
664 vk::PhysicalDeviceFeatures2 {},
665 vk::PhysicalDeviceVulkan13Features {},
666 vk::PhysicalDeviceVulkan12Features {}
671 device_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
674 auto feature_chain = vk::StructureChain {
675 vk::PhysicalDeviceFeatures2 {},
676 vk::PhysicalDeviceVulkan13Features {},
677 vk::PhysicalDeviceVulkan12Features {},
678 vk::PhysicalDeviceMeshShaderFeaturesEXT {}
682 feature_chain.unlink<vk::PhysicalDeviceMeshShaderFeaturesEXT>();
684 feature_chain.get<vk::PhysicalDeviceMeshShaderFeaturesEXT>().taskShader = VK_TRUE;
685 feature_chain.get<vk::PhysicalDeviceMeshShaderFeaturesEXT>().meshShader = VK_TRUE;
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;
694 std::vector<std::string> missing_required;
697 if (is_supported(ext)) {
698 device_extensions.push_back(ext.c_str());
700 missing_required.push_back(ext);
704 if (!missing_required.empty()) {
706 for (
const auto& ext : missing_required) {
713 std::source_location::current(),
714 "Device '{}' does not support required extension(s): {}",
719 if (is_supported(ext)) {
720 device_extensions.push_back(ext.c_str());
723 "Device '{}' does not support optional extension '{}'; skipping",
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();
739 }
catch (
const std::exception& e) {
741 std::source_location::current(),
742 "Failed to create logical device: {}", e.what());