// macOS-only MTLDevice properties, added to the device's concrete class before the game starts. // Values describe a built-in Apple GPU. Other selectors iOS lacks are logged and answer zero. #import #import #import "fx.h" static id fx_zero(id self, SEL _cmd, ...) { return nil; } static BOOL fx_no(id self, SEL _cmd) { return NO; } static BOOL fx_yes(id self, SEL _cmd) { return YES; } static NSUInteger fx_uint_zero(id self, SEL _cmd) { return 0; } static NSUInteger fx_uint_one(id self, SEL _cmd) { return 1; } static void fx_add(Class cls, const char *name, IMP imp, const char *types) { SEL sel = sel_registerName(name); if (!class_getInstanceMethod(cls, sel)) { class_addMethod(cls, sel, imp, types); fx_log("[fx] Metal compat: -[%s %s]", class_getName(cls), name); } } static BOOL (*fx_original_resolve)(id, SEL, SEL); static BOOL fx_device_resolve(id cls, SEL _cmd, SEL sel) { if (fx_original_resolve && fx_original_resolve(cls, _cmd, sel)) return YES; const char *name = sel_getName(sel); if (name[0] == '_' || name[0] == '.') return NO; fx_log("[fx] Metal compat: unknown -[%s %s] -> 0", class_getName(cls), name); class_addMethod(cls, sel, (IMP)fx_zero, "@@:"); return YES; } FX_EXPORT void fx_patch_metal(void) { id device = MTLCreateSystemDefaultDevice(); if (!device) { fx_log("[fx] no Metal device"); return; } Class cls = object_getClass(device); fx_log("[fx] Metal device %s (%s), unified memory %d", device.name.UTF8String, class_getName(cls), device.hasUnifiedMemory); fx_add(cls, "isDepth24Stencil8PixelFormatSupported", (IMP)fx_no, "B@:"); fx_add(cls, "isLowPower", (IMP)fx_no, "B@:"); fx_add(cls, "isHeadless", (IMP)fx_no, "B@:"); fx_add(cls, "isRemovable", (IMP)fx_no, "B@:"); fx_add(cls, "location", (IMP)fx_uint_zero, "Q@:"); // MTLDeviceLocationBuiltIn fx_add(cls, "locationNumber", (IMP)fx_uint_zero, "Q@:"); fx_add(cls, "maxTransferRate", (IMP)fx_uint_zero, "Q@:"); fx_add(cls, "peerGroupID", (IMP)fx_uint_zero, "Q@:"); fx_add(cls, "peerIndex", (IMP)fx_uint_zero, "I@:"); fx_add(cls, "peerCount", (IMP)fx_uint_one, "I@:"); fx_add(cls, "supportsBCTextureCompression", (IMP)fx_no, "B@:"); fx_add(cls, "areProgrammableSamplePositionsSupported", (IMP)fx_yes, "B@:"); Method resolve = class_getClassMethod(cls, @selector(resolveInstanceMethod:)); fx_original_resolve = (BOOL (*)(id, SEL, SEL))method_getImplementation(resolve); // Overrides only this concrete class; inherited NSObject behavior is kept via fx_original_resolve. if (!class_addMethod(object_getClass(cls), @selector(resolveInstanceMethod:), (IMP)fx_device_resolve, "B@::")) method_setImplementation(resolve, (IMP)fx_device_resolve); }