73 lines
3.1 KiB
C++
73 lines
3.1 KiB
C++
|
|
//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 <cstdlib>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#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<VkPhysicalDevice> devices(count);
|
||
|
|
vkEnumeratePhysicalDevices(Device::instance, &count, devices.data());
|
||
|
|
// The distinct GPUs, by device UUID, in the loader's order.
|
||
|
|
std::vector<std::array<std::uint8_t, VK_UUID_SIZE>> 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<std::uint8_t, VK_UUID_SIZE> 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;
|
||
|
|
}
|