1// 2// Copyright 2022 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6 7#include "common/apple_platform_utils.h" 8#include "common/debug.h" 9 10#include <Metal/Metal.h> 11 12namespace angle 13{ 14 15bool IsMetalRendererAvailable() 16{ 17#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) 18 static bool queriedMachineModel = false; 19 static bool machineModelSufficient = true; 20 21 if (!queriedMachineModel) 22 { 23 queriedMachineModel = true; 24 25 std::string fullMachineModel; 26 if (GetMacosMachineModel(&fullMachineModel)) 27 { 28 using MachineModelVersion = std::pair<int32_t, int32_t>; 29 30 std::string name; 31 MachineModelVersion version; 32 ParseMacMachineModel(fullMachineModel, &name, &version.first, &version.second); 33 34 std::optional<MachineModelVersion> minVersion; 35 if (name == "MacBookAir") 36 { 37 minVersion = {8, 1}; // MacBook Air (Retina, 13-inch, 2018) 38 } 39 else if (name == "MacBookPro") 40 { 41 minVersion = {13, 1}; // MacBook Pro (13-inch, 2016) 42 } 43 else if (name == "MacBook") 44 { 45 minVersion = {9, 1}; // MacBook (Retina, 12-inch, Early 2016) 46 } 47 else if (name == "Macmini") 48 { 49 minVersion = {8, 1}; // Mac mini (2018) 50 } 51 else if (name == "iMac") 52 { 53 minVersion = {17, 1}; // iMac (Retina 5K, 27-inch, Late 2015) 54 } 55 else if (name == "iMacPro") 56 { 57 minVersion = {1, 1}; // iMac Pro 58 } 59 60 if (minVersion.has_value() && version < minVersion.value()) 61 { 62 WARN() << "Disabling Metal because machine model \"" << fullMachineModel 63 << "\" is below the minium supported version."; 64 machineModelSufficient = false; 65 } 66 } 67 } 68 69 if (!machineModelSufficient) 70 { 71 ASSERT(queriedMachineModel); 72 return false; 73 } 74#endif 75 76 static bool gpuFamilySufficient = []() -> bool { 77 ANGLE_APPLE_OBJC_SCOPE 78 { 79 auto device = [MTLCreateSystemDefaultDevice() ANGLE_APPLE_AUTORELEASE]; 80 if (!device) 81 { 82 return false; 83 } 84#if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000 85 // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend. 86 return [device supportsFamily:MTLGPUFamilyMacCatalyst2]; 87#elif TARGET_OS_MACCATALYST || TARGET_OS_OSX 88 // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend. 89 return [device supportsFamily:MTLGPUFamilyMac2]; 90#else 91 // Devices starting with A9 onwards are supported. Simulator is supported as per 92 // definition that running simulator on Mac Family 1 devices is not supported. 93 return true; 94#endif 95 } 96 }(); 97 return gpuFamilySufficient; 98} 99 100#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST) 101bool GetMacosMachineModel(std::string *outMachineModel) 102{ 103# if TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 120000 104 const mach_port_t mainPort = kIOMasterPortDefault; 105# else 106 const mach_port_t mainPort = kIOMainPortDefault; 107# endif 108 io_service_t platformExpert = 109 IOServiceGetMatchingService(mainPort, IOServiceMatching("IOPlatformExpertDevice")); 110 111 if (platformExpert == IO_OBJECT_NULL) 112 { 113 return false; 114 } 115 116 CFDataRef modelData = static_cast<CFDataRef>( 117 IORegistryEntryCreateCFProperty(platformExpert, CFSTR("model"), kCFAllocatorDefault, 0)); 118 if (modelData == nullptr) 119 { 120 IOObjectRelease(platformExpert); 121 return false; 122 } 123 124 *outMachineModel = reinterpret_cast<const char *>(CFDataGetBytePtr(modelData)); 125 126 IOObjectRelease(platformExpert); 127 CFRelease(modelData); 128 129 return true; 130} 131 132bool ParseMacMachineModel(const std::string &identifier, 133 std::string *type, 134 int32_t *major, 135 int32_t *minor) 136{ 137 size_t numberLoc = identifier.find_first_of("0123456789"); 138 if (numberLoc == std::string::npos) 139 { 140 return false; 141 } 142 143 size_t commaLoc = identifier.find(',', numberLoc); 144 if (commaLoc == std::string::npos || commaLoc >= identifier.size()) 145 { 146 return false; 147 } 148 149 const char *numberPtr = &identifier[numberLoc]; 150 const char *commaPtr = &identifier[commaLoc + 1]; 151 char *endPtr = nullptr; 152 153 int32_t majorTmp = static_cast<int32_t>(std::strtol(numberPtr, &endPtr, 10)); 154 if (endPtr == numberPtr) 155 { 156 return false; 157 } 158 159 int32_t minorTmp = static_cast<int32_t>(std::strtol(commaPtr, &endPtr, 10)); 160 if (endPtr == commaPtr) 161 { 162 return false; 163 } 164 165 *major = majorTmp; 166 *minor = minorTmp; 167 *type = identifier.substr(0, numberLoc); 168 169 return true; 170} 171#endif 172 173} // namespace angle 174