From 978fa65a2cc5e73f70b4d5c4021e49654b7c61b1 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Tue, 5 Feb 2019 13:02:30 +0000 Subject: [PATCH] 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 Reviewed-by: Kai Ninomiya --- BUILD.gn | 2 + src/dawn_native/metal/BackendMTL.h | 31 +++++ src/dawn_native/metal/BackendMTL.mm | 161 +++++++++++++++++++++++++ src/dawn_native/metal/DeviceMTL.h | 7 +- src/dawn_native/metal/DeviceMTL.mm | 122 +------------------ src/dawn_native/metal/MetalBackend.mm | 4 - src/include/dawn_native/MetalBackend.h | 5 +- src/utils/MetalBinding.mm | 21 +++- 8 files changed, 220 insertions(+), 133 deletions(-) create mode 100644 src/dawn_native/metal/BackendMTL.h create mode 100644 src/dawn_native/metal/BackendMTL.mm diff --git a/BUILD.gn b/BUILD.gn index 0cac20f881..b5f41467a5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -568,6 +568,8 @@ source_set("libdawn_native_sources") { "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 0000000000..fe8df5e27b --- /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> 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 0000000000..9e89ed1818 --- /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 + +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(IORegistryEntrySearchCFProperty( + entry, kIOServicePlane, name, kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents)); + + if (data != nullptr) { + const uint32_t* valuePtr = + reinterpret_cast(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 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 CreateDeviceImpl() override { + return {new Device(this, mDevice)}; + } + + id mDevice = nil; + }; + + // Implementation of the Metal backend's BackendConnection + + Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Metal) { + } + + std::vector> Backend::DiscoverDefaultAdapters() { + NSArray>* devices = MTLCopyAllDevices(); + + std::vector> adapters; + for (id device in devices) { + adapters.push_back(std::make_unique(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 d250e909a6..1325dd4d57 100644 --- a/src/dawn_native/metal/DeviceMTL.h +++ b/src/dawn_native/metal/DeviceMTL.h @@ -34,7 +34,7 @@ namespace dawn_native { namespace metal { class Device : public DeviceBase { public: - Device(); + Device(AdapterBase* adapter, id mtlDevice); ~Device(); CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override; @@ -47,8 +47,6 @@ namespace dawn_native { namespace metal { Serial GetLastSubmittedCommandSerial() const final override; void TickImpl() override; - const dawn_native::PCIInfo& GetPCIInfo() const override; - id GetMTLDevice(); id GetPendingCommandBuffer(); @@ -85,7 +83,6 @@ namespace dawn_native { namespace metal { ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; - void CollectPCIInfo(); void OnCompletedHandler(); @@ -97,8 +94,6 @@ namespace dawn_native { namespace metal { Serial mCompletedSerial = 0; Serial mLastSubmittedSerial = 0; id 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 f6b4ab45e5..8e2ec5b03c 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 -#include - 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(IORegistryEntrySearchCFProperty( - entry, kIOServicePlane, name, kCFAllocatorDefault, - kIORegistryIterateRecursively | kIORegistryIterateParents)); - - if (data != nullptr) { - const uint32_t* valuePtr = - reinterpret_cast(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) + : 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 @@ namespace dawn_native { namespace metal { mMapTracker = nullptr; mResourceUploader = nullptr; - [mMtlDevice release]; - mMtlDevice = nil; - [mCommandQueue release]; mCommandQueue = nil; + + [mMtlDevice release]; + mMtlDevice = nil; } ResultOrError Device::CreateBindGroupImpl( @@ -243,10 +148,6 @@ namespace dawn_native { namespace metal { } } - const dawn_native::PCIInfo& Device::GetPCIInfo() const { - return mPCIInfo; - } - id Device::GetMTLDevice() { return mMtlDevice; } @@ -287,17 +188,6 @@ namespace dawn_native { namespace metal { 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> 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 b90a4d511b..d8e3eaa46d 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(new Device()); - } - id GetMetalDevice(dawnDevice cDevice) { Device* device = reinterpret_cast(cDevice); return device->GetMTLDevice(); diff --git a/src/include/dawn_native/MetalBackend.h b/src/include/dawn_native/MetalBackend.h index aad945a7b6..fdca226142 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 #include -#include +#include #import -#import namespace dawn_native { namespace metal { - DAWN_NATIVE_EXPORT dawnDevice CreateDevice(); DAWN_NATIVE_EXPORT id GetMetalDevice(dawnDevice device); }} // namespace dawn_native::metal diff --git a/src/utils/MetalBinding.mm b/src/utils/MetalBinding.mm index 55558eda02..51372533d6 100644 --- a/src/utils/MetalBinding.mm +++ b/src/utils/MetalBinding.mm @@ -22,6 +22,8 @@ #include "GLFW/glfw3.h" #include "GLFW/glfw3native.h" +#import + namespace utils { class SwapChainImplMTL { public: @@ -113,9 +115,21 @@ namespace utils { } 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(); + mInstance->DiscoverDefaultAdapters(); + + std::vector 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 @@ namespace utils { } private: + std::unique_ptr mInstance; id mMetalDevice = nil; dawnSwapChainImplementation mSwapchainImpl = {}; };