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 "dawn_native/metal/BackendMTL.h"
#include "common/Constants.h"
#include "dawn_native/Instance.h" #include "dawn_native/Instance.h"
#include "dawn_native/MetalBackend.h" #include "dawn_native/MetalBackend.h"
#include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/metal/DeviceMTL.h"
#include <IOKit/graphics/IOGraphicsLib.h> #import <IOKit/IOKitLib.h>
namespace dawn_native { namespace metal { namespace dawn_native { namespace metal {
namespace { namespace {
// Since CGDisplayIOServicePort was deprecated in macOS 10.9, we need create struct PCIIDs {
// an alternative function for getting I/O service port from current display. uint32_t vendorId;
io_service_t GetDisplayIOServicePort() { uint32_t deviceId;
// The matching service port (or 0 if none can be found) };
io_service_t servicePort = 0;
// Create matching dictionary for display service struct Vendor {
CFMutableDictionaryRef matchingDict = IOServiceMatching("IODisplayConnect"); const char* trademark;
if (matchingDict == nullptr) { uint32_t vendorId;
return 0; };
}
io_iterator_t iter; const Vendor kVendors[] = {{"AMD", kVendorID_AMD},
// IOServiceGetMatchingServices look up the default master ports that match a {"Radeon", kVendorID_AMD},
// matching dictionary, and will consume the reference on the matching dictionary, {"Intel", kVendorID_Intel},
// so we don't need to release the dictionary, but the iterator handle should {"Geforce", kVendorID_Nvidia},
// be released when its iteration is finished. {"Quadro", kVendorID_Nvidia}};
if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter) !=
kIOReturnSuccess) {
return 0;
}
// Vendor number and product number of current main display // Find vendor ID from MTLDevice name.
const uint32_t displayVendorNumber = CGDisplayVendorNumber(kCGDirectMainDisplay); MaybeError GetVendorIdFromVendors(id<MTLDevice> device, PCIIDs* ids) {
const uint32_t displayProductNumber = CGDisplayModelNumber(kCGDirectMainDisplay); uint32_t vendorId = 0;
const char* deviceName = [device.name UTF8String];
io_service_t serv; for (const auto& it : kVendors) {
while ((serv = IOIteratorNext(iter)) != IO_OBJECT_NULL) { if (strstr(deviceName, it.trademark) != nullptr) {
CFDictionaryRef displayInfo = vendorId = it.vendorId;
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; break;
} }
} }
CFRelease(displayInfo); if (vendorId == 0) {
IOObjectRelease(serv); return DAWN_CONTEXT_LOST_ERROR("Failed to find vendor id with the device");
}
IOObjectRelease(iter);
return servicePort;
} }
// Get integer property from registry entry. // Set vendor id with 0
*ids = PCIIDs{vendorId, 0};
return {};
}
// Extracts an integer property from a registry entry.
uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) { uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) {
uint32_t value = 0; uint32_t value = 0;
@ -93,17 +70,80 @@ namespace dawn_native { namespace metal {
entry, kIOServicePlane, name, kCFAllocatorDefault, entry, kIOServicePlane, name, kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)); kIORegistryIterateRecursively | kIORegistryIterateParents));
if (data != nullptr) { if (data == nullptr) {
const uint32_t* valuePtr = return value;
reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data));
if (valuePtr) {
value = *valuePtr;
} }
// CFDataGetBytePtr() is guaranteed to return a read-only pointer
value = *reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data));
CFRelease(data); CFRelease(data);
return value;
} }
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() { bool IsMetalSupported() {
@ -120,16 +160,12 @@ namespace dawn_native { namespace metal {
Adapter(InstanceBase* instance, id<MTLDevice> device) Adapter(InstanceBase* instance, id<MTLDevice> device)
: AdapterBase(instance, BackendType::Metal), mDevice([device retain]) { : AdapterBase(instance, BackendType::Metal), mDevice([device retain]) {
mPCIInfo.name = std::string([mDevice.name UTF8String]); 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. PCIIDs ids;
// TODO(cwallez@chromium.org): Once Chromium has the macOS 10.13 SDK rolled, we if (!instance->ConsumedError(GetDevicePCIInfo(device, &ids))) {
// should use MTLDevice.registryID to gather the information. mPCIInfo.vendorId = ids.vendorId;
io_registry_entry_t entry = GetDisplayIOServicePort(); mPCIInfo.deviceId = ids.deviceId;
if (entry != IO_OBJECT_NULL) { };
mPCIInfo.vendorId = GetEntryProperty(entry, CFSTR("vendor-id"));
mPCIInfo.deviceId = GetEntryProperty(entry, CFSTR("device-id"));
IOObjectRelease(entry);
}
if ([device isLowPower]) { if ([device isLowPower]) {
mDeviceType = DeviceType::IntegratedGPU; mDeviceType = DeviceType::IntegratedGPU;