Vulkan: Allocate MAP_READ buffer on HOST_CACHED memory type

This patch tries to allocate MAP_READ buffer on the memory with
VK_MEMORY_PROPERTY_HOST_CACHED_BIT memory type to optimize the CPU
read-only access.

Bug: dawn:849
Change-Id: I4a96ad54f566fa7e744c14506165e5c84e55b9d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/143720
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/native/vulkan/BufferVk.cpp b/src/dawn/native/vulkan/BufferVk.cpp
index bfa2e51..fc1f150 100644
--- a/src/dawn/native/vulkan/BufferVk.cpp
+++ b/src/dawn/native/vulkan/BufferVk.cpp
@@ -203,8 +203,10 @@
     device->fn.GetBufferMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
 
     MemoryKind requestKind = MemoryKind::Linear;
-    if (GetUsage() & kMappableBufferUsages) {
-        requestKind = MemoryKind::LinearMappable;
+    if (GetUsage() & wgpu::BufferUsage::MapRead) {
+        requestKind = MemoryKind::LinearReadMappable;
+    } else if (GetUsage() & wgpu::BufferUsage::MapWrite) {
+        requestKind = MemoryKind::LinearWriteMappable;
     }
     DAWN_TRY_ASSIGN(mMemoryAllocation,
                     device->GetResourceMemoryAllocator()->Allocate(requirements, requestKind));
diff --git a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
index e8466a7..5db4ab2 100644
--- a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
+++ b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp
@@ -38,6 +38,22 @@
 // size
 constexpr uint64_t kBuddyHeapsSize = 2 * kMaxSizeForSubAllocation;
 
+bool IsMemoryKindMappable(MemoryKind memoryKind) {
+    switch (memoryKind) {
+        case MemoryKind::LinearReadMappable:
+        case MemoryKind::LinearWriteMappable:
+            return true;
+
+        case MemoryKind::LazilyAllocated:
+        case MemoryKind::Linear:
+        case MemoryKind::Opaque:
+            return false;
+
+        default:
+            UNREACHABLE();
+    }
+}
+
 }  // anonymous namespace
 
 // SingleTypeAllocator is a combination of a BuddyMemoryAllocator and its client and can
@@ -136,7 +152,7 @@
     // 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 (!forceDisableSubAllocation && requirements.size < kMaxSizeForSubAllocation &&
-        kind != MemoryKind::LinearMappable &&
+        !IsMemoryKindMappable(kind) &&
         !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
@@ -166,7 +182,7 @@
     DAWN_TRY_ASSIGN(resourceHeap, mAllocatorsPerType[memoryType]->AllocateResourceHeap(size));
 
     void* mappedPointer = nullptr;
-    if (kind == MemoryKind::LinearMappable) {
+    if (IsMemoryKindMappable(kind)) {
         DAWN_TRY_WITH_CLEANUP(
             CheckVkSuccess(mDevice->fn.MapMemory(mDevice->GetVkDevice(),
                                                  ToBackend(resourceHeap.get())->GetMemory(), 0,
@@ -230,7 +246,7 @@
 
 int ResourceMemoryAllocator::FindBestTypeIndex(VkMemoryRequirements requirements, MemoryKind kind) {
     const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
-    bool mappable = kind == MemoryKind::LinearMappable;
+    bool mappable = IsMemoryKindMappable(kind);
 
     // Find a suitable memory type for this allocation
     int bestType = -1;
@@ -286,6 +302,19 @@
             continue;
         }
 
+        // Cached memory is optimal for read-only access from CPU as host memory accesses to
+        // uncached memory are slower than to cached memory.
+        bool currentHostCached =
+            info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+        bool bestHostCached =
+            info.memoryTypes[bestType].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+        if (kind == MemoryKind::LinearReadMappable && currentHostCached != bestHostCached) {
+            if (currentHostCached) {
+                bestType = static_cast<int>(i);
+            }
+            continue;
+        }
+
         // All things equal favor the memory in the biggest heap
         VkDeviceSize bestTypeHeapSize = info.memoryHeaps[info.memoryTypes[bestType].heapIndex].size;
         VkDeviceSize candidateHeapSize = info.memoryHeaps[info.memoryTypes[i].heapIndex].size;
diff --git a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
index 452819c..d870c1b 100644
--- a/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
+++ b/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h
@@ -34,7 +34,8 @@
 enum class MemoryKind {
     LazilyAllocated,
     Linear,
-    LinearMappable,
+    LinearReadMappable,
+    LinearWriteMappable,
     Opaque,
 };