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 = {};
     };