Metal: use the IORegistry to get the PCI ids

MTLDevice.registryID points to an entry in the IORegistry from which we
are able to find the entry for the actual GPU device, which contains
properties for the PCI device and vendor ids.

But MTLDevice.registryID is introduced from macOS 10.13. For macOS <=
10.12, we get the PCI ids by matching them with MTLDevice.name.

BUG=dawn:120, dawn:184

Change-Id: If4040c623fc61115c88d453eee0f2d05ed17a546
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8920
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
This commit is contained in:
Li, Hao 2019-07-17 01:04:50 +00:00 committed by Commit Bot service account
parent 1ffb0d6a02
commit 6eb681cc54
1 changed files with 109 additions and 73 deletions

View File

@ -14,76 +14,53 @@
#include "dawn_native/metal/BackendMTL.h"
#include "common/Constants.h"
#include "dawn_native/Instance.h"
#include "dawn_native/MetalBackend.h"
#include "dawn_native/metal/DeviceMTL.h"
#include <IOKit/graphics/IOGraphicsLib.h>
#import <IOKit/IOKitLib.h>
namespace dawn_native { namespace metal {
namespace {
// Since CGDisplayIOServicePort was deprecated in macOS 10.9, we need create
// an alternative function for getting I/O service port from current display.
io_service_t GetDisplayIOServicePort() {
// The matching service port (or 0 if none can be found)
io_service_t servicePort = 0;
struct PCIIDs {
uint32_t vendorId;
uint32_t deviceId;
};
// Create matching dictionary for display service
CFMutableDictionaryRef matchingDict = IOServiceMatching("IODisplayConnect");
if (matchingDict == nullptr) {
return 0;
}
struct Vendor {
const char* trademark;
uint32_t vendorId;
};
io_iterator_t iter;
// IOServiceGetMatchingServices look up the default master ports that match a
// matching dictionary, and will consume the reference on the matching dictionary,
// so we don't need to release the dictionary, but the iterator handle should
// be released when its iteration is finished.
if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter) !=
kIOReturnSuccess) {
return 0;
}
const Vendor kVendors[] = {{"AMD", kVendorID_AMD},
{"Radeon", kVendorID_AMD},
{"Intel", kVendorID_Intel},
{"Geforce", kVendorID_Nvidia},
{"Quadro", kVendorID_Nvidia}};
// Vendor number and product number of current main display
const uint32_t displayVendorNumber = CGDisplayVendorNumber(kCGDirectMainDisplay);
const uint32_t displayProductNumber = CGDisplayModelNumber(kCGDirectMainDisplay);
io_service_t serv;
while ((serv = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
CFDictionaryRef displayInfo =
IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName);
CFNumberRef vendorIDRef, productIDRef;
Boolean success;
// The ownership of CF object follows the 'Get Rule', we don't need to
// release these values
success = CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayVendorID),
(const void**)&vendorIDRef);
success &= CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayProductID),
(const void**)&productIDRef);
if (success) {
CFIndex vendorID = 0, productID = 0;
CFNumberGetValue(vendorIDRef, kCFNumberSInt32Type, &vendorID);
CFNumberGetValue(productIDRef, kCFNumberSInt32Type, &productID);
if (vendorID == displayVendorNumber && productID == displayProductNumber) {
// Check if vendor id and product id match with current display's
// If it does, we find the desired service port
servicePort = serv;
CFRelease(displayInfo);
break;
}
// Find vendor ID from MTLDevice name.
MaybeError GetVendorIdFromVendors(id<MTLDevice> device, PCIIDs* ids) {
uint32_t vendorId = 0;
const char* deviceName = [device.name UTF8String];
for (const auto& it : kVendors) {
if (strstr(deviceName, it.trademark) != nullptr) {
vendorId = it.vendorId;
break;
}
CFRelease(displayInfo);
IOObjectRelease(serv);
}
IOObjectRelease(iter);
return servicePort;
if (vendorId == 0) {
return DAWN_CONTEXT_LOST_ERROR("Failed to find vendor id with the device");
}
// Set vendor id with 0
*ids = PCIIDs{vendorId, 0};
return {};
}
// Get integer property from registry entry.
// Extracts an integer property from a registry entry.
uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) {
uint32_t value = 0;
@ -93,19 +70,82 @@ namespace dawn_native { namespace metal {
entry, kIOServicePlane, name, kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents));
if (data != nullptr) {
const uint32_t* valuePtr =
reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data));
if (valuePtr) {
value = *valuePtr;
}
CFRelease(data);
if (data == nullptr) {
return value;
}
// CFDataGetBytePtr() is guaranteed to return a read-only pointer
value = *reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data));
CFRelease(data);
return value;
}
// Queries the IO Registry to find the PCI device and vendor IDs of the MTLDevice.
// The registry entry correponding to [device registryID] doesn't contain the exact PCI ids
// because it corresponds to a driver. However its parent entry corresponds to the device
// itself and has uint32_t "device-id" and "registry-id" keys. For example on a dual-GPU
// MacBook Pro 2017 the IORegistry explorer shows the following tree (simplified here):
//
// - PCI0@0
// | - AppleACPIPCI
// | | - IGPU@2 (type IOPCIDevice)
// | | | - IntelAccelerator (type IOGraphicsAccelerator2)
// | | - PEG0@1
// | | | - IOPP
// | | | | - GFX0@0 (type IOPCIDevice)
// | | | | | - AMDRadeonX4000_AMDBaffinGraphicsAccelerator (type IOGraphicsAccelerator2)
//
// [device registryID] is the ID for one of the IOGraphicsAccelerator2 and we can see that
// their parent always is an IOPCIDevice that has properties for the device and vendor IDs.
MaybeError GetDeviceIORegistryPCIInfo(id<MTLDevice> device, PCIIDs* ids) {
// Get a matching dictionary for the IOGraphicsAccelerator2
CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching([device registryID]);
if (matchingDict == nullptr) {
return DAWN_CONTEXT_LOST_ERROR("Failed to create the matching dict for the device");
}
// IOServiceGetMatchingService will consume the reference on the matching dictionary,
// so we don't need to release the dictionary.
io_registry_entry_t acceleratorEntry =
IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
if (acceleratorEntry == IO_OBJECT_NULL) {
return DAWN_CONTEXT_LOST_ERROR(
"Failed to get the IO registry entry for the accelerator");
}
// Get the parent entry that will be the IOPCIDevice
io_registry_entry_t deviceEntry = IO_OBJECT_NULL;
if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) !=
kIOReturnSuccess) {
IOObjectRelease(acceleratorEntry);
return DAWN_CONTEXT_LOST_ERROR(
"Failed to get the IO registry entry for the device");
}
ASSERT(deviceEntry != IO_OBJECT_NULL);
IOObjectRelease(acceleratorEntry);
uint32_t vendorId = GetEntryProperty(deviceEntry, CFSTR("vendor-id"));
uint32_t deviceId = GetEntryProperty(deviceEntry, CFSTR("device-id"));
*ids = PCIIDs{vendorId, deviceId};
IOObjectRelease(deviceEntry);
return {};
}
MaybeError GetDevicePCIInfo(id<MTLDevice> device, PCIIDs* ids) {
// [device registryID] is introduced on macOS 10.13+, otherwise workaround to get vendor
// id by vendor name on old macOS
if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:{10, 13, 0}]) {
return GetDeviceIORegistryPCIInfo(device, ids);
} else {
return GetVendorIdFromVendors(device, ids);
}
}
bool IsMetalSupported() {
// Metal was first introduced in macOS 10.11
NSOperatingSystemVersion macOS10_11 = {10, 11, 0};
@ -120,16 +160,12 @@ namespace dawn_native { namespace metal {
Adapter(InstanceBase* instance, id<MTLDevice> device)
: AdapterBase(instance, BackendType::Metal), mDevice([device retain]) {
mPCIInfo.name = std::string([mDevice.name UTF8String]);
// Gather the PCI device and vendor IDs based on which device is rendering to the
// main display. This is obviously wrong for systems with multiple devices.
// TODO(cwallez@chromium.org): Once Chromium has the macOS 10.13 SDK rolled, we
// should use MTLDevice.registryID to gather the information.
io_registry_entry_t entry = GetDisplayIOServicePort();
if (entry != IO_OBJECT_NULL) {
mPCIInfo.vendorId = GetEntryProperty(entry, CFSTR("vendor-id"));
mPCIInfo.deviceId = GetEntryProperty(entry, CFSTR("device-id"));
IOObjectRelease(entry);
}
PCIIDs ids;
if (!instance->ConsumedError(GetDevicePCIInfo(device, &ids))) {
mPCIInfo.vendorId = ids.vendorId;
mPCIInfo.deviceId = ids.deviceId;
};
if ([device isLowPower]) {
mDeviceType = DeviceType::IntegratedGPU;