Metal: Implement the backend connection and adapter.
BUG=dawn:29
Change-Id: Idaca7d2f8ac52d5f46d8030571b5e2da3a573a97
Reviewed-on: https://dawn-review.googlesource.com/c/3940
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 0cac20f..b5f4146 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -568,6 +568,8 @@
"IOKit.framework",
]
sources += [
+ "src/dawn_native/metal/BackendMTL.h",
+ "src/dawn_native/metal/BackendMTL.mm",
"src/dawn_native/metal/BufferMTL.h",
"src/dawn_native/metal/BufferMTL.mm",
"src/dawn_native/metal/CommandBufferMTL.h",
diff --git a/src/dawn_native/metal/BackendMTL.h b/src/dawn_native/metal/BackendMTL.h
new file mode 100644
index 0000000..fe8df5e
--- /dev/null
+++ b/src/dawn_native/metal/BackendMTL.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef DAWNNATIVE_METAL_BACKENDMTL_H_
+#define DAWNNATIVE_METAL_BACKENDMTL_H_
+
+#include "dawn_native/BackendConnection.h"
+
+namespace dawn_native { namespace metal {
+
+ class Backend : public BackendConnection {
+ public:
+ Backend(InstanceBase* instance);
+
+ std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() override;
+ };
+
+}} // namespace dawn_native::metal
+
+#endif // DAWNNATIVE_METAL_BACKENDMTL_H_
diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm
new file mode 100644
index 0000000..9e89ed1
--- /dev/null
+++ b/src/dawn_native/metal/BackendMTL.mm
@@ -0,0 +1,161 @@
+// 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/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;
+ }
+ } // 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);
+ }
+ }
+
+ ~Adapter() override {
+ [mDevice release];
+ }
+
+ private:
+ ResultOrError<DeviceBase*> CreateDeviceImpl() override {
+ return {new Device(this, mDevice)};
+ }
+
+ id<MTLDevice> mDevice = nil;
+ };
+
+ // Implementation of the Metal backend's BackendConnection
+
+ Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Metal) {
+ }
+
+ 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) {
+ return new Backend(instance);
+ }
+
+}} // namespace dawn_native::metal
diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h
index d250e90..1325dd4 100644
--- a/src/dawn_native/metal/DeviceMTL.h
+++ b/src/dawn_native/metal/DeviceMTL.h
@@ -34,7 +34,7 @@
class Device : public DeviceBase {
public:
- Device();
+ Device(AdapterBase* adapter, id<MTLDevice> mtlDevice);
~Device();
CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
@@ -47,8 +47,6 @@
Serial GetLastSubmittedCommandSerial() const final override;
void TickImpl() override;
- const dawn_native::PCIInfo& GetPCIInfo() const override;
-
id<MTLDevice> GetMTLDevice();
id<MTLCommandBuffer> GetPendingCommandBuffer();
@@ -85,7 +83,6 @@
ResultOrError<TextureViewBase*> CreateTextureViewImpl(
TextureBase* texture,
const TextureViewDescriptor* descriptor) override;
- void CollectPCIInfo();
void OnCompletedHandler();
@@ -97,8 +94,6 @@
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
id<MTLCommandBuffer> mPendingCommands = nil;
-
- dawn_native::PCIInfo mPCIInfo;
};
}} // namespace dawn_native::metal
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index f6b4ab4..8e2ec5b 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -32,110 +32,15 @@
#include "dawn_native/metal/SwapChainMTL.h"
#include "dawn_native/metal/TextureMTL.h"
-#include <IOKit/graphics/IOGraphicsLib.h>
-#include <unistd.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;
- }
- } // anonymous namespace
-
- BackendConnection* Connect(InstanceBase* instance) {
- return nullptr;
- }
-
- // Device
-
- Device::Device()
- : DeviceBase(nullptr),
- mMtlDevice(MTLCreateSystemDefaultDevice()),
+ Device::Device(AdapterBase* adapter, id<MTLDevice> mtlDevice)
+ : DeviceBase(adapter),
+ mMtlDevice([mtlDevice retain]),
mMapTracker(new MapRequestTracker(this)),
mResourceUploader(new ResourceUploader(this)) {
[mMtlDevice retain];
mCommandQueue = [mMtlDevice newCommandQueue];
- CollectPCIInfo();
}
Device::~Device() {
@@ -155,11 +60,11 @@
mMapTracker = nullptr;
mResourceUploader = nullptr;
- [mMtlDevice release];
- mMtlDevice = nil;
-
[mCommandQueue release];
mCommandQueue = nil;
+
+ [mMtlDevice release];
+ mMtlDevice = nil;
}
ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl(
@@ -243,10 +148,6 @@
}
}
- const dawn_native::PCIInfo& Device::GetPCIInfo() const {
- return mPCIInfo;
- }
-
id<MTLDevice> Device::GetMTLDevice() {
return mMtlDevice;
}
@@ -287,17 +188,6 @@
return mResourceUploader.get();
}
- void Device::CollectPCIInfo() {
- 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);
- }
-
- mPCIInfo.name = std::string([mMtlDevice.name UTF8String]);
- }
-
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
return DAWN_UNIMPLEMENTED_ERROR("Device unable to create staging buffer.");
}
diff --git a/src/dawn_native/metal/MetalBackend.mm b/src/dawn_native/metal/MetalBackend.mm
index b90a4d5..d8e3eaa 100644
--- a/src/dawn_native/metal/MetalBackend.mm
+++ b/src/dawn_native/metal/MetalBackend.mm
@@ -21,10 +21,6 @@
namespace dawn_native { namespace metal {
- dawnDevice CreateDevice() {
- return reinterpret_cast<dawnDevice>(new Device());
- }
-
id<MTLDevice> GetMetalDevice(dawnDevice cDevice) {
Device* device = reinterpret_cast<Device*>(cDevice);
return device->GetMTLDevice();
diff --git a/src/include/dawn_native/MetalBackend.h b/src/include/dawn_native/MetalBackend.h
index aad945a..fdca226 100644
--- a/src/include/dawn_native/MetalBackend.h
+++ b/src/include/dawn_native/MetalBackend.h
@@ -15,15 +15,12 @@
#ifndef DAWNNATIVE_METALBACKEND_H_
#define DAWNNATIVE_METALBACKEND_H_
-#include <dawn/dawn.h>
#include <dawn/dawn_wsi.h>
-#include <dawn_native/dawn_native_export.h>
+#include <dawn_native/DawnNative.h>
#import <Metal/Metal.h>
-#import <QuartzCore/CAMetalLayer.h>
namespace dawn_native { namespace metal {
- DAWN_NATIVE_EXPORT dawnDevice CreateDevice();
DAWN_NATIVE_EXPORT id<MTLDevice> GetMetalDevice(dawnDevice device);
}} // namespace dawn_native::metal
diff --git a/src/utils/MetalBinding.mm b/src/utils/MetalBinding.mm
index 55558ed..5137253 100644
--- a/src/utils/MetalBinding.mm
+++ b/src/utils/MetalBinding.mm
@@ -22,6 +22,8 @@
#include "GLFW/glfw3.h"
#include "GLFW/glfw3native.h"
+#import <QuartzCore/CAMetalLayer.h>
+
namespace utils {
class SwapChainImplMTL {
public:
@@ -113,9 +115,21 @@
}
dawnDevice CreateDevice() override {
- dawnDevice device = dawn_native::metal::CreateDevice();
- mMetalDevice = dawn_native::metal::GetMetalDevice(device);
- return device;
+ // Make an instance and find a Metal adapter
+ mInstance = std::make_unique<dawn_native::Instance>();
+ mInstance->DiscoverDefaultAdapters();
+
+ std::vector<dawn_native::Adapter> adapters = mInstance->GetAdapters();
+ for (dawn_native::Adapter adapter : adapters) {
+ if (adapter.GetBackendType() == dawn_native::BackendType::Metal) {
+ dawnDevice device = adapter.CreateDevice();
+ mMetalDevice = dawn_native::metal::GetMetalDevice(device);
+ return device;
+ }
+ }
+
+ UNREACHABLE();
+ return {};
}
uint64_t GetSwapChainImplementation() override {
@@ -131,6 +145,7 @@
}
private:
+ std::unique_ptr<dawn_native::Instance> mInstance;
id<MTLDevice> mMetalDevice = nil;
dawnSwapChainImplementation mSwapchainImpl = {};
};