Edit

kc3-lang/angle/src/gpu_info_util/SystemInfo_macos.mm

Branch :

  • Show log

    Commit

  • Author : Seija
    Date : 2022-09-09 12:12:09
    Hash : 6c469cdf
    Message : Properly Create C++ string objects "" is an empty C string in Objective-C++. We need to use the proper constructors to convert a C string into std::string. Bug: angleproject:7646 Change-Id: Ided31dcbd9434cbae3ea828e29767bb3899e0f56 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3887639 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/gpu_info_util/SystemInfo_macos.mm
  • //
    // Copyright 2017 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.
    //
    
    // SystemInfo_macos.mm: implementation of the macOS-specific parts of SystemInfo.h
    
    #include "common/platform.h"
    
    #if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
    
    #    include "gpu_info_util/SystemInfo_internal.h"
    
    #    import <Cocoa/Cocoa.h>
    #    import <IOKit/IOKitLib.h>
    
    #    include "common/gl/cgl/FunctionsCGL.h"
    
    #    if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000
    #        define HAVE_MAIN_PORT_DEFAULT 1
    #    else
    #        define HAVE_MAIN_PORT_DEFAULT 0
    #    endif
    
    namespace angle
    {
    
    namespace
    {
    
    constexpr CGLRendererProperty kCGLRPRegistryIDLow  = static_cast<CGLRendererProperty>(140);
    constexpr CGLRendererProperty kCGLRPRegistryIDHigh = static_cast<CGLRendererProperty>(141);
    
    std::string GetMachineModel()
    {
    #    if HAVE_MAIN_PORT_DEFAULT
        const mach_port_t mainPort = kIOMainPortDefault;
    #    else
    #        pragma clang diagnostic push
    #        pragma clang diagnostic ignored "-Wdeprecated-declarations"
        const mach_port_t mainPort = kIOMasterPortDefault;
    #        pragma clang diagnostic pop
    #    endif
        io_service_t platformExpert =
            IOServiceGetMatchingService(mainPort, IOServiceMatching("IOPlatformExpertDevice"));
    
        if (platformExpert == IO_OBJECT_NULL)
        {
            return {};
        }
    
        CFDataRef modelData = static_cast<CFDataRef>(
            IORegistryEntryCreateCFProperty(platformExpert, CFSTR("model"), kCFAllocatorDefault, 0));
        if (modelData == nullptr)
        {
            IOObjectRelease(platformExpert);
            return {};
        }
    
        std::string result(reinterpret_cast<const char *>(CFDataGetBytePtr(modelData)));
    
        IOObjectRelease(platformExpert);
        CFRelease(modelData);
    
        return result;
    }
    
    // Extracts one integer property from a registry entry.
    bool GetEntryProperty(io_registry_entry_t entry, CFStringRef name, uint32_t *value)
    {
        *value = 0;
    
        CFDataRef data = static_cast<CFDataRef>(
            IORegistryEntrySearchCFProperty(entry, kIOServicePlane, name, kCFAllocatorDefault,
                                            kIORegistryIterateRecursively | kIORegistryIterateParents));
    
        if (data == nullptr)
        {
            return false;
        }
    
        const uint32_t *valuePtr = reinterpret_cast<const uint32_t *>(CFDataGetBytePtr(data));
    
        if (valuePtr == nullptr)
        {
            CFRelease(data);
            return false;
        }
    
        *value = *valuePtr;
        CFRelease(data);
        return true;
    }
    
    // Gathers the vendor and device IDs for GPUs listed in the IORegistry.
    void GetIORegistryDevices(std::vector<GPUDeviceInfo> *devices)
    {
        constexpr uint32_t kNumServices         = 2;
        const char *kServiceNames[kNumServices] = {"IOGraphicsAccelerator2", "AGXAccelerator"};
        const bool kServiceIsGraphicsAccelerator2[kNumServices] = {true, false};
        for (uint32_t i = 0; i < kNumServices; ++i)
        {
            // matchDictionary will be consumed by IOServiceGetMatchingServices, no need to release it.
            CFMutableDictionaryRef matchDictionary = IOServiceMatching(kServiceNames[i]);
    
            io_iterator_t entryIterator;
    #    if HAVE_MAIN_PORT_DEFAULT
            const mach_port_t mainPort = kIOMainPortDefault;
    #    else
    #        pragma clang diagnostic push
    #        pragma clang diagnostic ignored "-Wdeprecated-declarations"
            const mach_port_t mainPort = kIOMasterPortDefault;
    #        pragma clang diagnostic pop
    #    endif
            if (IOServiceGetMatchingServices(mainPort, matchDictionary, &entryIterator) !=
                kIOReturnSuccess)
            {
                continue;
            }
    
            io_registry_entry_t entry = IO_OBJECT_NULL;
            while ((entry = IOIteratorNext(entryIterator)) != IO_OBJECT_NULL)
            {
                constexpr uint32_t kClassCodeDisplayVGA = 0x30000;
                uint32_t classCode;
                GPUDeviceInfo info;
                // The registry ID of an IOGraphicsAccelerator2 or AGXAccelerator matches the ID used
                // for GPU selection by ANGLE_platform_angle_device_id
                if (IORegistryEntryGetRegistryEntryID(entry, &info.systemDeviceId) != kIOReturnSuccess)
                {
                    IOObjectRelease(entry);
                    continue;
                }
    
                io_registry_entry_t queryEntry = entry;
                if (kServiceIsGraphicsAccelerator2[i])
                {
                    // If the matching entry is an IOGraphicsAccelerator2, get the parent entry that
                    // will be the IOPCIDevice which holds vendor-id and device-id
                    io_registry_entry_t deviceEntry = IO_OBJECT_NULL;
                    if (IORegistryEntryGetParentEntry(entry, kIOServicePlane, &deviceEntry) !=
                            kIOReturnSuccess ||
                        deviceEntry == IO_OBJECT_NULL)
                    {
                        IOObjectRelease(entry);
                        continue;
                    }
                    IOObjectRelease(entry);
                    queryEntry = deviceEntry;
                }
    
                if (!GetEntryProperty(queryEntry, CFSTR("vendor-id"), &info.vendorId))
                {
                    IOObjectRelease(queryEntry);
                    continue;
                }
                // AGXAccelerator entries only provide a vendor ID.
                if (kServiceIsGraphicsAccelerator2[i])
                {
                    if (!GetEntryProperty(queryEntry, CFSTR("class-code"), &classCode))
                    {
                        IOObjectRelease(queryEntry);
                        continue;
                    }
                    if (classCode != kClassCodeDisplayVGA)
                    {
                        IOObjectRelease(queryEntry);
                        continue;
                    }
                    if (!GetEntryProperty(queryEntry, CFSTR("device-id"), &info.deviceId))
                    {
                        IOObjectRelease(queryEntry);
                        continue;
                    }
                    // Populate revisionId if we can
                    GetEntryProperty(queryEntry, CFSTR("revision-id"), &info.revisionId);
                }
    
                devices->push_back(info);
                IOObjectRelease(queryEntry);
            }
            IOObjectRelease(entryIterator);
    
            // If any devices have been populated by IOGraphicsAccelerator2, do not continue to
            // AGXAccelerator.
            if (!devices->empty())
            {
                break;
            }
        }
    }
    
    void ForceGPUSwitchIndex(SystemInfo *info)
    {
        VendorID activeVendor = 0;
        DeviceID activeDevice = 0;
    
        uint64_t gpuID = GetGpuIDFromDisplayID(kCGDirectMainDisplay);
    
        if (gpuID == 0)
            return;
    
        CFMutableDictionaryRef matchDictionary = IORegistryEntryIDMatching(gpuID);
    #    if HAVE_MAIN_PORT_DEFAULT
        const mach_port_t mainPort = kIOMainPortDefault;
    #    else
    #        pragma clang diagnostic push
    #        pragma clang diagnostic ignored "-Wdeprecated-declarations"
        const mach_port_t mainPort = kIOMasterPortDefault;
    #        pragma clang diagnostic pop
    #    endif
        io_service_t gpuEntry = IOServiceGetMatchingService(mainPort, matchDictionary);
    
        if (gpuEntry == IO_OBJECT_NULL)
        {
            IOObjectRelease(gpuEntry);
            return;
        }
    
        if (!(GetEntryProperty(gpuEntry, CFSTR("vendor-id"), &activeVendor) &&
              GetEntryProperty(gpuEntry, CFSTR("device-id"), &activeDevice)))
        {
            IOObjectRelease(gpuEntry);
            return;
        }
    
        IOObjectRelease(gpuEntry);
    
        for (size_t i = 0; i < info->gpus.size(); ++i)
        {
            if (info->gpus[i].vendorId == activeVendor && info->gpus[i].deviceId == activeDevice)
            {
                info->activeGPUIndex = static_cast<int>(i);
                break;
            }
        }
    }
    
    }  // anonymous namespace
    
    // Code from WebKit to get the active GPU's ID given a Core Graphics display ID.
    // https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/mac/PlatformScreenMac.mm
    // Used with permission.
    uint64_t GetGpuIDFromDisplayID(uint32_t displayID)
    {
        return GetGpuIDFromOpenGLDisplayMask(CGDisplayIDToOpenGLDisplayMask(displayID));
    }
    
    // Code from WebKit to query the GPU ID given an OpenGL display mask.
    // https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/mac/PlatformScreenMac.mm
    // Used with permission.
    uint64_t GetGpuIDFromOpenGLDisplayMask(uint32_t displayMask)
    {
        if (@available(macOS 10.13, *))
        {
            GLint numRenderers              = 0;
            CGLRendererInfoObj rendererInfo = nullptr;
            CGLError error = CGLQueryRendererInfo(displayMask, &rendererInfo, &numRenderers);
            if (!numRenderers || !rendererInfo || error != kCGLNoError)
                return 0;
    
            // The 0th renderer should not be the software renderer.
            GLint isAccelerated;
            error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPAccelerated, &isAccelerated);
            if (!isAccelerated || error != kCGLNoError)
            {
                CGLDestroyRendererInfo(rendererInfo);
                return 0;
            }
    
            GLint gpuIDLow  = 0;
            GLint gpuIDHigh = 0;
    
            error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow);
            if (error != kCGLNoError)
            {
                CGLDestroyRendererInfo(rendererInfo);
                return 0;
            }
    
            error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh);
            if (error != kCGLNoError)
            {
                CGLDestroyRendererInfo(rendererInfo);
                return 0;
            }
    
            CGLDestroyRendererInfo(rendererInfo);
            return (static_cast<uint64_t>(static_cast<uint32_t>(gpuIDHigh)) << 32) |
                   static_cast<uint64_t>(static_cast<uint32_t>(gpuIDLow));
        }
    
        return 0;
    }
    
    // Get VendorID from metal device's registry ID
    VendorID GetVendorIDFromMetalDeviceRegistryID(uint64_t registryID)
    {
    #    if defined(ANGLE_PLATFORM_MACOS)
        // On macOS, the following code is only supported since 10.13.
        if (@available(macOS 10.13, *))
    #    endif
        {
            // Get a matching dictionary for the IOGraphicsAccelerator2
            CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching(registryID);
            if (matchingDict == nullptr)
            {
                return 0;
            }
    
            // IOServiceGetMatchingService will consume the reference on the matching dictionary,
            // so we don't need to release the dictionary.
    #    if HAVE_MAIN_PORT_DEFAULT
            const mach_port_t mainPort = kIOMainPortDefault;
    #    else
    #        pragma clang diagnostic push
    #        pragma clang diagnostic ignored "-Wdeprecated-declarations"
            const mach_port_t mainPort = kIOMasterPortDefault;
    #        pragma clang diagnostic pop
    #    endif
            io_registry_entry_t acceleratorEntry = IOServiceGetMatchingService(mainPort, matchingDict);
            if (acceleratorEntry == IO_OBJECT_NULL)
            {
                return 0;
            }
    
            // Get the parent entry that will be the IOPCIDevice
            io_registry_entry_t deviceEntry = IO_OBJECT_NULL;
            if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) !=
                    kIOReturnSuccess ||
                deviceEntry == IO_OBJECT_NULL)
            {
                IOObjectRelease(acceleratorEntry);
                return 0;
            }
    
            IOObjectRelease(acceleratorEntry);
    
            uint32_t vendorId;
            if (!GetEntryProperty(deviceEntry, CFSTR("vendor-id"), &vendorId))
            {
                vendorId = 0;
            }
    
            IOObjectRelease(deviceEntry);
    
            return vendorId;
        }
        return 0;
    }
    
    bool GetSystemInfo_mac(SystemInfo *info)
    {
        {
            int32_t major = 0;
            int32_t minor = 0;
            ParseMacMachineModel(GetMachineModel(), &info->machineModelName, &major, &minor);
            info->machineModelVersion = std::to_string(major) + "." + std::to_string(minor);
        }
    
        GetIORegistryDevices(&info->gpus);
        if (info->gpus.empty())
        {
            return false;
        }
    
        // Call the generic GetDualGPUInfo function to initialize info fields
        // such as isOptimus, isAMDSwitchable, and the activeGPUIndex
        GetDualGPUInfo(info);
    
        // Then override the activeGPUIndex field of info to reflect the current
        // GPU instead of the non-intel GPU
        if (@available(macOS 10.13, *))
        {
            ForceGPUSwitchIndex(info);
        }
    
        // Figure out whether this is a dual-GPU system.
        //
        // TODO(kbr): this code was ported over from Chromium, and its correctness
        // could be improved - need to use Mac-specific APIs to determine whether
        // offline renderers are allowed, and whether these two GPUs are really the
        // integrated/discrete GPUs in a laptop.
        if (info->gpus.size() == 2 &&
            ((IsIntel(info->gpus[0].vendorId) && !IsIntel(info->gpus[1].vendorId)) ||
             (!IsIntel(info->gpus[0].vendorId) && IsIntel(info->gpus[1].vendorId))))
        {
            info->isMacSwitchable = true;
        }
    
    #    if defined(ANGLE_PLATFORM_MACCATALYST) && defined(ANGLE_CPU_ARM64)
        info->needsEAGLOnMac = true;
    #    endif
    
        return true;
    }
    
    }  // namespace angle
    
    #endif  // defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)