mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-26 19:47:42 +00:00
Merge pull request #67 from lucannez64/pr61/metal-device-fixes
Metal: device edge-case fixes and a MacBookPro16,1 GPU name for Riot
This commit is contained in:
7 files changed
+156
-29
No files matched your search
@@ -18,4 +18,5 @@ jobs:
|
||||
- run: python -m unittest discover -s tests -p 'test_hidden_riot_recovery.py'
|
||||
- run: python -m unittest discover -s tests -p 'test_desktop_run.py'
|
||||
- run: python -m unittest discover -s tests -p 'test_urlsession_runner.py'
|
||||
- run: python -m unittest discover -s tests -p 'test_metal_device_info.py'
|
||||
- run: python -m unittest discover -s tests -p 'test_exec_guest.py'
|
||||
+1
-1
@@ -23,7 +23,7 @@ c++ -std=c++17 -I build/darling-src/src/external/metal/private-include \
|
||||
build/test-metal-device-info
|
||||
```
|
||||
|
||||
Expected: `METAL_DEVICE_INFO failures=0 pass=1`. It covers how `MTLDevice.name` turns Vulkan names into macOS style (`AMD Radeon RX 6800 XT (RADV NAVI21)` becomes `AMD Radeon RX 6800 XT`, `Intel(R) UHD Graphics 630 (CML GT2)` becomes `Intel UHD Graphics 630`, NVIDIA names stay as they are), the `DARLING_METAL_VRAM_MB` parser, and the working-set sum over Vulkan heaps.
|
||||
Expected: `METAL_DEVICE_INFO failures=0 pass=1`. It covers how `MTLDevice.name` turns Vulkan names into macOS style (`AMD Radeon RX 6800 XT (RADV NAVI21)` becomes `AMD Radeon RX 6800 XT`, `Intel(R) UHD Graphics 630 (CML GT2)` becomes `Intel UHD Graphics 630`, NVIDIA names stay as they are), the `DARLING_METAL_VRAM_MB` parser, the working-set sum over Vulkan heaps, and a non-zero `registryID`. `python3 -m unittest tests.test_metal_device_info` runs the same check against the header in `patches/metal.patch`, so it also works without a prepared source tree; CI runs it.
|
||||
|
||||
The LLVM version check compiles Iridium's actual dynamic loader against small stub libraries. It verifies that a matching major initializes, older/newer majors produce a version mismatch, and a missing library fails initialization. It does not start Darling. The native AIR translator (`python3 tools/build-riot-air-check.py --native`) selects both headers and the shared library from the same `llvm-config`; its build prints those paths so shader results can be attributed to the tested LLVM installation.
|
||||
|
||||
|
||||
@@ -375,6 +375,27 @@ Tested on a GTX 1060 6GB with driver 580.178.04 on NixOS, Hyprland and Xwayland:
|
||||
the client and the tutorial match render on the NVIDIA GPU. Newer drivers were not
|
||||
retested with the option off.
|
||||
|
||||
### Metal device name and memory budget
|
||||
|
||||
`MTLDevice` reports the host GPU: its Vulkan name in macOS style (for example
|
||||
`AMD Radeon RX 6800 XT (RADV NAVI21)` becomes `AMD Radeon RX 6800 XT`) and a working set
|
||||
(`recommendedMaxWorkingSetSize`) from the GPU's Vulkan memory heaps. Apps size their GPU
|
||||
allocations from the working set, so only override it to match the card's real memory.
|
||||
Both can be overridden per launch:
|
||||
|
||||
```sh
|
||||
DARLING_METAL_GPU_NAME='AMD Radeon Pro 5500M' DARLING_METAL_VRAM_MB=4096 ./scripts/run.sh
|
||||
```
|
||||
|
||||
The Riot launcher sets `DARLING_METAL_MAC_GPU_NAME=1`: the name becomes the GPU that
|
||||
the MacBookPro16,1 IOKit reports shipped with, from the host's vendor: `AMD Radeon Pro 5500M`
|
||||
on AMD and `Intel UHD Graphics 630` on Intel. NVIDIA GPUs keep their own name, so the game
|
||||
never takes AMD or Intel driver paths on an NVIDIA card. The working set is still the host's.
|
||||
`DARLING_METAL_MAC_GPU_NAME=0 ./scripts/run.sh` reports the host name instead; other Darling
|
||||
apps report it unless they set the variable. `DARLING_METAL_GPU_NAME` wins over both.
|
||||
|
||||
`registryID` is a fixed non-zero value, as on a real Mac.
|
||||
|
||||
### Host-side checks
|
||||
|
||||
These are host settings, not game settings. Run on AC power, and use a performance power profile (`powerprofilesctl set performance`, or `asusctl profile -P Performance` on ASUS laptops). On the tested desktop the game is limited mostly by its own main thread on the CPU, not by the GPU (see [status](STATUS.md#game-fps-2026-10-1314)), so laptop CPU power limits matter as much as the GPU choice.
|
||||
|
||||
+63
-27
@@ -1134,7 +1134,7 @@ index 2a57796..0cbedf2 100644
|
||||
-
|
||||
@end
|
||||
diff --git a/src/Metal/MTLDevice.mm b/src/Metal/MTLDevice.mm
|
||||
index f58184a..a80a625 100644
|
||||
index f58184a..2c45608 100644
|
||||
--- a/src/Metal/MTLDevice.mm
|
||||
+++ b/src/Metal/MTLDevice.mm
|
||||
@@ -2,14 +2,32 @@
|
||||
@@ -1218,7 +1218,7 @@ index f58184a..a80a625 100644
|
||||
}
|
||||
|
||||
@synthesize device = _device;
|
||||
@@ -116,6 +168,39 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -116,6 +168,50 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
[_threadExitCondition unlock];
|
||||
}
|
||||
|
||||
@@ -1228,16 +1228,27 @@ index f58184a..a80a625 100644
|
||||
+ // Overrides are read once per process.
|
||||
+ static const char* const nameOverride = getenv("DARLING_METAL_GPU_NAME");
|
||||
+ static const uint64_t vramOverride = MTLDeviceInfo::parseMegabytes(getenv("DARLING_METAL_VRAM_MB"));
|
||||
+ static const bool macName = [] {
|
||||
+ const char* value = getenv("DARLING_METAL_MAC_GPU_NAME");
|
||||
+ return value && value[0] == '1' && value[1] == '\0';
|
||||
+ }();
|
||||
+
|
||||
+ std::string vulkanName = _device->name();
|
||||
+ _name = [[NSString alloc] initWithUTF8String: MTLDeviceInfo::resolveName(nameOverride, vulkanName.c_str()).c_str()];
|
||||
+ auto priv = dynamic_cast<Indium::PrivateDevice*>(_device.get());
|
||||
+ std::string vulkanName;
|
||||
+ try {
|
||||
+ vulkanName = _device ? _device->name() : "";
|
||||
+ } catch (...) {
|
||||
+ vulkanName = "";
|
||||
+ }
|
||||
+ const char* mac = macName && priv ? MTLDeviceInfo::macGpuName(priv->properties().vendorID) : nullptr;
|
||||
+ _name = [[NSString alloc] initWithUTF8String: MTLDeviceInfo::resolveName(nameOverride, vulkanName.c_str(), mac).c_str()];
|
||||
+ if (_name == nil) {
|
||||
+ _name = [[NSString alloc] initWithUTF8String: MTLDeviceInfo::kFallbackName];
|
||||
+ }
|
||||
+
|
||||
+ uint64_t workingSet = 0;
|
||||
+ _sharesSystemMemory = NO;
|
||||
+ if (auto priv = dynamic_cast<Indium::PrivateDevice*>(_device.get())) {
|
||||
+ if (priv) {
|
||||
+ const VkPhysicalDeviceType type = priv->properties().deviceType;
|
||||
+ _sharesSystemMemory = type == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU || type == VK_PHYSICAL_DEVICE_TYPE_CPU;
|
||||
+ const VkPhysicalDeviceMemoryProperties memory = priv->memoryProperties();
|
||||
@@ -1258,7 +1269,7 @@ index f58184a..a80a625 100644
|
||||
- (instancetype)initWithDevice: (std::shared_ptr<Indium::Device>)device
|
||||
{
|
||||
self = [super init];
|
||||
@@ -123,6 +208,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -123,6 +219,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
_device = device;
|
||||
_threadExitCondition = [NSCondition new];
|
||||
_threadIsRunning = NO;
|
||||
@@ -1266,7 +1277,7 @@ index f58184a..a80a625 100644
|
||||
_pollingThread = [[NSThread alloc] initWithTarget: self selector: @selector(pollingLoop) object: nil];
|
||||
[_pollingThread start];
|
||||
}
|
||||
@@ -133,6 +219,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -133,6 +230,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
{
|
||||
[_pollingThread release];
|
||||
[_threadExitCondition release];
|
||||
@@ -1274,7 +1285,7 @@ index f58184a..a80a625 100644
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
@@ -153,12 +240,60 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -153,12 +251,60 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
[_threadExitCondition unlock];
|
||||
}
|
||||
|
||||
@@ -1311,7 +1322,7 @@ index f58184a..a80a625 100644
|
||||
+- (BOOL)isRemovable { return NO; }
|
||||
+- (BOOL)isHeadless { return NO; }
|
||||
+- (NSString*)name { return _name; }
|
||||
+- (uint64_t)registryID { return 0; }
|
||||
+- (uint64_t)registryID { return MTLDeviceInfo::kDefaultRegistryID; }
|
||||
+- (BOOL)supportsFamily: (NSUInteger)family { return YES; }
|
||||
+- (BOOL)supportsFeatureSet: (NSUInteger)featureSet { return YES; }
|
||||
+- (BOOL)supportsTextureSampleCount: (NSUInteger)sampleCount { return sampleCount <= 4; }
|
||||
@@ -1336,7 +1347,7 @@ index f58184a..a80a625 100644
|
||||
if (!pso) {
|
||||
if (error) {
|
||||
// TODO: better error and/or match what the official Metal method does
|
||||
@@ -166,6 +301,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -166,6 +312,7 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -1344,7 +1355,7 @@ index f58184a..a80a625 100644
|
||||
return [[MTLComputePipelineStateInternal alloc] initWithState: pso device: self label: descriptor.label];
|
||||
}
|
||||
|
||||
@@ -183,7 +319,13 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -183,7 +330,13 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
reflection: (MTLAutoreleasedComputePipelineReflection*)reflection
|
||||
error: (NSError**)error
|
||||
{
|
||||
@@ -1359,7 +1370,7 @@ index f58184a..a80a625 100644
|
||||
if (!pso) {
|
||||
if (error) {
|
||||
// TODO: better error and/or match what the official Metal method does
|
||||
@@ -191,13 +333,44 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -191,13 +344,44 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -1405,7 +1416,7 @@ index f58184a..a80a625 100644
|
||||
if (!pso) {
|
||||
if (error) {
|
||||
// TODO: better error and/or match what the official Metal method does
|
||||
@@ -205,7 +378,114 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -205,7 +389,118 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -1453,7 +1464,9 @@ index f58184a..a80a625 100644
|
||||
+- (id<MTLDepthStencilState>)newDepthStencilStateWithDescriptor:(MTLDepthStencilDescriptor*)descriptor
|
||||
+{
|
||||
+ if (!descriptor) return nil;
|
||||
+ auto state = _device->newDepthStencilState([descriptor asIndiumDescriptor]);
|
||||
+ std::shared_ptr<Indium::DepthStencilState> state;
|
||||
+ try { state = _device->newDepthStencilState([descriptor asIndiumDescriptor]); }
|
||||
+ catch (...) { return nil; }
|
||||
+ if (!state) return nil;
|
||||
+ return [[MTLDepthStencilStateInternal alloc] initWithState:state device:self label:descriptor.label];
|
||||
+}
|
||||
@@ -1475,7 +1488,9 @@ index f58184a..a80a625 100644
|
||||
+ desc.lodMaxClamp = descriptor.lodMaxClamp;
|
||||
+ desc.supportArgumentBuffers = descriptor.supportArgumentBuffers;
|
||||
+ desc.compareFunction = static_cast<Indium::CompareFunction>(descriptor.compareFunction);
|
||||
+ auto sampler = _device->newSamplerState(desc);
|
||||
+ std::shared_ptr<Indium::SamplerState> sampler;
|
||||
+ try { sampler = _device->newSamplerState(desc); }
|
||||
+ catch (...) { return nil; }
|
||||
+ if (!sampler) return nil;
|
||||
+ return [[MTLSamplerStateInternal alloc] initWithSampler:sampler device:self label:descriptor.label];
|
||||
+}
|
||||
@@ -1521,7 +1536,7 @@ index f58184a..a80a625 100644
|
||||
}
|
||||
|
||||
- (id<MTLCommandQueue>)newCommandQueue
|
||||
@@ -275,8 +555,21 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
@@ -275,8 +570,21 @@ void MTLRemoveDeviceObserver(id<NSObject> observer) {
|
||||
- (id<MTLLibrary>)newLibraryWithData: (dispatch_data_t)data
|
||||
error: (NSError**)error
|
||||
{
|
||||
@@ -2581,7 +2596,7 @@ index 66a9056..f28235c 100644
|
||||
-
|
||||
@end
|
||||
diff --git a/src/Metal/MTLTexture.mm b/src/Metal/MTLTexture.mm
|
||||
index 6c71fb9..822c112 100644
|
||||
index 6c71fb9..186b8c0 100644
|
||||
--- a/src/Metal/MTLTexture.mm
|
||||
+++ b/src/Metal/MTLTexture.mm
|
||||
@@ -1,3 +1,5 @@
|
||||
@@ -2590,7 +2605,7 @@ index 6c71fb9..822c112 100644
|
||||
// SPDX-FileCopyrightText: 2026 Darling Developers
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
@@ -7,11 +9,37 @@
|
||||
@@ -7,11 +9,38 @@
|
||||
#import <Metal/MTLPixelFormatInternal.h>
|
||||
#import <Metal/MTLTypesInternal.h>
|
||||
|
||||
@@ -2606,7 +2621,8 @@ index 6c71fb9..822c112 100644
|
||||
+ if (FILE* log = fopen("/tmp/metal-trace.log", "a")) {
|
||||
+ const unsigned char* b = (const unsigned char*) bytes;
|
||||
+ unsigned long nonZero = 0;
|
||||
+ size_t probe = std::min<size_t>(4096, (size_t) region.size.width * 4);
|
||||
+ // Every pixel format takes at least one byte per pixel, so the first `width` bytes lie inside the first row.
|
||||
+ size_t probe = std::min<size_t>(4096, (size_t) region.size.width);
|
||||
+ for (size_t i = 0; i < probe; ++i) if (b[i]) ++nonZero;
|
||||
+ fprintf(log, "replaceRegion tex=%p %lux%lu fmt=%lu level=%lu region=(%lu,%lu %lux%lu) firstBytesNonZero=%lu/%zu took=%.1fms\n", texture,
|
||||
+ (unsigned long) texture.width, (unsigned long) texture.height, (unsigned long) texture.pixelFormat, (unsigned long) level,
|
||||
@@ -2628,7 +2644,7 @@ index 6c71fb9..822c112 100644
|
||||
@synthesize device = _device;
|
||||
@synthesize resourceOptions = _resourceOptions;
|
||||
@synthesize label = _label;
|
||||
@@ -164,7 +192,9 @@
|
||||
@@ -164,7 +193,9 @@
|
||||
withBytes: (const void*)pixelBytes
|
||||
bytesPerRow: (NSUInteger)bytesPerRow
|
||||
{
|
||||
@@ -2638,7 +2654,7 @@ index 6c71fb9..822c112 100644
|
||||
}
|
||||
|
||||
- (void)replaceRegion: (MTLRegion)region
|
||||
@@ -174,7 +204,9 @@
|
||||
@@ -174,7 +205,9 @@
|
||||
bytesPerRow: (NSUInteger)bytesPerRow
|
||||
bytesPerImage: (NSUInteger)bytesPerImage
|
||||
{
|
||||
@@ -2736,7 +2752,7 @@ index 37cccb3..79b2350 100644
|
||||
-
|
||||
@end
|
||||
diff --git a/src/Metal/MTLVertexDescriptor.mm b/src/Metal/MTLVertexDescriptor.mm
|
||||
index cde968d..aa7fe05 100644
|
||||
index cde968d..0a016c6 100644
|
||||
--- a/src/Metal/MTLVertexDescriptor.mm
|
||||
+++ b/src/Metal/MTLVertexDescriptor.mm
|
||||
@@ -1,24 +1,435 @@
|
||||
@@ -3015,8 +3031,8 @@ index cde968d..aa7fe05 100644
|
||||
+ if (index >= 31) [NSException raise:NSRangeException format:@"Vertex descriptor index %lu exceeds 30", (unsigned long)index];
|
||||
+ MTLVertexBufferLayoutDescriptor* old = _items[index];
|
||||
+ _items[index] = [descriptor retain];
|
||||
+ [old release];
|
||||
+ MTLVertexBufferLayoutArrayRetireItem(self, old);
|
||||
+ [old release];
|
||||
+ const uint32_t bit = 1u << index;
|
||||
+ if (descriptor) _present |= bit; else _present &= ~bit;
|
||||
+ if (descriptor && object_getClass(descriptor) == [MTLVertexBufferLayoutDescriptor class]) _concrete |= bit; else _concrete &= ~bit;
|
||||
@@ -3077,8 +3093,8 @@ index cde968d..aa7fe05 100644
|
||||
+ if (index >= 31) [NSException raise:NSRangeException format:@"Vertex descriptor index %lu exceeds 30", (unsigned long)index];
|
||||
+ MTLVertexAttributeDescriptor* old = _items[index];
|
||||
+ _items[index] = [descriptor retain];
|
||||
+ [old release];
|
||||
+ MTLVertexAttributeArrayRetireItem(self, old);
|
||||
+ [old release];
|
||||
+ const uint32_t bit = 1u << index;
|
||||
+ if (descriptor) _present |= bit; else _present &= ~bit;
|
||||
+ if (descriptor && object_getClass(descriptor) == [MTLVertexAttributeDescriptor class]) _concrete |= bit; else _concrete &= ~bit;
|
||||
@@ -3440,7 +3456,7 @@ diff --git a/private-include/Metal/MTLDepthStencilInternal.h b/private-include/M
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/private-include/Metal/MTLDepthStencilInternal.h
|
||||
@@ -0,0 +1,25 @@
|
||||
@@ -0,0 +1,27 @@
|
||||
+// SPDX-FileCopyrightText: 2026 Darling Developers
|
||||
+// SPDX-License-Identifier: MPL-2.0
|
||||
+
|
||||
@@ -3462,15 +3478,17 @@ new file mode 100644
|
||||
+- (instancetype)initWithState:(std::shared_ptr<Indium::DepthStencilState>)state device:(id<MTLDevice>)device label:(NSString*)label;
|
||||
+#endif
|
||||
+@end
|
||||
+#if DARLING_METAL_ENABLED
|
||||
+// Direct access for the encoders: the property getter is a message send that returns the shared_ptr by value.
|
||||
+const std::shared_ptr<Indium::DepthStencilState>& MTLDepthStencilGetIndium(id object);
|
||||
+#endif
|
||||
+
|
||||
+#endif
|
||||
diff --git a/private-include/Metal/MTLDeviceInfo.h b/private-include/Metal/MTLDeviceInfo.h
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/private-include/Metal/MTLDeviceInfo.h
|
||||
@@ -0,0 +1,148 @@
|
||||
@@ -0,0 +1,166 @@
|
||||
+// SPDX-FileCopyrightText: 2026 Darling Developers
|
||||
+// SPDX-License-Identifier: MPL-2.0
|
||||
+
|
||||
@@ -3489,6 +3507,8 @@ new file mode 100644
|
||||
+
|
||||
+// Used when neither the cleaned nor the raw Vulkan name has any characters.
|
||||
+static constexpr const char* kFallbackName = "Darling Indium Device";
|
||||
+// A real Mac never reports registry ID 0, and apps use it as a key for the device.
|
||||
+static constexpr uint64_t kDefaultRegistryID = 0x100000439ULL;
|
||||
+
|
||||
+static inline bool isSpace(char c) {
|
||||
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
||||
@@ -3568,10 +3588,26 @@ new file mode 100644
|
||||
+}
|
||||
+
|
||||
+// DARLING_METAL_GPU_NAME (used verbatim when non-empty) wins over the Vulkan name.
|
||||
+static inline std::string resolveName(const char* overrideName, const char* vulkanName) {
|
||||
+// With DARLING_METAL_MAC_GPU_NAME=1: the GPU from the host's vendor that the MacBookPro16,1
|
||||
+// iokitd reports shipped with, by Vulkan vendor ID. nullptr for other vendors (NVIDIA), which
|
||||
+// keep their own name so apps never take another vendor's driver paths.
|
||||
+static inline const char* macGpuName(uint32_t vendorID) {
|
||||
+ switch (vendorID) {
|
||||
+ case 0x1002: return "AMD Radeon Pro 5500M";
|
||||
+ case 0x8086: return "Intel UHD Graphics 630";
|
||||
+ default: return nullptr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// DARLING_METAL_GPU_NAME wins, then the Mac GPU name (when enabled and known), then the
|
||||
+// cleaned Vulkan name.
|
||||
+static inline std::string resolveName(const char* overrideName, const char* vulkanName, const char* macName = nullptr) {
|
||||
+ if (overrideName && overrideName[0] != '\0') {
|
||||
+ return overrideName;
|
||||
+ }
|
||||
+ if (macName && macName[0] != '\0') {
|
||||
+ return macName;
|
||||
+ }
|
||||
+ return metalNameFromVulkanName(vulkanName);
|
||||
+}
|
||||
+
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
"""Compile tools/test-metal-device-info.cpp against MTLDeviceInfo.h as shipped in patches/metal.patch."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
HEADER = 'private-include/Metal/MTLDeviceInfo.h'
|
||||
|
||||
|
||||
def header_from_patch():
|
||||
"""MTLDeviceInfo.h is a new file in the patch, so its text is the section's added lines."""
|
||||
patch = (ROOT / 'patches/metal.patch').read_text()
|
||||
section = re.search(r'(?ms)^diff --git a/%s b/%s\n.*?^@@ [^\n]*\n(.*?)(?=^diff --git |\Z)' % (HEADER, HEADER), patch)
|
||||
if not section:
|
||||
raise AssertionError(HEADER + ' is not a new file in patches/metal.patch')
|
||||
lines = section.group(1).splitlines(keepends=True)
|
||||
if not all(line.startswith('+') for line in lines):
|
||||
raise AssertionError(HEADER + ' section has context or removed lines')
|
||||
return ''.join(line[1:] for line in lines)
|
||||
|
||||
|
||||
class MetalDeviceInfoTests(unittest.TestCase):
|
||||
def test_host_check_passes(self):
|
||||
compiler = shutil.which('c++')
|
||||
if not compiler:
|
||||
self.skipTest('no C++ compiler')
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
include = Path(folder) / 'include/Metal'
|
||||
include.mkdir(parents=True)
|
||||
(include / 'MTLDeviceInfo.h').write_text(header_from_patch())
|
||||
binary = Path(folder) / 'test-metal-device-info'
|
||||
build = subprocess.run([compiler, '-std=c++17', '-Wall', '-Werror', '-I', str(include.parent),
|
||||
str(ROOT / 'tools/test-metal-device-info.cpp'), '-o', str(binary)],
|
||||
capture_output=True, text=True)
|
||||
self.assertEqual(build.returncode, 0, build.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True, timeout=30)
|
||||
self.assertEqual(result.returncode, 0, result.stdout)
|
||||
self.assertIn('METAL_DEVICE_INFO failures=0 pass=1', result.stdout)
|
||||
self.assertIn('PASS registry ID is non-zero', result.stdout)
|
||||
self.assertIn('PASS Mac GPU name', result.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -29,7 +29,9 @@ unset RCLOL_APP_ARG3 RCLOL_APP_ARG3_SLOT RCLOL_UX_ARG1 RCLOL_UX_ARG1_SLOT
|
||||
unset RCLOL_UX_ARG2 RCLOL_UX_ARG2_SLOT
|
||||
# The paired runtime presents completed GPU textures without CPU readback.
|
||||
# Explicit 0 or an empty value selects the CPU fallback for diagnostics.
|
||||
for option in RCLOL_GPU_DIRECT_PRESENT RCLOL_GPU_DIRECT_ASYNC RCLOL_GPU_DIRECT_VSYNC DARLING_CA_PRESENT_AT_COMMIT DARLING_CA_SKIP_LOCAL_IOSURFACE_RENDER RCLOL_FORWARD_MOUSE_MOVED RCLOL_MOUSE_CONFINEMENT; do
|
||||
# DARLING_METAL_MAC_GPU_NAME reports the MacBookPro16,1 GPU of the host vendor (AMD or Intel) as
|
||||
# MTLDevice.name, matching the model IOKit reports; memory figures stay the host's.
|
||||
for option in RCLOL_GPU_DIRECT_PRESENT RCLOL_GPU_DIRECT_ASYNC RCLOL_GPU_DIRECT_VSYNC DARLING_CA_PRESENT_AT_COMMIT DARLING_CA_SKIP_LOCAL_IOSURFACE_RENDER RCLOL_FORWARD_MOUSE_MOVED RCLOL_MOUSE_CONFINEMENT DARLING_METAL_MAC_GPU_NAME; do
|
||||
eval 'value=${'"$option"'-1}'
|
||||
case "$value" in 0|'') unset "$option";; *) export "$option=$value";; esac
|
||||
done
|
||||
|
||||
@@ -49,6 +49,13 @@ int main() {
|
||||
expectName("", MTLDeviceInfo::kFallbackName);
|
||||
expectName(nullptr, MTLDeviceInfo::kFallbackName);
|
||||
|
||||
// -[MTLDevice registryID]: never 0, like a real Mac.
|
||||
{
|
||||
bool ok = MTLDeviceInfo::kDefaultRegistryID != 0;
|
||||
failures += !ok;
|
||||
std::printf("%s registry ID is non-zero\n", ok ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
// DARLING_METAL_GPU_NAME override.
|
||||
{
|
||||
bool ok = MTLDeviceInfo::resolveName("My GPU", "AMD Radeon RX 6800 XT (RADV NAVI21)") == "My GPU"
|
||||
@@ -58,6 +65,19 @@ int main() {
|
||||
std::printf("%s name override\n", ok ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
// DARLING_METAL_MAC_GPU_NAME: MacBookPro16,1 GPUs for AMD and Intel hosts; NVIDIA keeps its name.
|
||||
{
|
||||
using MTLDeviceInfo::macGpuName;
|
||||
bool ok = std::string(macGpuName(0x1002)) == "AMD Radeon Pro 5500M"
|
||||
&& std::string(macGpuName(0x8086)) == "Intel UHD Graphics 630"
|
||||
&& macGpuName(0x10de) == nullptr
|
||||
&& MTLDeviceInfo::resolveName(nullptr, "AMD Radeon RX 6600 (RADV NAVI23)", macGpuName(0x1002)) == "AMD Radeon Pro 5500M"
|
||||
&& MTLDeviceInfo::resolveName(nullptr, "NVIDIA GeForce GTX 1060 6GB", macGpuName(0x10de)) == "NVIDIA GeForce GTX 1060 6GB"
|
||||
&& MTLDeviceInfo::resolveName("My GPU", "AMD Radeon RX 6600 (RADV NAVI23)", macGpuName(0x1002)) == "My GPU";
|
||||
failures += !ok;
|
||||
std::printf("%s Mac GPU name\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);
|
||||
|
||||
Reference in new issue
Block a user