// // Copyright 2022 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // #include "common/apple_platform_utils.h" #include "common/debug.h" #include "common/system_utils.h" #include #include namespace angle { bool IsMetalRendererAvailable() { #if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) static bool queriedMachineModel = false; static bool machineModelSufficient = true; if (!queriedMachineModel) { queriedMachineModel = true; std::string fullMachineModel; if (GetMacosMachineModel(&fullMachineModel)) { using MachineModelVersion = std::pair; std::string name; MachineModelVersion version; ParseMacMachineModel(fullMachineModel, &name, &version.first, &version.second); std::optional minVersion; if (name == "MacBookAir") { minVersion = {8, 1}; // MacBook Air (Retina, 13-inch, 2018) } else if (name == "MacBookPro") { minVersion = {13, 1}; // MacBook Pro (13-inch, 2016) } else if (name == "MacBook") { minVersion = {9, 1}; // MacBook (Retina, 12-inch, Early 2016) } else if (name == "Macmini") { minVersion = {8, 1}; // Mac mini (2018) } else if (name == "iMac") { minVersion = {17, 1}; // iMac (Retina 5K, 27-inch, Late 2015) } else if (name == "iMacPro") { minVersion = {1, 1}; // iMac Pro } if (minVersion.has_value() && version < minVersion.value()) { WARN() << "Disabling Metal because machine model \"" << fullMachineModel << "\" is below the minium supported version."; machineModelSufficient = false; } } } if (!machineModelSufficient) { ASSERT(queriedMachineModel); return false; } #endif #if defined(ANGLE_PLATFORM_MACOS) && defined(__aarch64__) NSOperatingSystemVersion systemVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; if (systemVersion.majorVersion == 15 && systemVersion.minorVersion == 0) { // On ARM64 MacOS 15.0.x, Metal Shader with newLibraryWithSource didn't work, // if the executable path contains non-ASCII characters. // Bug: https://issues.chromium.org/issues/389559087 std::string executableName = GetExecutablePath(); for (char c : executableName) { if (static_cast(c) > 127) { return false; } } } #endif static bool gpuFamilySufficient = []() -> bool { ANGLE_APPLE_OBJC_SCOPE { auto device = [MTLCreateSystemDefaultDevice() ANGLE_APPLE_AUTORELEASE]; if (!device) { return false; } #if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000 // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend. return [device supportsFamily:MTLGPUFamilyMacCatalyst2]; #elif TARGET_OS_MACCATALYST || TARGET_OS_OSX // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend. return [device supportsFamily:MTLGPUFamilyMac2]; #else // Devices starting with A9 onwards are supported. Simulator is supported as per // definition that running simulator on Mac Family 1 devices is not supported. return true; #endif } }(); return gpuFamilySufficient; } #if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) bool GetMacosMachineModel(std::string *outMachineModel) { # if TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 120000 const mach_port_t mainPort = kIOMasterPortDefault; # else const mach_port_t mainPort = kIOMainPortDefault; # endif io_service_t platformExpert = IOServiceGetMatchingService(mainPort, IOServiceMatching("IOPlatformExpertDevice")); if (platformExpert == IO_OBJECT_NULL) { return false; } CFDataRef modelData = static_cast( IORegistryEntryCreateCFProperty(platformExpert, CFSTR("model"), kCFAllocatorDefault, 0)); if (modelData == nullptr) { IOObjectRelease(platformExpert); return false; } *outMachineModel = reinterpret_cast(CFDataGetBytePtr(modelData)); IOObjectRelease(platformExpert); CFRelease(modelData); return true; } bool ParseMacMachineModel(const std::string &identifier, std::string *type, int32_t *major, int32_t *minor) { size_t numberLoc = identifier.find_first_of("0123456789"); if (numberLoc == std::string::npos) { return false; } size_t commaLoc = identifier.find(',', numberLoc); if (commaLoc == std::string::npos || commaLoc >= identifier.size()) { return false; } const char *numberPtr = &identifier[numberLoc]; const char *commaPtr = &identifier[commaLoc + 1]; char *endPtr = nullptr; int32_t majorTmp = static_cast(std::strtol(numberPtr, &endPtr, 10)); if (endPtr == numberPtr) { return false; } int32_t minorTmp = static_cast(std::strtol(commaPtr, &endPtr, 10)); if (endPtr == commaPtr) { return false; } *major = majorTmp; *minor = minorTmp; *type = identifier.substr(0, numberLoc); return true; } #endif } // namespace angle