Select a physical device by config selector or score.
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;
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
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();
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",
436 cand.viable() ? "" : " | REJECTED: ",
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
530
532 "Selected GPU [{}] {} by {} (uuid={}, score={})",
533 selected->index, selected->name, selection_basis, selected->uuid_hex, selected->score);
534
535 return true;
536}
#define MF_INFO(comp, ctx,...)
#define MF_LOG(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
vk::PhysicalDevice device
const char * reject_reason
uint64_t device_local_bytes
QueueFamilyIndices families
uint32_t present_family_mask
static QueueFamilyIndices find_queue_families(vk::PhysicalDevice device)
Find queue families on the given physical device.
std::array< uint8_t, VK_UUID_SIZE > m_device_uuid
Selected device UUID.
bool m_supports_mesh_shaders
Whether the device supports mesh shaders.
vk::PhysicalDevice m_physical_device
Selected physical device (GPU)
bool m_graphics_presents
Graphics family passed the surfaceless presentation probe.
std::string m_device_name
Selected device name, cached for logging.
QueueFamilyIndices m_queue_families
Indices of required queue families.
constexpr std::string_view to_string(EventType type)
Converts an EventType to its string representation.
@ 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)
static constexpr uint32_t MAX_TRACKED_FAMILIES
Width of present_family_mask.