//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // CRAFTER_GRAPHICS_DEVICE picks the GPU by its number among the distinct // GPUs in the Vulkan loader's list. Without it the first discrete GPU is // used, as before. A machine with two of the same GPU had no way to render on // the second, which matters when the first is full: a job filling its memory // leaves no room to even create a device there. A GPU listed twice, as a // Flatpak with two driver manifests lists them, counts once. A value that // isn't a number (a PCI address, say) is an error, as one out of range is: it // used to pick the first GPU without a word. // // Needs at least two distinct GPUs; skips with fewer. #include #include #include #include "vulkan/vulkan.h" import Crafter.Graphics; import std; using namespace Crafter; int main() { // In a child, before this process has a Vulkan instance. const pid_t child = fork(); if (child == 0) { setenv("CRAFTER_GRAPHICS_DEVICE", "CA:00.0", 1); try { Device::Initialize(); } catch (const std::runtime_error& error) { _exit(std::string_view(error.what()).find("CRAFTER_GRAPHICS_DEVICE") != std::string_view::npos ? 0 : 2); } _exit(1); } int status = 0; waitpid(child, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { std::println("FAIL CRAFTER_GRAPHICS_DEVICE=CA:00.0 is an error naming the variable (child exit {})", WIFEXITED(status) ? WEXITSTATUS(status) : -1); return EXIT_FAILURE; } std::println("PASS CRAFTER_GRAPHICS_DEVICE=CA:00.0 is an error naming the variable"); setenv("CRAFTER_GRAPHICS_DEVICE", "1", 1); Device::Initialize(); std::uint32_t count = 0; vkEnumeratePhysicalDevices(Device::instance, &count, nullptr); std::vector devices(count); vkEnumeratePhysicalDevices(Device::instance, &count, devices.data()); // The distinct GPUs, by device UUID, in the loader's order. std::vector> distinct; auto uuidOf = [](VkPhysicalDevice device) { VkPhysicalDeviceIDProperties id { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; VkPhysicalDeviceProperties2 properties { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &id }; vkGetPhysicalDeviceProperties2(device, &properties); std::array uuid; std::ranges::copy(id.deviceUUID, uuid.begin()); return uuid; }; for (VkPhysicalDevice device : devices) { if (std::ranges::find(distinct, uuidOf(device)) == distinct.end()) distinct.push_back(uuidOf(device)); } if (distinct.size() < 2) { std::println("SKIP one GPU; nothing to choose between"); return 77; } const bool picked = uuidOf(Device::physDevice) == distinct[1]; std::println("{} CRAFTER_GRAPHICS_DEVICE=1 selects the second distinct GPU ({}, {} listed)", picked ? "PASS" : "FAIL", Device::deviceProperties.deviceName, count); return picked ? EXIT_SUCCESS : EXIT_FAILURE; }