Include Metal Capabilities Helper (#372)

* Add Tart Metal capabilities to base images

* Simplify Metal capability usage documentation

* Make Tart Metal capabilities opt-in

* Fix Metal shim architecture checks on Xcode 27

* Support protected TCC databases on macOS 27
This commit is contained in:
Fedor Kororkov
2026-08-19 12:48:52 -04:00
committed by GitHub
parent 4849509b2d
commit 795a125e78
13 changed files with 801 additions and 2 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Cua AI, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
# Tart Metal capabilities
This is a vendored adaptation of the minimal [Cua/Lume Metal capability shim](https://github.com/trycua/cua/tree/3c1acf27748c3e0f8ff71cd0c9ab072b1e160997/libs/lume/metal-capability-shim)
at revision `3c1acf27748c3e0f8ff71cd0c9ab072b1e160997`. The full shim source and
Metal capability probe are copied here. The source's `Lume` identifiers,
diagnostics, and `LUME_METAL_*` settings are renamed to `Tart` and `TART_METAL_*`.
The original Cua copyright and [MIT license](LICENSE) are retained. The upstream
source SHA-256 before renaming is
`e1371b1e579bca895e6b3a2b581b9f328203d9786bf809912a4a5d1672010cce`.
The shim changes only Apple-family capability answers and selected memory
limits. It does not advertise additional Common, Mac, or Metal families. It
uses private, version-sensitive behavior; reported capabilities are not a
guarantee that every corresponding GPU operation is supported.
Configuration is read once, when each process loads the library:
| Variable | Behavior |
| --- | --- |
| `TART_METAL_APPLE_FAMILY_MAX` | Required Apple-family ceiling, from 1001 through 1999. Missing, zero, or invalid values disable the shim. |
| `TART_METAL_MAX_THREADGROUP_MEMORY` | Memory floor in bytes; defaults to 65536. |
| `TART_METAL_RECOMMENDED_WORKING_SET_SIZE` | Optional working-set floor in bytes; unchanged when unset. |
The old `LUME_METAL_*` names are not recognized. Base images install the library
without enabling it for the guest agent or other processes. See the repository's
[Metal capabilities instructions](../../README.md#metal-capabilities) for
explicit per-command activation and host setup.
From the repository root, run `bash scripts/test-tart-metal-capabilities.sh` to
build and test without installing anything on the host. The installer accepts
`DESTDIR` for staging and `TART_METAL_SOURCE_DIR` for Packer's uploaded sources.
@@ -0,0 +1,207 @@
// Copyright 2026 Cua AI, Inc.
// SPDX-License-Identifier: MIT
// Adapted from the Cua/Lume team's process-scoped Metal capability shim.
// Credit to the Lume folks for the idea and original implementation:
// https://github.com/trycua/cua/blob/3c1acf27748c3e0f8ff71cd0c9ab072b1e160997/blog/gpu-passthrough-macos-vms.md
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import <objc/runtime.h>
#include <errno.h>
#include <stdlib.h>
typedef unsigned long long TartU64;
typedef struct {
BOOL enabled;
NSUInteger appleFamilyMax;
NSUInteger maxThreadgroupMemory;
BOOL hasRecommendedWorkingSetSize;
NSUInteger recommendedWorkingSetSize;
} TartMetalConfiguration;
static TartMetalConfiguration gConfiguration = {0};
static IMP gOriginalInitGPUFamilySupport = NULL;
static IMP gOriginalMaxThreadgroupMemoryLength = NULL;
static IMP gOriginalRecommendedMaxWorkingSetSize = NULL;
static IMP gOriginalSupportsFamily = NULL;
static BOOL gDeviceHooksInstalled = NO;
static BOOL parseUnsignedEnvironmentValue(const char *name, TartU64 *value) {
const char *rawValue = getenv(name);
if (!rawValue || !*rawValue) return NO;
errno = 0;
char *end = NULL;
unsigned long long parsed = strtoull(rawValue, &end, 0);
if (errno != 0 || end == rawValue || !end || *end != '\0') return NO;
*value = parsed;
return YES;
}
static BOOL loadConfiguration(void) {
TartU64 appleFamilyMax = 0;
if (!parseUnsignedEnvironmentValue(
"TART_METAL_APPLE_FAMILY_MAX",
&appleFamilyMax
) || appleFamilyMax < 1001 || appleFamilyMax >= 2000) {
return NO;
}
TartU64 maxThreadgroupMemory = 65536;
const char *rawThreadgroupMemory = getenv("TART_METAL_MAX_THREADGROUP_MEMORY");
if (rawThreadgroupMemory && *rawThreadgroupMemory &&
!parseUnsignedEnvironmentValue("TART_METAL_MAX_THREADGROUP_MEMORY", &maxThreadgroupMemory)) {
return NO;
}
TartU64 recommendedWorkingSetSize = 0;
const char *rawWorkingSetSize = getenv("TART_METAL_RECOMMENDED_WORKING_SET_SIZE");
BOOL hasRecommendedWorkingSetSize = rawWorkingSetSize && *rawWorkingSetSize;
if (hasRecommendedWorkingSetSize &&
!parseUnsignedEnvironmentValue(
"TART_METAL_RECOMMENDED_WORKING_SET_SIZE",
&recommendedWorkingSetSize
)) {
return NO;
}
gConfiguration.enabled = YES;
gConfiguration.appleFamilyMax = (NSUInteger)appleFamilyMax;
gConfiguration.maxThreadgroupMemory = (NSUInteger)maxThreadgroupMemory;
gConfiguration.hasRecommendedWorkingSetSize = hasRecommendedWorkingSetSize;
gConfiguration.recommendedWorkingSetSize = (NSUInteger)recommendedWorkingSetSize;
return YES;
}
static NSUInteger hookMaxThreadgroupMemoryLength(id self, SEL selector) {
NSUInteger original = gOriginalMaxThreadgroupMemoryLength
? ((NSUInteger(*)(id, SEL))(void *)gOriginalMaxThreadgroupMemoryLength)(self, selector)
: 0;
return original < gConfiguration.maxThreadgroupMemory
? gConfiguration.maxThreadgroupMemory
: original;
}
static NSUInteger hookRecommendedMaxWorkingSetSize(id self, SEL selector) {
NSUInteger original = gOriginalRecommendedMaxWorkingSetSize
? ((NSUInteger(*)(id, SEL))(void *)gOriginalRecommendedMaxWorkingSetSize)(self, selector)
: 0;
return original < gConfiguration.recommendedWorkingSetSize
? gConfiguration.recommendedWorkingSetSize
: original;
}
static BOOL hookSupportsFamily(id self, SEL selector, NSUInteger family) {
BOOL original = gOriginalSupportsFamily
? ((BOOL(*)(id, SEL, NSUInteger))(void *)gOriginalSupportsFamily)(self, selector, family)
: NO;
BOOL isConfiguredAppleFamily = family >= 1001 &&
family <= gConfiguration.appleFamilyMax;
return original || isConfiguredAppleFamily;
}
static BOOL replaceMethod(
Class deviceClass,
NSString *selectorName,
IMP replacement,
IMP *original
) {
SEL selector = NSSelectorFromString(selectorName);
Method method = class_getInstanceMethod(deviceClass, selector);
if (!method) return NO;
*original = method_setImplementation(method, replacement);
return *original != NULL;
}
static void installDeviceHooks(id device) {
@synchronized([device class]) {
if (gDeviceHooksInstalled) return;
Class deviceClass = [device class];
Method maxThreadgroupMemory = class_getInstanceMethod(
deviceClass,
NSSelectorFromString(@"maxThreadgroupMemoryLength")
);
Method supportsFamily = class_getInstanceMethod(
deviceClass,
NSSelectorFromString(@"supportsFamily:")
);
Method recommendedWorkingSetSize = gConfiguration.hasRecommendedWorkingSetSize
? class_getInstanceMethod(
deviceClass,
NSSelectorFromString(@"recommendedMaxWorkingSetSize")
)
: NULL;
if (!maxThreadgroupMemory || !supportsFamily ||
(gConfiguration.hasRecommendedWorkingSetSize && !recommendedWorkingSetSize)) {
NSLog(@"[TartMetalCapabilities] Required device methods are unavailable; leaving stock capabilities unchanged");
return;
}
BOOL installed = replaceMethod(
deviceClass,
@"maxThreadgroupMemoryLength",
(IMP)hookMaxThreadgroupMemoryLength,
&gOriginalMaxThreadgroupMemoryLength
);
installed = installed && replaceMethod(
deviceClass,
@"supportsFamily:",
(IMP)hookSupportsFamily,
&gOriginalSupportsFamily
);
if (installed && gConfiguration.hasRecommendedWorkingSetSize) {
installed = replaceMethod(
deviceClass,
@"recommendedMaxWorkingSetSize",
(IMP)hookRecommendedMaxWorkingSetSize,
&gOriginalRecommendedMaxWorkingSetSize
);
}
if (!installed) {
NSLog(@"[TartMetalCapabilities] Capability hook installation was incomplete");
return;
}
gDeviceHooksInstalled = YES;
NSLog(
@"[TartMetalCapabilities] Enabled for %@ (appleFamilyMax=%llu maxThreadgroupMemory=%llu)",
[NSProcessInfo processInfo].processName,
(TartU64)gConfiguration.appleFamilyMax,
(TartU64)gConfiguration.maxThreadgroupMemory
);
}
}
static void hookInitGPUFamilySupport(id self, SEL selector) {
installDeviceHooks(self);
((void(*)(id, SEL))(void *)gOriginalInitGPUFamilySupport)(self, selector);
}
__attribute__((constructor))
static void initializeTartMetalCapabilities(void) {
@autoreleasepool {
if (!loadConfiguration()) return;
Class deviceClass = NSClassFromString(@"_MTLDevice");
if (!deviceClass) return;
Method method = class_getInstanceMethod(
deviceClass,
NSSelectorFromString(@"initGPUFamilySupport")
);
if (!method) return;
gOriginalInitGPUFamilySupport = method_setImplementation(
method,
(IMP)hookInitGPUFamilySupport
);
}
}
@@ -0,0 +1,66 @@
#import "../Sources/TartMetalCapabilities.m"
#include <assert.h>
#include <stdio.h>
#include <string.h>
static void resetConfiguration(void) {
unsetenv("TART_METAL_APPLE_FAMILY_MAX");
unsetenv("TART_METAL_MAX_THREADGROUP_MEMORY");
unsetenv("TART_METAL_RECOMMENDED_WORKING_SET_SIZE");
unsetenv("LUME_METAL_APPLE_FAMILY_MAX");
gConfiguration = (TartMetalConfiguration){0};
}
int main(int argc, const char *argv[]) {
// In this mode, check the configuration loaded by the real constructor
// before main(), as it would be for a newly executed workload.
if (argc == 2) {
NSUInteger expected = (NSUInteger)strtoull(argv[1], NULL, 10);
assert(gConfiguration.enabled == (expected != 0));
assert(gConfiguration.appleFamilyMax == expected);
return 0;
}
resetConfiguration();
assert(!loadConfiguration());
setenv("LUME_METAL_APPLE_FAMILY_MAX", "1009", 1);
assert(!loadConfiguration());
const char *invalidFamilies[] = {"", "0", "1000", "2000", "-1", "1009x"};
for (size_t i = 0; i < sizeof(invalidFamilies) / sizeof(invalidFamilies[0]); i++) {
resetConfiguration();
setenv("TART_METAL_APPLE_FAMILY_MAX", invalidFamilies[i], 1);
assert(!loadConfiguration());
assert(!gConfiguration.enabled);
}
resetConfiguration();
setenv("TART_METAL_APPLE_FAMILY_MAX", "1009", 1);
assert(loadConfiguration());
assert(gConfiguration.appleFamilyMax == 1009);
assert(gConfiguration.maxThreadgroupMemory == 65536);
assert(!gConfiguration.hasRecommendedWorkingSetSize);
resetConfiguration();
setenv("TART_METAL_APPLE_FAMILY_MAX", "1999", 1);
setenv("TART_METAL_MAX_THREADGROUP_MEMORY", "32768", 1);
setenv("TART_METAL_RECOMMENDED_WORKING_SET_SIZE", "1073741824", 1);
assert(loadConfiguration());
assert(gConfiguration.appleFamilyMax == 1999);
assert(gConfiguration.maxThreadgroupMemory == 32768);
assert(gConfiguration.hasRecommendedWorkingSetSize);
assert(gConfiguration.recommendedWorkingSetSize == 1073741824);
resetConfiguration();
setenv("TART_METAL_APPLE_FAMILY_MAX", "1009", 1);
setenv("TART_METAL_MAX_THREADGROUP_MEMORY", "invalid", 1);
assert(!loadConfiguration());
unsetenv("TART_METAL_MAX_THREADGROUP_MEMORY");
setenv("TART_METAL_RECOMMENDED_WORKING_SET_SIZE", "invalid", 1);
assert(!loadConfiguration());
resetConfiguration();
puts("configuration: OK");
return 0;
}
@@ -0,0 +1,58 @@
#include <assert.h>
#include <mach-o/dyld.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static pid_t startChild(const char *executable, const char *library, const char *family) {
pid_t child = fork();
assert(child >= 0);
if (child == 0) {
char *libraryAssignment = NULL;
char *familyAssignment = NULL;
assert(asprintf(&libraryAssignment, "DYLD_INSERT_LIBRARIES=%s", library) >= 0);
assert(asprintf(&familyAssignment, "TART_METAL_APPLE_FAMILY_MAX=%s", family) >= 0);
execl("/usr/bin/env", "env", libraryAssignment, familyAssignment,
executable, family, "child", NULL);
_exit(1);
}
return child;
}
int main(int argc, char **argv) {
assert(argc >= 2);
const char *library = getenv("DYLD_INSERT_LIBRARIES");
const char *family = getenv("TART_METAL_APPLE_FAMILY_MAX");
const char *memory = getenv("TART_METAL_MAX_THREADGROUP_MEMORY");
int loaded = 0;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
if (strstr(_dyld_get_image_name(i), "/TartMetalCapabilities.dylib")) loaded = 1;
}
if (strcmp(argv[1], "stock") == 0) {
assert(!library && !family && !memory && !loaded);
puts("stock process without injection: OK");
return 0;
}
assert(library && family && memory);
assert(strcmp(family, argv[1]) == 0);
assert(strcmp(memory, "65536") == 0);
assert(loaded);
if (argc == 3) return 0;
// Concurrent workloads must get independent overrides while their parent
// retains its explicit opt-in. Exercise the documented /usr/bin/env path.
pid_t children[] = {
startChild(argv[0], library, "1008"),
startChild(argv[0], library, "0"),
};
for (size_t i = 0; i < sizeof(children) / sizeof(children[0]); i++) {
int status = 0;
assert(waitpid(children[i], &status, 0) == children[i]);
assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
assert(strcmp(getenv("TART_METAL_APPLE_FAMILY_MAX"), argv[1]) == 0);
puts("per-process injection and overrides: OK");
return 0;
}
@@ -0,0 +1,42 @@
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#include <errno.h>
#include <stdlib.h>
static BOOL parseFamily(const char *raw, NSUInteger *family) {
errno = 0;
char *end = NULL;
unsigned long long parsed = strtoull(raw, &end, 0);
if (errno != 0 || end == raw || !end || *end != '\0') return NO;
*family = (NSUInteger)parsed;
return YES;
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSUInteger family = 1009;
if (argc > 1 && !parseFamily(argv[1], &family)) {
fprintf(stderr, "invalid family: %s\n", argv[1]);
return 2;
}
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
if (!device) {
fprintf(stderr, "Metal device unavailable\n");
return 1;
}
printf("device=%s\n", device.name.UTF8String);
printf("family=%llu\n", (unsigned long long)family);
printf(
"supports_family=%s\n",
[device supportsFamily:(MTLGPUFamily)family] ? "true" : "false"
);
printf(
"max_threadgroup_memory=%llu\n",
(unsigned long long)device.maxThreadgroupMemoryLength
);
}
return 0;
}