Add a toggle to disable validation of AHB VkImage required size

- Move the check of the required size till after the VkImage is
  bound to memory. This is required by the Vulkan spec.

- also add AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to test
  CPUWriteThenGPURead. Some GPU drivers require at least one
  non-CPU usage to import into Vulkan.

Bug: 333424893
Change-Id: I80abc52eec19c093576bf68f2f1ed2bfa16929ee
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/187587
Reviewed-by: Brian Sheedy <bsheedy@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index 2b373e7..2d9d36f 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -581,6 +581,11 @@
     {Toggle::D3D11UseUnmonitoredFence,
      {"d3d11_use_unmonitored_fence", "Use d3d11 unmonitored fence.",
       "https://crbug.com/chromium/335553337", ToggleStage::Device}},
+    {Toggle::IgnoreImportedAHardwareBufferVulkanImageSize,
+     {"ignore_imported_ahardwarebuffer_vulkan_image_size",
+      "Don't validate the required VkImage size against the size of the AHardwareBuffer on import. "
+      "Some drivers report the wrong size.",
+      "https://crbug.com/333424893", ToggleStage::Device}},
     // Comment to separate the }} so it is clearer what to copy-paste to add a toggle.
 }};
 }  // anonymous namespace
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index b5f35e6..cb343ee 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -144,6 +144,7 @@
     VulkanSkipDraw,
 
     D3D11UseUnmonitoredFence,
+    IgnoreImportedAHardwareBufferVulkanImageSize,
 
     EnumCount,
     InvalidEnum = EnumCount,
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 6075a4c..b7d9eb1 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -663,6 +663,10 @@
         deviceToggles->Default(Toggle::ResolveMultipleAttachmentInSeparatePasses, true);
     }
 
+    if (IsAndroidSamsung() || IsAndroidQualcomm()) {
+        deviceToggles->Default(Toggle::IgnoreImportedAHardwareBufferVulkanImageSize, 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
@@ -806,6 +810,14 @@
 #endif
 }
 
+bool PhysicalDevice::IsAndroidSamsung() const {
+#if DAWN_PLATFORM_IS(ANDROID)
+    return gpu_info::IsSamsung(GetVendorId());
+#else
+    return false;
+#endif
+}
+
 bool PhysicalDevice::IsIntelMesa() const {
     if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
         return mDeviceInfo.driverProperties.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR;
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.h b/src/dawn/native/vulkan/PhysicalDeviceVk.h
index fb8d266..8a09e4f 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.h
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.h
@@ -60,6 +60,7 @@
 
     bool IsAndroidQualcomm() const;
     bool IsAndroidARM() const;
+    bool IsAndroidSamsung() const;
     bool IsIntelMesa() const;
 
     uint32_t GetDefaultComputeSubgroupSize() const;
diff --git a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
index 7691e89..732806a 100644
--- a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
+++ b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
@@ -676,19 +676,9 @@
 
     // Import the memory as VkDeviceMemory and bind to the VkImage.
     {
-        // Get the valid memory types for the VkImage.
+        // Choose the best memory type that satisfies the import's constraint.
         VkMemoryRequirements memoryRequirements;
-        device->fn.GetImageMemoryRequirements(vkDevice, sharedTextureMemory->mVkImage->Get(),
-                                              &memoryRequirements);
-
-        DAWN_INVALID_IF(memoryRequirements.size > bufferProperties.allocationSize,
-                        "Required texture memory size (%u) is larger than the AHardwareBuffer "
-                        "allocation size (%u).",
-                        memoryRequirements.size, bufferProperties.allocationSize);
-
-        // Choose the best memory type that satisfies both the image's constraint and the
-        // import's constraint.
-        memoryRequirements.memoryTypeBits &= bufferProperties.memoryTypeBits;
+        memoryRequirements.memoryTypeBits = bufferProperties.memoryTypeBits;
         int memoryTypeIndex = device->GetResourceMemoryAllocator()->FindBestTypeIndex(
             memoryRequirements, MemoryKind::Opaque);
         DAWN_INVALID_IF(memoryTypeIndex == -1,
@@ -724,6 +714,22 @@
             device->fn.BindImageMemory(vkDevice, sharedTextureMemory->mVkImage->Get(),
                                        sharedTextureMemory->mVkDeviceMemory->Get(), 0),
             "vkBindImageMemory"));
+
+        // Verify the texture memory requirements fit within the constraints of the AHardwareBuffer.
+        device->fn.GetImageMemoryRequirements(vkDevice, sharedTextureMemory->mVkImage->Get(),
+                                              &memoryRequirements);
+
+        DAWN_INVALID_IF((memoryRequirements.memoryTypeBits & bufferProperties.memoryTypeBits) == 0,
+                        "Required memory type bits (%u) do not overlap with AHardwareBuffer memory "
+                        "type bits (%u).",
+                        memoryRequirements.memoryTypeBits, bufferProperties.memoryTypeBits);
+
+        if (!device->IsToggleEnabled(Toggle::IgnoreImportedAHardwareBufferVulkanImageSize)) {
+            DAWN_INVALID_IF(memoryRequirements.size > bufferProperties.allocationSize,
+                            "Required texture memory size (%u) is larger than the AHardwareBuffer "
+                            "allocation size (%u).",
+                            memoryRequirements.size, bufferProperties.allocationSize);
+        }
     }
     return sharedTextureMemory;
 #else
diff --git a/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp b/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
index 6e97075..d24cc0a 100644
--- a/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
+++ b/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
@@ -255,7 +255,8 @@
         .height = 4,
         .layers = 1,
         .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
-        .usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+        // Include at least one GPU usage. Otherwise import to Vulkan can fail.
+        .usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
     };
     AHardwareBuffer* aHardwareBuffer;
     EXPECT_EQ(AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer), 0);