Report GPU arch on Metal devices with no vendorID

This change attempts to better classify devices with a Metal backend
that aren't currently reporting vendor/device ID. In practice this
mostly means Apple-produced GPUs, like the M1 series.

Bug: dawn:1443
Change-Id: I9e8467a50c9f8eeccc00863f6dee32c0f91380dd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/92123
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Brandon Jones <bajones@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/common/BUILD.gn b/src/dawn/common/BUILD.gn
index a9ecded..c843e86 100644
--- a/src/dawn/common/BUILD.gn
+++ b/src/dawn/common/BUILD.gn
@@ -192,7 +192,7 @@
   script = "${dawn_root}/generator/dawn_gpu_info_generator.py"
   args = [
     "--gpu-info-json",
-    rebase_path("${dawn_root}/gpu_info.json", root_build_dir),
+    rebase_path("${dawn_root}/src/dawn/gpu_info.json", root_build_dir),
   ]
   outputs = [
     "src/dawn/common/GPUInfo_autogen.h",
diff --git a/src/dawn/common/CMakeLists.txt b/src/dawn/common/CMakeLists.txt
index 7e1c373..a2741d1 100644
--- a/src/dawn/common/CMakeLists.txt
+++ b/src/dawn/common/CMakeLists.txt
@@ -24,7 +24,7 @@
     SCRIPT "${Dawn_SOURCE_DIR}/generator/dawn_gpu_info_generator.py"
     PRINT_NAME "Dawn GPU info utilities"
     ARGS "--gpu-info-json"
-         "${Dawn_SOURCE_DIR}/gpu_info.json"
+         "${Dawn_SOURCE_DIR}/src/dawn/gpu_info.json"
     RESULT_VARIABLE "DAWN_GPU_INFO_AUTOGEN_SOURCES"
 )
 
diff --git a/gpu_info.json b/src/dawn/gpu_info.json
similarity index 86%
rename from gpu_info.json
rename to src/dawn/gpu_info.json
index d969962..6a1ddc0 100644
--- a/gpu_info.json
+++ b/src/dawn/gpu_info.json
@@ -33,7 +33,15 @@
     },
 
     "Apple": {
-      "id": "0x106b"
+      "id": "0x106b",
+
+      "_comment": [
+        "Apple GPUs do not report a DeviceID via the Metal API, and as such the typical device",
+        "pattern matching does not work for them. The recommended approach is to find the highest",
+        "supported 'common' family supported and report it as the architecture.",
+        "Examples: 'common-1', 'common-3'",
+        "https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/detecting_gpu_features_and_metal_software_versions"
+      ]
     },
 
     "ARM": {
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 53580aa..b1c015d 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -33,6 +33,8 @@
 
 MaybeError AdapterBase::Initialize() {
     DAWN_TRY_CONTEXT(InitializeImpl(), "initializing adapter (backend=%s)", mBackend);
+    InitializeVendorArchitectureImpl();
+
     DAWN_TRY_CONTEXT(
         InitializeSupportedFeaturesImpl(),
         "gathering supported features for \"%s\" - \"%s\" (vendorId=%#06x deviceId=%#06x "
@@ -44,9 +46,6 @@
         "backend=%s type=%s)",
         mName, mDriverDescription, mVendorId, mDeviceId, mBackend, mAdapterType);
 
-    mVendorName = gpu_info::GetVendorName(mVendorId);
-    mArchitectureName = gpu_info::GetArchitectureName(mVendorId, mDeviceId);
-
     // Enforce internal Dawn constants.
     mLimits.v1.maxVertexBufferArrayStride =
         std::min(mLimits.v1.maxVertexBufferArrayStride, kMaxVertexBufferArrayStride);
@@ -138,6 +137,11 @@
     callback(status, ToAPI(device.Detach()), nullptr, userdata);
 }
 
+void AdapterBase::InitializeVendorArchitectureImpl() {
+    mVendorName = gpu_info::GetVendorName(mVendorId);
+    mArchitectureName = gpu_info::GetArchitectureName(mVendorId, mDeviceId);
+}
+
 uint32_t AdapterBase::GetVendorId() const {
     return mVendorId;
 }
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index a02e77c..ba69b38 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -86,6 +86,8 @@
     // Check base WebGPU limits and populate supported limits.
     virtual MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) = 0;
 
+    virtual void InitializeVendorArchitectureImpl();
+
     ResultOrError<Ref<DeviceBase>> CreateDeviceInternal(const DeviceDescriptor* descriptor);
 
     virtual MaybeError ResetInternalDeviceForTestingImpl();
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 4d5d854..e142b2b 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -47,11 +47,10 @@
 };
 
 #if DAWN_PLATFORM_IS(MACOS)
-const Vendor kVendors[] = {{"AMD", gpu_info::kVendorID_AMD},
-                           {"Radeon", gpu_info::kVendorID_AMD},
-                           {"Intel", gpu_info::kVendorID_Intel},
-                           {"Geforce", gpu_info::kVendorID_Nvidia},
-                           {"Quadro", gpu_info::kVendorID_Nvidia}};
+const Vendor kVendors[] = {
+    {"AMD", gpu_info::kVendorID_AMD},        {"Apple", gpu_info::kVendorID_Apple},
+    {"Radeon", gpu_info::kVendorID_AMD},     {"Intel", gpu_info::kVendorID_Intel},
+    {"Geforce", gpu_info::kVendorID_Nvidia}, {"Quadro", gpu_info::kVendorID_Nvidia}};
 
 // Find vendor ID from MTLDevice name.
 MaybeError GetVendorIdFromVendors(id<MTLDevice> device, PCIIDs* ids) {
@@ -147,10 +146,16 @@
     // [device registryID] is introduced on macOS 10.13+, otherwise workaround to get vendor
     // id by vendor name on old macOS
     if (@available(macos 10.13, *)) {
-        return GetDeviceIORegistryPCIInfo(device, ids);
-    } else {
-        return GetVendorIdFromVendors(device, ids);
+        auto result = GetDeviceIORegistryPCIInfo(device, ids);
+        if (result.IsError()) {
+            dawn::WarningLog() << "GetDeviceIORegistryPCIInfo failed: "
+                               << result.AcquireError()->GetFormattedMessage();
+        } else if (ids->vendorId != 0) {
+            return result;
+        }
     }
+
+    return GetVendorIdFromVendors(device, ids);
 }
 
 bool IsMetalSupported() {
@@ -391,6 +396,33 @@
         return {};
     }
 
+    void InitializeVendorArchitectureImpl() override {
+        if (@available(macOS 10.15, iOS 13.0, *)) {
+            // According to Apple's documentation:
+            // https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/detecting_gpu_features_and_metal_software_versions
+            // - "Use the Common family to create apps that target a range of GPUs on multiple
+            //   platforms.""
+            // - "A GPU can be a member of more than one family; in most cases, a GPU supports one
+            //   of the Common families and then one or more families specific to the build target."
+            // So we'll use the highest supported common family as the reported "architecture" on
+            // devices where a deviceID isn't available.
+            if (mDeviceId == 0) {
+                if ([*mDevice supportsFamily:MTLGPUFamilyCommon3]) {
+                    mArchitectureName = "common-3";
+                } else if ([*mDevice supportsFamily:MTLGPUFamilyCommon2]) {
+                    mArchitectureName = "common-2";
+                } else if ([*mDevice supportsFamily:MTLGPUFamilyCommon1]) {
+                    mArchitectureName = "common-1";
+                }
+            }
+        }
+
+        mVendorName = gpu_info::GetVendorName(mVendorId);
+        if (mDeviceId != 0) {
+            mArchitectureName = gpu_info::GetArchitectureName(mVendorId, mDeviceId);
+        }
+    };
+
     enum class MTLGPUFamily {
         Apple1,
         Apple2,
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index c88ccf9..49386e8 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -649,6 +649,11 @@
             << ", backend: " << ParamName(properties.backendType) << "\n"
             << "   vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str()
             << (properties.selected ? " [Selected]" : "") << "\n";
+
+        if (strlen(properties.vendorName) || strlen(properties.architecture)) {
+            log << "   vendorName: " << properties.vendorName
+                << ", architecture: " << properties.architecture << "\n";
+        }
     }
 }