xref: /aosp_15_r20/external/angle/src/common/apple_platform_utils.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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