• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
9#include "common/debug.h"
10#include "common/system_utils.h"
11
12#include <Foundation/Foundation.h>
13#include <Metal/Metal.h>
14
15namespace angle
16{
17
18bool IsMetalRendererAvailable()
19{
20#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
21    static bool queriedMachineModel    = false;
22    static bool machineModelSufficient = true;
23
24    if (!queriedMachineModel)
25    {
26        queriedMachineModel = true;
27
28        std::string fullMachineModel;
29        if (GetMacosMachineModel(&fullMachineModel))
30        {
31            using MachineModelVersion = std::pair<int32_t, int32_t>;
32
33            std::string name;
34            MachineModelVersion version;
35            ParseMacMachineModel(fullMachineModel, &name, &version.first, &version.second);
36
37            std::optional<MachineModelVersion> minVersion;
38            if (name == "MacBookAir")
39            {
40                minVersion = {8, 1};  // MacBook Air (Retina, 13-inch, 2018)
41            }
42            else if (name == "MacBookPro")
43            {
44                minVersion = {13, 1};  // MacBook Pro (13-inch, 2016)
45            }
46            else if (name == "MacBook")
47            {
48                minVersion = {9, 1};  // MacBook (Retina, 12-inch, Early 2016)
49            }
50            else if (name == "Macmini")
51            {
52                minVersion = {8, 1};  // Mac mini (2018)
53            }
54            else if (name == "iMac")
55            {
56                minVersion = {17, 1};  // iMac (Retina 5K, 27-inch, Late 2015)
57            }
58            else if (name == "iMacPro")
59            {
60                minVersion = {1, 1};  // iMac Pro
61            }
62
63            if (minVersion.has_value() && version < minVersion.value())
64            {
65                WARN() << "Disabling Metal because machine model \"" << fullMachineModel
66                       << "\" is below the minium supported version.";
67                machineModelSufficient = false;
68            }
69        }
70    }
71
72    if (!machineModelSufficient)
73    {
74        ASSERT(queriedMachineModel);
75        return false;
76    }
77#endif
78
79#if defined(ANGLE_PLATFORM_MACOS) && defined(__aarch64__)
80    NSOperatingSystemVersion systemVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
81    if (systemVersion.majorVersion == 15 && systemVersion.minorVersion == 0)
82    {
83        // On ARM64 MacOS 15.0.x, Metal Shader with newLibraryWithSource didn't work,
84        // if the executable path contains non-ASCII characters.
85        // Bug: https://issues.chromium.org/issues/389559087
86        std::string executableName = GetExecutablePath();
87        for (char c : executableName)
88        {
89            if (static_cast<unsigned char>(c) > 127)
90            {
91                return false;
92            }
93        }
94    }
95#endif
96
97    static bool gpuFamilySufficient = []() -> bool {
98        ANGLE_APPLE_OBJC_SCOPE
99        {
100            auto device = [MTLCreateSystemDefaultDevice() ANGLE_APPLE_AUTORELEASE];
101            if (!device)
102            {
103                return false;
104            }
105#if TARGET_OS_MACCATALYST && __IPHONE_OS_VERSION_MIN_REQUIRED < 160000
106            // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend.
107            return [device supportsFamily:MTLGPUFamilyMacCatalyst2];
108#elif TARGET_OS_MACCATALYST || TARGET_OS_OSX
109            // Devices in family 1, such as MacBookPro11,4, cannot use ANGLE's Metal backend.
110            return [device supportsFamily:MTLGPUFamilyMac2];
111#else
112            // Devices starting with A9 onwards are supported. Simulator is supported as per
113            // definition that running simulator on Mac Family 1 devices is not supported.
114            return true;
115#endif
116        }
117    }();
118    return gpuFamilySufficient;
119}
120
121#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
122bool GetMacosMachineModel(std::string *outMachineModel)
123{
124#    if TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 120000
125    const mach_port_t mainPort = kIOMasterPortDefault;
126#    else
127    const mach_port_t mainPort = kIOMainPortDefault;
128#    endif
129    io_service_t platformExpert =
130        IOServiceGetMatchingService(mainPort, IOServiceMatching("IOPlatformExpertDevice"));
131
132    if (platformExpert == IO_OBJECT_NULL)
133    {
134        return false;
135    }
136
137    CFDataRef modelData = static_cast<CFDataRef>(
138        IORegistryEntryCreateCFProperty(platformExpert, CFSTR("model"), kCFAllocatorDefault, 0));
139    if (modelData == nullptr)
140    {
141        IOObjectRelease(platformExpert);
142        return false;
143    }
144
145    *outMachineModel = reinterpret_cast<const char *>(CFDataGetBytePtr(modelData));
146
147    IOObjectRelease(platformExpert);
148    CFRelease(modelData);
149
150    return true;
151}
152
153bool ParseMacMachineModel(const std::string &identifier,
154                          std::string *type,
155                          int32_t *major,
156                          int32_t *minor)
157{
158    size_t numberLoc = identifier.find_first_of("0123456789");
159    if (numberLoc == std::string::npos)
160    {
161        return false;
162    }
163
164    size_t commaLoc = identifier.find(',', numberLoc);
165    if (commaLoc == std::string::npos || commaLoc >= identifier.size())
166    {
167        return false;
168    }
169
170    const char *numberPtr = &identifier[numberLoc];
171    const char *commaPtr  = &identifier[commaLoc + 1];
172    char *endPtr          = nullptr;
173
174    int32_t majorTmp = static_cast<int32_t>(std::strtol(numberPtr, &endPtr, 10));
175    if (endPtr == numberPtr)
176    {
177        return false;
178    }
179
180    int32_t minorTmp = static_cast<int32_t>(std::strtol(commaPtr, &endPtr, 10));
181    if (endPtr == commaPtr)
182    {
183        return false;
184    }
185
186    *major = majorTmp;
187    *minor = minorTmp;
188    *type  = identifier.substr(0, numberLoc);
189
190    return true;
191}
192#endif
193
194}  // namespace angle
195