Files
darling-lol/tools/test-metal-device-info.cpp
T
lucannez64andClaude Opus 5.5 40f7a650d1 Report the host GPU name and VRAM from MTLDevice
-[MTLDevice name] returned "Darling Indium Device" for every GPU. It now
returns the selected Vulkan device's name in macOS Metal style: the
trailing driver note in brackets and the (R)/(TM) marks are removed, and
NVIDIA names are unchanged. The name is never replaced with another
vendor's. recommendedMaxWorkingSetSize is the device-local heap size on
discrete GPUs and all heaps on integrated/CPU devices, with the old 1 GiB
as fallback. Both are computed once per device.

hasUnifiedMemory stays YES by default: synchronizeResource:/
synchronizeTexture: are missing and Private buffers created with data
abort in Indium, so reporting NO on discrete GPUs would send apps down
unimplemented paths. DARLING_METAL_HOST_UNIFIED_MEMORY=1 opts in to the
GPU-type answer. DARLING_METAL_GPU_NAME and DARLING_METAL_VRAM_MB
override the name and working set.

The cleaning logic lives in private-include/Metal/MTLDeviceInfo.h and is
covered by the host check tools/test-metal-device-info.cpp. Metal now
links indium_private for the Vulkan properties, and the runtime build
script adds Indium's private headers before compiling MTLDevice.mm.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-17 20:49:42 +02:00

90 lines
5.0 KiB
C++

// Host check for the GPU name and working-set helpers behind -[MTLDevice name] and
// -[MTLDevice recommendedMaxWorkingSetSize] (private-include/Metal/MTLDeviceInfo.h).
// Build and run after preparing the sources (see docs/TESTING.md):
// c++ -std=c++17 -I build/darling-src/src/external/metal/private-include
// tools/test-metal-device-info.cpp -o build/test-metal-device-info
// build/test-metal-device-info
#include <Metal/MTLDeviceInfo.h>
#include <cstdio>
#include <string>
static int failures = 0;
static void expectName(const char* vulkan, const char* expected) {
std::string actual = MTLDeviceInfo::metalNameFromVulkanName(vulkan);
bool ok = actual == expected;
failures += !ok;
std::printf("%s name \"%s\" -> \"%s\"%s%s%s\n", ok ? "PASS" : "FAIL", vulkan ? vulkan : "(null)",
actual.c_str(), ok ? "" : " (expected \"", ok ? "" : expected, ok ? "" : "\")");
}
static void expectBytes(const char* what, unsigned long long actual, unsigned long long expected) {
bool ok = actual == expected;
failures += !ok;
std::printf("%s %s -> %llu%s\n", ok ? "PASS" : "FAIL", what, actual, ok ? "" : " (mismatch)");
if (!ok) std::printf(" expected %llu\n", expected);
}
int main() {
// Mesa and proprietary Vulkan names -> macOS Metal style.
expectName("AMD Radeon RX 6800 XT (RADV NAVI21)", "AMD Radeon RX 6800 XT");
expectName("AMD Radeon RX 6650 XT (RADV NAVI23)", "AMD Radeon RX 6650 XT");
expectName("AMD Radeon Graphics (RADV RENOIR)", "AMD Radeon Graphics");
expectName("AMD Radeon Pro 5500M (RADV NAVI14)", "AMD Radeon Pro 5500M");
expectName("AMD RADV NAVI21 (ACO)", "AMD RADV NAVI21");
expectName("Intel(R) UHD Graphics 630 (CML GT2)", "Intel UHD Graphics 630");
expectName("Intel(R) Arc(TM) A770 Graphics (DG2)", "Intel Arc A770 Graphics");
expectName("NVIDIA GeForce GTX 680", "NVIDIA GeForce GTX 680");
expectName("NVIDIA GeForce RTX 2070 SUPER", "NVIDIA GeForce RTX 2070 SUPER");
expectName("NVIDIA GeForce RTX 4090", "NVIDIA GeForce RTX 4090");
expectName("llvmpipe (LLVM 22.1.0, 256 bits)", "llvmpipe");
// Whitespace, nesting and edge cases.
expectName(" AMD Radeon\tRX 7900 XTX (RADV NAVI31) ", "AMD Radeon RX 7900 XTX");
expectName("Foo GPU (driver (nested) x)", "Foo GPU");
expectName("Foo (Bar) GPU", "Foo (Bar) GPU"); // not trailing: kept
expectName("(RADV NAVI21)", "(RADV NAVI21)"); // whole name: kept
expectName("Broken (unbalanced", "Broken (unbalanced");
expectName("(R)(TM)", "(R)(TM)"); // cleaned empty -> raw name
expectName(" ", MTLDeviceInfo::kFallbackName); // raw empty -> neutral fallback
expectName("", MTLDeviceInfo::kFallbackName);
expectName(nullptr, MTLDeviceInfo::kFallbackName);
// DARLING_METAL_GPU_NAME override.
{
bool ok = MTLDeviceInfo::resolveName("My GPU", "AMD Radeon RX 6800 XT (RADV NAVI21)") == "My GPU"
&& MTLDeviceInfo::resolveName("", "AMD Radeon RX 6800 XT (RADV NAVI21)") == "AMD Radeon RX 6800 XT"
&& MTLDeviceInfo::resolveName(nullptr, "NVIDIA GeForce RTX 4090") == "NVIDIA GeForce RTX 4090";
failures += !ok;
std::printf("%s name override\n", ok ? "PASS" : "FAIL");
}
// DARLING_METAL_VRAM_MB parsing: positive decimal integer only.
const unsigned long long MiB = 1024ull * 1024ull;
expectBytes("vram \"8192\"", MTLDeviceInfo::parseMegabytes("8192"), 8192 * MiB);
expectBytes("vram \"1\"", MTLDeviceInfo::parseMegabytes("1"), MiB);
expectBytes("vram \"0\"", MTLDeviceInfo::parseMegabytes("0"), 0);
expectBytes("vram \"\"", MTLDeviceInfo::parseMegabytes(""), 0);
expectBytes("vram null", MTLDeviceInfo::parseMegabytes(nullptr), 0);
expectBytes("vram \"-5\"", MTLDeviceInfo::parseMegabytes("-5"), 0);
expectBytes("vram \"8G\"", MTLDeviceInfo::parseMegabytes("8G"), 0);
expectBytes("vram \" 8\"", MTLDeviceInfo::parseMegabytes(" 8"), 0);
expectBytes("vram \"1.5\"", MTLDeviceInfo::parseMegabytes("1.5"), 0);
expectBytes("vram max", MTLDeviceInfo::parseMegabytes("17592186044415"), 17592186044415ull * MiB);
expectBytes("vram overflow", MTLDeviceInfo::parseMegabytes("17592186044416"), 0);
expectBytes("vram huge", MTLDeviceInfo::parseMegabytes("99999999999999999999999"), 0);
// Working set from Vulkan heaps.
const MTLDeviceInfo::Heap discrete[] = {{8192 * MiB, true}, {16384 * MiB, false}, {256 * MiB, true}};
expectBytes("discrete heaps", MTLDeviceInfo::workingSetBytes(discrete, 3, false), 8448 * MiB);
const MTLDeviceInfo::Heap apu[] = {{512 * MiB, true}, {15000 * MiB, false}};
expectBytes("integrated heaps", MTLDeviceInfo::workingSetBytes(apu, 2, true), 15512 * MiB);
const MTLDeviceInfo::Heap hostOnly[] = {{4096 * MiB, false}};
expectBytes("discrete without device-local heap", MTLDeviceInfo::workingSetBytes(hostOnly, 1, false), 0);
expectBytes("no heaps", MTLDeviceInfo::workingSetBytes(nullptr, 0, false), 0);
const MTLDeviceInfo::Heap huge[] = {{UINT64_MAX, true}, {UINT64_MAX, true}};
expectBytes("saturating sum", MTLDeviceInfo::workingSetBytes(huge, 2, false), UINT64_MAX);
std::printf("METAL_DEVICE_INFO failures=%d pass=%d\n", failures, failures == 0);
return failures == 0 ? 0 : 1;
}