Disable Vulkan backend on old Intel Windows driver version

Skip enabling Vulkan backend on Intel Windows driver version < 30.0.101.2111 due to many flaky issue.

Bug: chromium:1338622, dawn:1392
Change-Id: I6975783bdc18d8a94d6c35e134756e3713833a29
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105741
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
diff --git a/src/dawn/common/GPUInfo.cpp b/src/dawn/common/GPUInfo.cpp
index 8a6f75d..4b80876 100644
--- a/src/dawn/common/GPUInfo.cpp
+++ b/src/dawn/common/GPUInfo.cpp
@@ -15,6 +15,9 @@
 #include "dawn/common/GPUInfo.h"
 
 #include <algorithm>
+#include <array>
+#include <iterator>
+#include <sstream>
 
 #include "dawn/common/Assert.h"
 
@@ -33,18 +36,52 @@
 // last two fields.
 // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html for
 // more details.
-uint32_t GetIntelD3DDriverBuildNumber(const D3DDriverVersion& driverVersion) {
-    return driverVersion[2] * 10000 + driverVersion[3];
+uint32_t GetIntelWindowsDriverBuildNumber(const DriverVersion& driverVersion) {
+    size_t size = driverVersion.size();
+    ASSERT(size >= 2);
+    return driverVersion[size - 2] * 10000 + driverVersion[size - 1];
 }
 
 }  // anonymous namespace
 
-int CompareD3DDriverVersion(PCIVendorID vendorId,
-                            const D3DDriverVersion& version1,
-                            const D3DDriverVersion& version2) {
+DriverVersion::DriverVersion() = default;
+
+DriverVersion::DriverVersion(const std::initializer_list<uint16_t>& version) {
+    ASSERT(version.size() <= kMaxVersionFields);
+    mDriverVersion->assign(version.begin(), version.end());
+}
+
+uint16_t& DriverVersion::operator[](size_t i) {
+    return mDriverVersion->operator[](i);
+}
+
+const uint16_t& DriverVersion::operator[](size_t i) const {
+    return mDriverVersion->operator[](i);
+}
+
+uint32_t DriverVersion::size() const {
+    return mDriverVersion->size();
+}
+
+std::string DriverVersion::ToString() const {
+    std::ostringstream oss;
+    if (mDriverVersion->size() > 0) {
+        // Convert all but the last element to avoid a trailing "."
+        std::copy(mDriverVersion->begin(), mDriverVersion->end() - 1,
+                  std::ostream_iterator<uint16_t>(oss, "."));
+        // Add the last element
+        oss << mDriverVersion->back();
+    }
+
+    return oss.str();
+}
+
+int CompareWindowsDriverVersion(PCIVendorID vendorId,
+                                const DriverVersion& version1,
+                                const DriverVersion& version2) {
     if (IsIntel(vendorId)) {
-        uint32_t buildNumber1 = GetIntelD3DDriverBuildNumber(version1);
-        uint32_t buildNumber2 = GetIntelD3DDriverBuildNumber(version2);
+        uint32_t buildNumber1 = GetIntelWindowsDriverBuildNumber(version1);
+        uint32_t buildNumber2 = GetIntelWindowsDriverBuildNumber(version2);
         return buildNumber1 < buildNumber2 ? -1 : (buildNumber1 == buildNumber2 ? 0 : 1);
     }
 
diff --git a/src/dawn/common/GPUInfo.h b/src/dawn/common/GPUInfo.h
index a96b907..855d05c 100644
--- a/src/dawn/common/GPUInfo.h
+++ b/src/dawn/common/GPUInfo.h
@@ -15,22 +15,42 @@
 #ifndef SRC_DAWN_COMMON_GPUINFO_H_
 #define SRC_DAWN_COMMON_GPUINFO_H_
 
-#include "dawn/common/GPUInfo_autogen.h"
+#include <string>
 
-#include <array>
+#include "dawn/common/GPUInfo_autogen.h"
+#include "dawn/common/StackContainer.h"
 
 namespace gpu_info {
 
-using D3DDriverVersion = std::array<uint16_t, 4>;
+// Four uint16 fields could cover almost all driver version schemas:
+// D3D12: AA.BB.CCC.DDDD
+// Vulkan: AAA.BBB.CCC.DDD on Nvidia, CCC.DDDD for Intel Windows, and AA.BB.CCC for others,
+// See https://vulkan.gpuinfo.org/
+static constexpr uint32_t kMaxVersionFields = 4;
+
+class DriverVersion {
+  public:
+    DriverVersion();
+    DriverVersion(const std::initializer_list<uint16_t>& version);
+
+    uint16_t& operator[](size_t i);
+    const uint16_t& operator[](size_t i) const;
+
+    uint32_t size() const;
+    std::string ToString() const;
+
+  private:
+    StackVector<uint16_t, kMaxVersionFields> mDriverVersion;
+};
 
 // Do comparison between two driver versions. Currently we only support the comparison between
-// Intel D3D driver versions.
+// Intel Windows driver versions.
 // - Return -1 if build number of version1 is smaller
 // - Return 1 if build number of version1 is bigger
 // - Return 0 if version1 and version2 represent same driver version
-int CompareD3DDriverVersion(PCIVendorID vendorId,
-                            const D3DDriverVersion& version1,
-                            const D3DDriverVersion& version2);
+int CompareWindowsDriverVersion(PCIVendorID vendorId,
+                                const DriverVersion& version1,
+                                const DriverVersion& version2);
 
 // Intel architectures
 bool IsSkylake(PCIDeviceID deviceId);
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 2c3fafb..426b284 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -169,6 +169,10 @@
     return mDeviceId;
 }
 
+const gpu_info::DriverVersion& AdapterBase::GetDriverVersion() const {
+    return mDriverVersion;
+}
+
 wgpu::BackendType AdapterBase::GetBackendType() const {
     return mBackend;
 }
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index 8bef321..67b4e88 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -19,6 +19,7 @@
 
 #include "dawn/native/DawnNative.h"
 
+#include "dawn/common/GPUInfo.h"
 #include "dawn/common/RefCounted.h"
 #include "dawn/common/ityp_span.h"
 #include "dawn/native/Error.h"
@@ -50,6 +51,7 @@
 
     uint32_t GetVendorId() const;
     uint32_t GetDeviceId() const;
+    const gpu_info::DriverVersion& GetDriverVersion() const;
     wgpu::BackendType GetBackendType() const;
     InstanceBase* GetInstance() const;
 
@@ -72,6 +74,7 @@
     uint32_t mDeviceId = 0xFFFFFFFF;
     std::string mName;
     wgpu::AdapterType mAdapterType = wgpu::AdapterType::Unknown;
+    gpu_info::DriverVersion mDriverVersion;
     std::string mDriverDescription;
 
     // Features set that CAN be supported by devices of this adapter. Some features in this set may
diff --git a/src/dawn/native/d3d12/AdapterD3D12.cpp b/src/dawn/native/d3d12/AdapterD3D12.cpp
index 2e749de..0b9d284 100644
--- a/src/dawn/native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn/native/d3d12/AdapterD3D12.cpp
@@ -14,7 +14,7 @@
 
 #include "dawn/native/d3d12/AdapterD3D12.h"
 
-#include <sstream>
+#include <string>
 
 #include "dawn/common/Constants.h"
 #include "dawn/common/WindowsUtils.h"
@@ -57,10 +57,6 @@
     return mD3d12Device;
 }
 
-const gpu_info::D3DDriverVersion& Adapter::GetDriverVersion() const {
-    return mDriverVersion;
-}
-
 MaybeError Adapter::InitializeImpl() {
     // D3D12 cannot check for feature support without a device.
     // Create the device to populate the adapter properties then reuse it when needed for actual
@@ -94,14 +90,12 @@
     if (mHardwareAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umdVersion) !=
         DXGI_ERROR_UNSUPPORTED) {
         uint64_t encodedVersion = umdVersion.QuadPart;
-
-        std::ostringstream o;
-        o << "D3D12 driver version ";
-        for (size_t i = 0; i < mDriverVersion.size(); ++i) {
-            mDriverVersion[i] = (encodedVersion >> (48 - 16 * i)) & 0xFFFF;
-            o << mDriverVersion[i] << ".";
-        }
-        mDriverDescription = o.str();
+        uint16_t mask = 0xFFFF;
+        mDriverVersion = {static_cast<uint16_t>((encodedVersion >> 48) & mask),
+                          static_cast<uint16_t>((encodedVersion >> 32) & mask),
+                          static_cast<uint16_t>((encodedVersion >> 16) & mask),
+                          static_cast<uint16_t>(encodedVersion & mask)};
+        mDriverDescription = std::string("D3D12 driver version ") + mDriverVersion.ToString();
     }
 
     return {};
diff --git a/src/dawn/native/d3d12/AdapterD3D12.h b/src/dawn/native/d3d12/AdapterD3D12.h
index 2c8a377..aa3538c 100644
--- a/src/dawn/native/d3d12/AdapterD3D12.h
+++ b/src/dawn/native/d3d12/AdapterD3D12.h
@@ -17,7 +17,6 @@
 
 #include "dawn/native/Adapter.h"
 
-#include "dawn/common/GPUInfo.h"
 #include "dawn/native/d3d12/D3D12Info.h"
 #include "dawn/native/d3d12/d3d12_platform.h"
 
@@ -37,7 +36,6 @@
     IDXGIAdapter3* GetHardwareAdapter() const;
     Backend* GetBackend() const;
     ComPtr<ID3D12Device> GetDevice() const;
-    const gpu_info::D3DDriverVersion& GetDriverVersion() const;
 
   private:
     ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(
@@ -60,7 +58,6 @@
 
     ComPtr<IDXGIAdapter3> mHardwareAdapter;
     ComPtr<ID3D12Device> mD3d12Device;
-    gpu_info::D3DDriverVersion mDriverVersion;
 
     Backend* mBackend;
     D3D12DeviceInfo mDeviceInfo = {};
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp
index f01915d..e8c6fe1 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -688,9 +688,9 @@
     // Currently this workaround is only needed on Intel Gen9, Gen9.5 and Gen11 GPUs.
     // See http://crbug.com/1161355 for more information.
     if (gpu_info::IsIntelGen9(vendorId, deviceId) || gpu_info::IsIntelGen11(vendorId, deviceId)) {
-        constexpr gpu_info::D3DDriverVersion kFixedDriverVersion = {31, 0, 101, 2114};
-        if (gpu_info::CompareD3DDriverVersion(vendorId, ToBackend(GetAdapter())->GetDriverVersion(),
-                                              kFixedDriverVersion) < 0) {
+        const gpu_info::DriverVersion kFixedDriverVersion = {31, 0, 101, 2114};
+        if (gpu_info::CompareWindowsDriverVersion(vendorId, GetAdapter()->GetDriverVersion(),
+                                                  kFixedDriverVersion) < 0) {
             SetToggle(
                 Toggle::UseTempBufferInSmallFormatTextureToTextureCopyFromGreaterToLessMipLevel,
                 true);
@@ -721,9 +721,9 @@
     // This workaround is only needed on Intel Gen12LP with driver prior to 30.0.101.1692.
     // See http://crbug.com/dawn/949 for more information.
     if (gpu_info::IsIntelGen12LP(vendorId, deviceId)) {
-        constexpr gpu_info::D3DDriverVersion kFixedDriverVersion = {30, 0, 101, 1692};
-        if (gpu_info::CompareD3DDriverVersion(vendorId, ToBackend(GetAdapter())->GetDriverVersion(),
-                                              kFixedDriverVersion) == -1) {
+        const gpu_info::DriverVersion kFixedDriverVersion = {30, 0, 101, 1692};
+        if (gpu_info::CompareWindowsDriverVersion(vendorId, GetAdapter()->GetDriverVersion(),
+                                                  kFixedDriverVersion) == -1) {
             SetToggle(Toggle::D3D12AllocateExtraMemoryFor2DArrayTexture, true);
         }
     }
diff --git a/src/dawn/native/vulkan/AdapterVk.cpp b/src/dawn/native/vulkan/AdapterVk.cpp
index b05f03e..be468e8 100644
--- a/src/dawn/native/vulkan/AdapterVk.cpp
+++ b/src/dawn/native/vulkan/AdapterVk.cpp
@@ -17,14 +17,45 @@
 #include <algorithm>
 #include <string>
 
+#include "dawn/common/GPUInfo.h"
 #include "dawn/native/Limits.h"
 #include "dawn/native/vulkan/BackendVk.h"
 #include "dawn/native/vulkan/DeviceVk.h"
 
-#include "dawn/common/GPUInfo.h"
-
 namespace dawn::native::vulkan {
 
+namespace {
+
+gpu_info::DriverVersion DecodeVulkanDriverVersion(uint32_t vendorID, uint32_t versionRaw) {
+    gpu_info::DriverVersion driverVersion;
+    switch (vendorID) {
+        case gpu_info::kVendorID_Nvidia:
+            driverVersion = {static_cast<uint16_t>((versionRaw >> 22) & 0x3FF),
+                             static_cast<uint16_t>((versionRaw >> 14) & 0x0FF),
+                             static_cast<uint16_t>((versionRaw >> 6) & 0x0FF),
+                             static_cast<uint16_t>(versionRaw & 0x003F)};
+            break;
+        case gpu_info::kVendorID_Intel:
+#if DAWN_PLATFORM_IS(WINDOWS)
+            // Windows Vulkan driver releases together with D3D driver, so they share the same
+            // version. But only CCC.DDDD is encoded in 32-bit driverVersion.
+            driverVersion = {static_cast<uint16_t>(versionRaw >> 14),
+                             static_cast<uint16_t>(versionRaw & 0x3FFF)};
+            break;
+#endif
+        default:
+            // Use Vulkan driver conversions for other vendors
+            driverVersion = {static_cast<uint16_t>(versionRaw >> 22),
+                             static_cast<uint16_t>((versionRaw >> 12) & 0x3FF),
+                             static_cast<uint16_t>(versionRaw & 0xFFF)};
+            break;
+    }
+
+    return driverVersion;
+}
+
+}  // anonymous namespace
+
 Adapter::Adapter(InstanceBase* instance,
                  VulkanInstance* vulkanInstance,
                  VkPhysicalDevice physicalDevice)
@@ -59,14 +90,35 @@
 MaybeError Adapter::InitializeImpl() {
     DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this));
 
+    mDriverVersion = DecodeVulkanDriverVersion(mDeviceInfo.properties.vendorID,
+                                               mDeviceInfo.properties.driverVersion);
+    const std::string driverVersionStr = mDriverVersion.ToString();
+
+#if DAWN_PLATFORM_IS(WINDOWS)
+    // Disable Vulkan adapter on Windows Intel driver < 30.0.101.2111 due to flaky
+    // issues.
+    const gpu_info::DriverVersion kDriverVersion({30, 0, 101, 2111});
+    if (gpu_info::IsIntel(mDeviceInfo.properties.vendorID) &&
+        gpu_info::CompareWindowsDriverVersion(mDeviceInfo.properties.vendorID, mDriverVersion,
+                                              kDriverVersion) == -1) {
+        return DAWN_FORMAT_INTERNAL_ERROR(
+            "Disable Intel Vulkan adapter on Windows driver version %s. See "
+            "https://crbug.com/1338622.",
+            driverVersionStr);
+    }
+#endif
+
     if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
         mDriverDescription = mDeviceInfo.driverProperties.driverName;
         if (mDeviceInfo.driverProperties.driverInfo[0] != '\0') {
             mDriverDescription += std::string(": ") + mDeviceInfo.driverProperties.driverInfo;
         }
+        // There may be no driver version in driverInfo.
+        if (mDriverDescription.find(driverVersionStr) == std::string::npos) {
+            mDriverDescription += std::string(" ") + driverVersionStr;
+        }
     } else {
-        mDriverDescription =
-            "Vulkan driver version: " + std::to_string(mDeviceInfo.properties.driverVersion);
+        mDriverDescription = std::string("Vulkan driver version ") + driverVersionStr;
     }
 
     mDeviceId = mDeviceInfo.properties.deviceID;
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index 7e18885..e7638e0 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -387,16 +387,6 @@
                 (properties.backendType == wgpu::BackendType::Null);
         }
 
-#if DAWN_PLATFORM_IS(WINDOWS)
-        if (selected && !mRunSuppressedTests &&
-            properties.backendType == wgpu::BackendType::Vulkan &&
-            gpu_info::IsIntel(properties.vendorID)) {
-            dawn::WarningLog()
-                << "Deselecting Windows Intel Vulkan adapter. See https://crbug.com/1338622.";
-            selected &= false;
-        }
-#endif
-
         // In Windows Remote Desktop sessions we may be able to discover multiple adapters that
         // have the same name and backend type. We will just choose one adapter from them in our
         // tests.
diff --git a/src/dawn/tests/unittests/GPUInfoTests.cpp b/src/dawn/tests/unittests/GPUInfoTests.cpp
index 42f12cb..be01489 100644
--- a/src/dawn/tests/unittests/GPUInfoTests.cpp
+++ b/src/dawn/tests/unittests/GPUInfoTests.cpp
@@ -18,14 +18,26 @@
 
 namespace {
 const PCIVendorID vendorID = 0x8086;
-const gpu_info::D3DDriverVersion version1 = {20, 19, 15, 5107};
-const gpu_info::D3DDriverVersion version2 = {21, 20, 16, 5077};
-const gpu_info::D3DDriverVersion version3 = {27, 20, 100, 9946};
-const gpu_info::D3DDriverVersion version4 = {27, 20, 101, 2003};
+// Intel D3D12
+const gpu_info::DriverVersion version1 = {20, 19, 15, 5107};
+const gpu_info::DriverVersion version2 = {21, 20, 16, 5077};
+const gpu_info::DriverVersion version3 = {27, 20, 100, 9946};
+const gpu_info::DriverVersion version4 = {27, 20, 101, 2003};
+// Intel Vulkan
+const gpu_info::DriverVersion version5 = {100, 9466};
+const gpu_info::DriverVersion version6 = {101, 3222};
+const gpu_info::DriverVersion version7 = {101, 3790};
+
 }  // anonymous namespace
 
-TEST(GPUInfo, CompareD3DDriverVersion) {
-    EXPECT_EQ(gpu_info::CompareD3DDriverVersion(vendorID, version1, version2), -1);
-    EXPECT_EQ(gpu_info::CompareD3DDriverVersion(vendorID, version2, version3), -1);
-    EXPECT_EQ(gpu_info::CompareD3DDriverVersion(vendorID, version3, version4), -1);
+TEST(GPUInfo, CompareWindowsDriverVersion) {
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version1, version2), -1);
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version2, version3), -1);
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version3, version4), -1);
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version5, version6), -1);
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version6, version7), -1);
+    // Windows Vulkan driver releases together with D3D12 driver, so they share the same version.
+    // Expect Intel D3D12 driver and Vulkan driver to be comparable.
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version3, version6), -1);
+    EXPECT_EQ(gpu_info::CompareWindowsDriverVersion(vendorID, version4, version7), -1);
 }