blob: 7e1be7974323dbdbe6580c8091cb44bd133bd51c [file] [log] [blame]
// Copyright 2019 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_native/metal/BackendMTL.h"
#include "dawn_native/Instance.h"
#include "dawn_native/MetalBackend.h"
#include "dawn_native/metal/DeviceMTL.h"
#include <IOKit/graphics/IOGraphicsLib.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;
// Create matching dictionary for display service
CFMutableDictionaryRef matchingDict = IOServiceMatching("IODisplayConnect");
if (matchingDict == nullptr) {
return 0;
}
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;
}
// 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;
}
}
CFRelease(displayInfo);
IOObjectRelease(serv);
}
IOObjectRelease(iter);
return servicePort;
}
// Get integer property from registry entry.
uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) {
uint32_t value = 0;
// Recursively search registry entry and its parents for property name
// The data should release with CFRelease
CFDataRef data = static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
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);
}
return value;
}
bool IsMetalSupported() {
// Metal was first introduced in macOS 10.11
NSOperatingSystemVersion macOS10_11 = {10, 11, 0};
return [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:macOS10_11];
}
} // anonymous namespace
// The Metal backend's Adapter.
class Adapter : public AdapterBase {
public:
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);
}
if ([device isLowPower]) {
mDeviceType = DeviceType::IntegratedGPU;
} else {
mDeviceType = DeviceType::DiscreteGPU;
}
}
~Adapter() override {
[mDevice release];
}
private:
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
return {new Device(this, mDevice, descriptor)};
}
id<MTLDevice> mDevice = nil;
};
// Implementation of the Metal backend's BackendConnection
Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Metal) {
if (GetInstance()->IsBackendValidationEnabled()) {
setenv("METAL_DEVICE_WRAPPER_TYPE", "1", 1);
}
}
std::vector<std::unique_ptr<AdapterBase>> Backend::DiscoverDefaultAdapters() {
NSArray<id<MTLDevice>>* devices = MTLCopyAllDevices();
std::vector<std::unique_ptr<AdapterBase>> adapters;
for (id<MTLDevice> device in devices) {
adapters.push_back(std::make_unique<Adapter>(GetInstance(), device));
}
[devices release];
return adapters;
}
BackendConnection* Connect(InstanceBase* instance) {
if (!IsMetalSupported()) {
return nullptr;
}
return new Backend(instance);
}
}} // namespace dawn_native::metal