Vulkan: Disable suballocation for 2D textures in some situation on Intel Mesa

This patch disables resource sub-allocation for the 2D textures with
CopyDst or RenderAttachment usage on Intel Gen12 GPUs using Mesa
driver on Linux and ChromeOS because of the driver issues about rebinding a
VkDeviceMemory from a VkImage to another VkImage.

Bug: dawn:1688
Change-Id: I28bb01a2d641a9024330ed761d27e0145d6b8aad
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/124382
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/common/GPUInfo.cpp b/src/dawn/common/GPUInfo.cpp
index 4b80876..b46f242 100644
--- a/src/dawn/common/GPUInfo.cpp
+++ b/src/dawn/common/GPUInfo.cpp
@@ -90,6 +90,16 @@
     return 0;
 }
 
+int CompareIntelMesaDriverVersion(const DriverVersion& version1, const DriverVersion& version2) {
+    for (uint32_t i = 0; i < 3; ++i) {
+        int diff = static_cast<int32_t>(version1[i]) - static_cast<int32_t>(version2[i]);
+        if (diff != 0) {
+            return diff;
+        }
+    }
+    return 0;
+}
+
 // Intel GPUs
 bool IsSkylake(PCIDeviceID deviceId) {
     return std::find(Skylake.cbegin(), Skylake.cend(), deviceId) != Skylake.cend();
diff --git a/src/dawn/common/GPUInfo.h b/src/dawn/common/GPUInfo.h
index 855d05c..dab977f 100644
--- a/src/dawn/common/GPUInfo.h
+++ b/src/dawn/common/GPUInfo.h
@@ -52,6 +52,12 @@
                                 const DriverVersion& version1,
                                 const DriverVersion& version2);
 
+// Do comparison between two Intel Mesa driver versions.
+// - Return a negative number if build number of version1 is smaller
+// - Return a positive number if build number of version1 is bigger
+// - Return 0 if version1 and version2 represent same driver version
+int CompareIntelMesaDriverVersion(const DriverVersion& version1, const DriverVersion& version2);
+
 // Intel architectures
 bool IsSkylake(PCIDeviceID deviceId);
 
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index c2c105a..cd802ff 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -329,11 +329,11 @@
       "default on Qualcomm GPUs, which have been observed experiencing a driver crash in this "
       "situation.",
       "https://crbug.com/dawn/1564", ToggleStage::Device}},
-    {Toggle::D3D12Allocate2DTextureWithCopyDstOrRenderAttachmentAsCommittedResource,
-     {"d3d12_allocate_2d_texture_with_copy_dst_or_render_attachment_as_committed_resource",
-      "Allocate each 2D texture with CopyDst or RenderAttachment usage as committed resources "
-      "instead of placed resources. This toggle is enabled by default on D3D12 backends using "
-      "Intel Gen9.5 and Gen11 GPUs due to a driver issue on Intel D3D12 driver.",
+    {Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment,
+     {"disable_sub_allocation_for_2d_texture_with_copy_dst_or_render_attachment",
+      "Disable resource sub-allocation for the 2D texture with CopyDst or RenderAttachment usage. "
+      "This toggle is enabled by default on D3D12 backends using Intel Gen9.5 and Gen11 GPUs and "
+      "on Vulkan backends using Intel Gen12 GPUs due to Intel Mesa Vulkan and D3D12 driver issues.",
       "https://crbug.com/1237175", ToggleStage::Device}},
     {Toggle::MetalUseCombinedDepthStencilFormatForStencil8,
      {"metal_use_combined_depth_stencil_format_for_stencil8",
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 4962e3c..0eab485 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -83,7 +83,7 @@
     ApplyClearBigIntegerColorValueWithDraw,
     MetalUseMockBlitEncoderForWriteTimestamp,
     VulkanSplitCommandBufferOnDepthStencilComputeSampleAfterRenderPass,
-    D3D12Allocate2DTextureWithCopyDstOrRenderAttachmentAsCommittedResource,
+    DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment,
     MetalUseCombinedDepthStencilFormatForStencil8,
     MetalUseBothDepthAndStencilAttachmentsForCombinedDepthStencilFormats,
     MetalKeepMultisubresourceDepthStencilTexturesInitialized,
diff --git a/src/dawn/native/d3d12/AdapterD3D12.cpp b/src/dawn/native/d3d12/AdapterD3D12.cpp
index d56ef2f..ccd42bf 100644
--- a/src/dawn/native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn/native/d3d12/AdapterD3D12.cpp
@@ -558,7 +558,7 @@
     if ((gpu_info::IsIntelGen9(vendorId, deviceId) && !gpu_info::IsSkylake(deviceId)) ||
         gpu_info::IsIntelGen11(vendorId, deviceId)) {
         deviceToggles->Default(
-            Toggle::D3D12Allocate2DTextureWithCopyDstOrRenderAttachmentAsCommittedResource, true);
+            Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment, true);
         // Now we don't need to force clearing depth stencil textures with CopyDst as all the depth
         // stencil textures (can only be 2D textures) will be created with CreateCommittedResource()
         // instead of CreatePlacedResource().
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp
index 3c99d88..f87078a 100644
--- a/src/dawn/native/d3d12/TextureD3D12.cpp
+++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -612,7 +612,7 @@
     }
     bool forceAllocateAsCommittedResource =
         (device->IsToggleEnabled(
-            Toggle::D3D12Allocate2DTextureWithCopyDstOrRenderAttachmentAsCommittedResource)) &&
+            Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment)) &&
         GetDimension() == wgpu::TextureDimension::e2D &&
         (GetInternalUsage() & (wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment));
     DAWN_TRY_ASSIGN(mResourceAllocation,
diff --git a/src/dawn/native/vulkan/AdapterVk.cpp b/src/dawn/native/vulkan/AdapterVk.cpp
index d6a61dc..cec496a 100644
--- a/src/dawn/native/vulkan/AdapterVk.cpp
+++ b/src/dawn/native/vulkan/AdapterVk.cpp
@@ -416,6 +416,17 @@
         deviceToggles->Default(Toggle::AlwaysResolveIntoZeroLevelAndLayer, true);
     }
 
+    if (IsIntelMesa() && gpu_info::IsIntelGen12LP(GetVendorId(), GetDeviceId())) {
+        // dawn:1688: Intel Mesa driver has a bug about reusing the VkDeviceMemory that was
+        // previously bound to a 2D VkImage. To work around that bug we have to disable the resource
+        // sub-allocation for 2D textures with CopyDst or RenderAttachment usage.
+        const gpu_info::DriverVersion kDriverVersion = {21, 3, 6, 0};
+        if (gpu_info::CompareIntelMesaDriverVersion(GetDriverVersion(), kDriverVersion) >= 0) {
+            deviceToggles->Default(
+                Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment, true);
+        }
+    }
+
     // The environment can request to various options for depth-stencil formats that could be
     // unavailable. Override the decision if it is not applicable.
     bool supportsD32s8 = IsDepthStencilFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT);
@@ -474,4 +485,11 @@
 #endif
 }
 
+bool Adapter::IsIntelMesa() const {
+    if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
+        return mDeviceInfo.driverProperties.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR;
+    }
+    return false;
+}
+
 }  // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/AdapterVk.h b/src/dawn/native/vulkan/AdapterVk.h
index 0b8226a..0eb9317 100644
--- a/src/dawn/native/vulkan/AdapterVk.h
+++ b/src/dawn/native/vulkan/AdapterVk.h
@@ -43,6 +43,7 @@
     bool IsDepthStencilFormatSupported(VkFormat format) const;
 
     bool IsAndroidQualcomm() const;
+    bool IsIntelMesa() const;
 
   private:
     MaybeError InitializeImpl() override;
diff --git a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
index 390b326..ba1816e 100644
--- a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
+++ b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
@@ -124,7 +124,8 @@
 
 ResultOrError<ResourceMemoryAllocation> ResourceMemoryAllocator::Allocate(
     const VkMemoryRequirements& requirements,
-    MemoryKind kind) {
+    MemoryKind kind,
+    bool forceDisableSubAllocation) {
     // The Vulkan spec guarantees at least on memory type is valid.
     int memoryType = FindBestTypeIndex(requirements, kind);
     ASSERT(memoryType >= 0);
@@ -134,7 +135,8 @@
     // Sub-allocate non-mappable resources because at the moment the mapped pointer
     // is part of the resource and not the heap, which doesn't match the Vulkan model.
     // TODO(crbug.com/dawn/849): allow sub-allocating mappable resources, maybe.
-    if (requirements.size < kMaxSizeForSubAllocation && kind != MemoryKind::LinearMappable &&
+    if (!forceDisableSubAllocation && requirements.size < kMaxSizeForSubAllocation &&
+        kind != MemoryKind::LinearMappable &&
         !mDevice->IsToggleEnabled(Toggle::DisableResourceSuballocation)) {
         // When sub-allocating, Vulkan requires that we respect bufferImageGranularity. Some
         // hardware puts information on the memory's page table entry and allocating a linear
diff --git a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
index 1ece6d7..2be1895 100644
--- a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
+++ b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
@@ -43,7 +43,8 @@
     ~ResourceMemoryAllocator();
 
     ResultOrError<ResourceMemoryAllocation> Allocate(const VkMemoryRequirements& requirements,
-                                                     MemoryKind kind);
+                                                     MemoryKind kind,
+                                                     bool forceDisableSubAllocation = false);
     void Deallocate(ResourceMemoryAllocation* allocation);
 
     void DestroyPool();
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index b15b9be..2e767a7 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -730,8 +730,14 @@
     VkMemoryRequirements requirements;
     device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
 
-    DAWN_TRY_ASSIGN(mMemoryAllocation, device->GetResourceMemoryAllocator()->Allocate(
-                                           requirements, MemoryKind::Opaque));
+    bool forceDisableSubAllocation =
+        (device->IsToggleEnabled(
+            Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment)) &&
+        GetDimension() == wgpu::TextureDimension::e2D &&
+        (GetInternalUsage() & (wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment));
+    DAWN_TRY_ASSIGN(mMemoryAllocation,
+                    device->GetResourceMemoryAllocator()->Allocate(requirements, MemoryKind::Opaque,
+                                                                   forceDisableSubAllocation));
 
     DAWN_TRY(CheckVkSuccess(
         device->fn.BindImageMemory(device->GetVkDevice(), mHandle,