1 // Copyright 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "host-common/opengl/NativeGpuInfo.h"
16 
17 #include "host-common/opengl/macTouchOpenGL.h"
18 
19 #include <CoreGraphics/CGDirectDisplay.h>
20 #include <IOKit/graphics/IOGraphicsLib.h>
21 #include <IOKit/IOTypes.h>
22 #include <IOKit/IOKitLib.h>
23 
24 #include <string>
25 #include <vector>
26 
27 #include <stdio.h>
28 
29 typedef std::pair<uint32_t, uint32_t> GpuVendorDeviceId;
30 typedef std::vector<GpuVendorDeviceId> GpuVendorDeviceIdList;
31 
32 // Based on code from:
33 // https://chromium.googlesource.com/chromium/src/gpu/+/e626016b34c63b7ff51bf9a6c20b37bcc18150c4/config/gpu_info_collector_mac.mm
34 // https://github.com/glfw/glfw/blob/e0a6772e5e4c672179fc69a90bcda3369792ed1f/src/cocoa_monitor.m
35 
36 // GetEntryProperty():
37 // Return 0 if we couldn't find the property.
38 // The property values we use should not be 0, so it's OK to use 0 as failure.
GetEntryProperty(io_registry_entry_t entry,CFStringRef property_name)39 uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef property_name) {
40     CFDataRef ref(
41         static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
42             entry,
43             kIOServicePlane,
44             property_name,
45             kCFAllocatorDefault,
46             kIORegistryIterateRecursively | kIORegistryIterateParents)));
47 
48     if (!ref)
49         return 0;
50 
51     uint32_t value = 0;
52     const uint32_t* value_pointer =
53         reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(ref));
54 
55     if (value_pointer != NULL)
56         value = *value_pointer;
57 
58     CFRelease(ref);
59     return value;
60 }
61 
GetGPUInfoMac()62 static auto GetGPUInfoMac() {
63 
64     GpuVendorDeviceIdList res;
65 
66     io_iterator_t iter;
67     io_service_t serv = 0;
68 
69     CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect");
70     kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault,
71                              matching,
72                              &iter);
73 
74     if (err) return res;
75 
76     while ((serv = IOIteratorNext(iter)) != 0) {
77          uint32_t vendor_id = GetEntryProperty(serv, CFSTR("vendor-id"));
78          if (vendor_id) {
79              uint32_t device_id = GetEntryProperty(serv, CFSTR("device-id"));
80              if (device_id) {
81                 res.push_back(std::make_pair(vendor_id, device_id));
82              }
83          }
84      }
85 
86     return res;
87 }
88 
getGpuInfoListNative(GpuInfoList * out)89 void getGpuInfoListNative(GpuInfoList* out) {
90     // This call initializes a pixel format,
91     // which should update the IOKit stuff to the
92     // correct GPU that will actually be used
93     // while the emulator is running.
94     macTouchOpenGL();
95 
96     auto gpulist = GetGPUInfoMac();
97 
98     char vendoridbuf[64] = {};
99     char deviceidbuf[64] = {};
100 
101     for (const auto& elt : gpulist) {
102         snprintf(vendoridbuf, sizeof(vendoridbuf), "%04x", elt.first);
103         snprintf(deviceidbuf, sizeof(deviceidbuf), "%04x", elt.second);
104         out->infos.emplace_back(
105                 std::string(vendoridbuf), // make -> vendorid
106                 std::string(deviceidbuf), // model -> deviceid
107                 std::string(deviceidbuf), // device_id -> deviceid
108                 "", "", "" // revision, version, renderer blank
109                 );
110     }
111 }
112 
113 // macOS: Disable Vulkan for now unless on M1.
isVulkanSafeToUseNative()114 bool isVulkanSafeToUseNative() {
115 #ifdef __aarch64__
116     return true;
117 #else
118     return false;
119 #endif
120 }