Merge "Refactor vk image readback in end2end tests" into main

GitOrigin-RevId: fbc9e43e236777dacf23c0d4bf71dc414df984a9
Change-Id: I8e755fe3be9a9bd57a317cdb407ee76e2617e1a1
diff --git a/common/end2end/Android.bp b/common/end2end/Android.bp
index 9d35c3f..f8ee718 100644
--- a/common/end2end/Android.bp
+++ b/common/end2end/Android.bp
@@ -9,6 +9,7 @@
     defaults: ["gfxstream_cc_defaults"],
     srcs: [
         "GfxstreamEnd2EndTests.cpp",
+        "GfxstreamEnd2EndTestUtils.cpp",
         "GfxstreamEnd2EndCompositionTests.cpp",
         "GfxstreamEnd2EndGlTests.cpp",
         "GfxstreamEnd2EndVkTests.cpp",
diff --git a/common/end2end/GfxstreamEnd2EndTestUtils.cpp b/common/end2end/GfxstreamEnd2EndTestUtils.cpp
new file mode 100644
index 0000000..6355e93
--- /dev/null
+++ b/common/end2end/GfxstreamEnd2EndTestUtils.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "GfxstreamEnd2EndTestUtils.h"
+
+namespace gfxstream {
+namespace tests {
+namespace utils {
+
+using testing::Eq;
+using testing::Ge;
+using testing::IsEmpty;
+using testing::IsNull;
+using testing::Not;
+using testing::NotNull;
+
+uint32_t getMemoryType(const vkhpp::PhysicalDevice& physicalDevice,
+                       const vkhpp::MemoryRequirements& memoryRequirements,
+                       vkhpp::MemoryPropertyFlags memoryProperties) {
+    const auto props = physicalDevice.getMemoryProperties();
+    for (uint32_t i = 0; i < props.memoryTypeCount; i++) {
+        if (!(memoryRequirements.memoryTypeBits & (1 << i))) {
+            continue;
+        }
+        if ((props.memoryTypes[i].propertyFlags & memoryProperties) != memoryProperties) {
+            continue;
+        }
+        return i;
+    }
+    return -1;
+}
+
+void readImageData(vkhpp::Image image, uint32_t width, uint32_t height,
+                   vkhpp::ImageLayout currentLayout, void* dst, uint64_t dstSize,
+                   const GfxstreamEnd2EndTest::TypicalVkTestEnvironment& testEnvironment) {
+    auto& instance = testEnvironment.instance;
+    auto& physicalDevice = testEnvironment.physicalDevice;
+    auto& device = testEnvironment.device;
+    auto& queue = testEnvironment.queue;
+    auto queueFamilyIndex = testEnvironment.queueFamilyIndex;
+
+    // Read-back buffer
+    const vkhpp::BufferCreateInfo readbackBufferCreateInfo = {
+        .size = static_cast<VkDeviceSize>(dstSize),
+        .usage = vkhpp::BufferUsageFlagBits::eTransferDst,
+        .sharingMode = vkhpp::SharingMode::eExclusive,
+    };
+    auto readbackBuffer = device->createBufferUnique(readbackBufferCreateInfo).value;
+    ASSERT_THAT(readbackBuffer, IsValidHandle());
+
+    vkhpp::MemoryRequirements readbackBufferMemoryRequirements{};
+    device->getBufferMemoryRequirements(*readbackBuffer, &readbackBufferMemoryRequirements);
+
+    const auto readbackBufferMemoryType = getMemoryType(
+        physicalDevice, readbackBufferMemoryRequirements,
+        vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
+
+    // Read-back memory
+    const vkhpp::MemoryAllocateInfo readbackBufferMemoryAllocateInfo = {
+        .allocationSize = readbackBufferMemoryRequirements.size,
+        .memoryTypeIndex = readbackBufferMemoryType,
+    };
+    auto readbackBufferMemory =
+        device->allocateMemoryUnique(readbackBufferMemoryAllocateInfo).value;
+    ASSERT_THAT(readbackBufferMemory, IsValidHandle());
+    ASSERT_THAT(device->bindBufferMemory(*readbackBuffer, *readbackBufferMemory, 0), IsVkSuccess());
+
+    // Command buffer
+    const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
+        .queueFamilyIndex = queueFamilyIndex,
+    };
+
+    auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
+    ASSERT_THAT(commandPool, IsValidHandle());
+    const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
+        .level = vkhpp::CommandBufferLevel::ePrimary,
+        .commandPool = *commandPool,
+        .commandBufferCount = 1,
+    };
+    auto readbackCommandBuffers =
+        device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
+    ASSERT_THAT(readbackCommandBuffers, Not(IsEmpty()));
+    auto readbackCommandBuffer = std::move(readbackCommandBuffers[0]);
+    ASSERT_THAT(readbackCommandBuffer, IsValidHandle());
+
+    const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
+        .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
+    };
+    readbackCommandBuffer->begin(commandBufferBeginInfo);
+    const vkhpp::ImageMemoryBarrier readbackBarrier{
+        .oldLayout = currentLayout,
+        .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
+        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .image = image,
+        .subresourceRange =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .levelCount = 1,
+                .layerCount = 1,
+            },
+    };
+
+    readbackCommandBuffer->pipelineBarrier(
+        vkhpp::PipelineStageFlagBits::eAllCommands, vkhpp::PipelineStageFlagBits::eAllCommands,
+        vkhpp::DependencyFlags(), nullptr, nullptr, readbackBarrier);
+
+    const vkhpp::BufferImageCopy bufferImageCopy = {
+        .imageSubresource =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .layerCount = 1,
+            },
+        .imageExtent =
+            {
+                .width = width,
+                .height = height,
+                .depth = 1,
+            },
+    };
+    readbackCommandBuffer->copyImageToBuffer(image, vkhpp::ImageLayout::eTransferSrcOptimal,
+                                             *readbackBuffer, 1, &bufferImageCopy);
+
+    const vkhpp::ImageMemoryBarrier restoreBarrier{
+        .oldLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
+        .newLayout = currentLayout,
+        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .image = image,
+        .subresourceRange =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .levelCount = 1,
+                .layerCount = 1,
+            },
+    };
+
+    readbackCommandBuffer->pipelineBarrier(
+        vkhpp::PipelineStageFlagBits::eAllCommands, vkhpp::PipelineStageFlagBits::eAllCommands,
+        vkhpp::DependencyFlags(), nullptr, nullptr, restoreBarrier);
+
+    readbackCommandBuffer->end();
+
+    auto readbackFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
+    ASSERT_THAT(readbackCommandBuffer, IsValidHandle());
+
+    // Execute the command to copy image back to buffer
+    const vkhpp::SubmitInfo readbackSubmitInfo = {
+        .commandBufferCount = 1,
+        .pCommandBuffers = &readbackCommandBuffer.get(),
+    };
+    queue.submit(readbackSubmitInfo, *readbackFence);
+
+    auto readbackWaitResult = device->waitForFences(*readbackFence, VK_TRUE, 3000000000L);
+    ASSERT_THAT(readbackWaitResult, IsVkSuccess());
+
+    // Verify content
+    void* mapped;
+    auto mapResult = device->mapMemory(*readbackBufferMemory, 0, VK_WHOLE_SIZE,
+                                       vkhpp::MemoryMapFlags{}, &mapped);
+    ASSERT_THAT(mapResult, IsVkSuccess());
+    ASSERT_THAT(mapped, NotNull());
+    memcpy(dst, mapped, dstSize);
+    device->unmapMemory(*readbackBufferMemory);
+}
+
+}  // namespace utils
+}  // namespace tests
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/common/end2end/GfxstreamEnd2EndTestUtils.h b/common/end2end/GfxstreamEnd2EndTestUtils.h
new file mode 100644
index 0000000..063ba7a
--- /dev/null
+++ b/common/end2end/GfxstreamEnd2EndTestUtils.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "GfxstreamEnd2EndTests.h"
+
+namespace gfxstream {
+namespace tests {
+namespace utils {
+uint32_t getMemoryType(const vkhpp::PhysicalDevice& physicalDevice,
+                       const vkhpp::MemoryRequirements& memoryRequirements,
+                       vkhpp::MemoryPropertyFlags memoryProperties);
+
+void readImageData(vkhpp::Image image, uint32_t width, uint32_t height,
+                   vkhpp::ImageLayout currentLayout, void* dst, uint64_t dstSize,
+                   const GfxstreamEnd2EndTest::TypicalVkTestEnvironment& testEnvironment);
+}  // namespace utils
+}  // namespace tests
+}  // namespace gfxstream
diff --git a/common/end2end/GfxstreamEnd2EndTests.cpp b/common/end2end/GfxstreamEnd2EndTests.cpp
index 9786865..00b9c42 100644
--- a/common/end2end/GfxstreamEnd2EndTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndTests.cpp
@@ -611,22 +611,6 @@
     };
 }
 
-uint32_t GfxstreamEnd2EndTest::GetMemoryType(const vkhpp::PhysicalDevice& physicalDevice,
-                                             const vkhpp::MemoryRequirements& memoryRequirements,
-                                             vkhpp::MemoryPropertyFlags memoryProperties) {
-    const auto props = physicalDevice.getMemoryProperties();
-    for (uint32_t i = 0; i < props.memoryTypeCount; i++) {
-        if (!(memoryRequirements.memoryTypeBits & (1 << i))) {
-            continue;
-        }
-        if ((props.memoryTypes[i].propertyFlags & memoryProperties) != memoryProperties) {
-            continue;
-        }
-        return i;
-    }
-    return -1;
-}
-
 void GfxstreamEnd2EndTest::SnapshotSaveAndLoad() {
     auto directory = testing::TempDir();
 
diff --git a/common/end2end/GfxstreamEnd2EndTests.h b/common/end2end/GfxstreamEnd2EndTests.h
index 856024d..35c7579 100644
--- a/common/end2end/GfxstreamEnd2EndTests.h
+++ b/common/end2end/GfxstreamEnd2EndTests.h
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#pragma once
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -513,10 +515,6 @@
     VkExpected<TypicalVkTestEnvironment> SetUpTypicalVkTestEnvironment(
         const TypicalVkTestEnvironmentOptions& opts = {});
 
-    uint32_t GetMemoryType(const vkhpp::PhysicalDevice& physicalDevice,
-                           const vkhpp::MemoryRequirements& memoryRequirements,
-                           vkhpp::MemoryPropertyFlags memoryProperties);
-
     void SnapshotSaveAndLoad();
 
     GlExpected<Image> LoadImage(const std::string& basename);
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotBufferTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotBufferTests.cpp
index af0a75f..dbc043a 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotBufferTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotBufferTests.cpp
@@ -14,6 +14,7 @@
 
 #include <string>
 
+#include "GfxstreamEnd2EndTestUtils.h"
 #include "GfxstreamEnd2EndTests.h"
 #include "gfxstream/RutabagaLayerTestUtils.h"
 
@@ -52,7 +53,7 @@
     vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
 
-    const auto stagingBufferMemoryType = GetMemoryType(
+    const auto stagingBufferMemoryType = utils::getMemoryType(
         physicalDevice, stagingBufferMemoryRequirements,
         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
 
@@ -94,7 +95,7 @@
     vkhpp::MemoryRequirements vertexBufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*vertexBuffer, &vertexBufferMemoryRequirements);
 
-    const auto vertexBufferMemoryType = GetMemoryType(
+    const auto vertexBufferMemoryType = utils::getMemoryType(
         physicalDevice, vertexBufferMemoryRequirements,
         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
 
@@ -163,7 +164,7 @@
     vkhpp::MemoryRequirements readbackBufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*readbackBuffer, &readbackBufferMemoryRequirements);
 
-    const auto readbackBufferMemoryType = GetMemoryType(
+    const auto readbackBufferMemoryType = utils::getMemoryType(
         physicalDevice, readbackBufferMemoryRequirements,
         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
 
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
index e81faa5..cb9938f 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
@@ -14,6 +14,7 @@
 
 #include <string>
 
+#include "GfxstreamEnd2EndTestUtils.h"
 #include "GfxstreamEnd2EndTests.h"
 #include "gfxstream/RutabagaLayerTestUtils.h"
 
@@ -58,8 +59,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
@@ -104,8 +105,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
@@ -179,8 +180,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
@@ -224,8 +225,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryDedicatedAllocateInfo dedicatedAllocateInfo = {
@@ -271,8 +272,12 @@
     for (size_t i = 0; i < kSize; i++) {
         srcBufferContent[i] = static_cast<uint8_t>(i & 0xff);
     }
-    auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
-        VK_ASSERT(SetUpTypicalVkTestEnvironment());
+    TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
+    auto& instance = testEnvironment.instance;
+    auto& physicalDevice = testEnvironment.physicalDevice;
+    auto& device = testEnvironment.device;
+    auto& queue = testEnvironment.queue;
+    auto queueFamilyIndex = testEnvironment.queueFamilyIndex;
 
     // Staging buffer
     const vkhpp::BufferCreateInfo bufferCreateInfo = {
@@ -286,7 +291,7 @@
     vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
 
-    const auto stagingBufferMemoryType = GetMemoryType(
+    const auto stagingBufferMemoryType = utils::getMemoryType(
         physicalDevice, stagingBufferMemoryRequirements,
         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
 
@@ -338,8 +343,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
@@ -427,85 +432,13 @@
     // Snapshot
     SnapshotSaveAndLoad();
 
-    // Read-back buffer
-    const vkhpp::BufferCreateInfo readbackBufferCreateInfo = {
-        .size = static_cast<VkDeviceSize>(kSize),
-        .usage = vkhpp::BufferUsageFlagBits::eTransferDst,
-        .sharingMode = vkhpp::SharingMode::eExclusive,
-    };
-    auto readbackBuffer = device->createBufferUnique(readbackBufferCreateInfo).value;
-    ASSERT_THAT(readbackBuffer, IsValidHandle());
-
-    vkhpp::MemoryRequirements readbackBufferMemoryRequirements{};
-    device->getBufferMemoryRequirements(*readbackBuffer, &readbackBufferMemoryRequirements);
-
-    const auto readbackBufferMemoryType = GetMemoryType(
-        physicalDevice, readbackBufferMemoryRequirements,
-        vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
-
-    // Read-back memory
-    const vkhpp::MemoryAllocateInfo readbackBufferMemoryAllocateInfo = {
-        .allocationSize = readbackBufferMemoryRequirements.size,
-        .memoryTypeIndex = readbackBufferMemoryType,
-    };
-    auto readbackBufferMemory =
-        device->allocateMemoryUnique(readbackBufferMemoryAllocateInfo).value;
-    ASSERT_THAT(readbackBufferMemory, IsValidHandle());
-    ASSERT_THAT(device->bindBufferMemory(*readbackBuffer, *readbackBufferMemory, 0), IsVkSuccess());
-
-    auto readbackCommandBuffers =
-        device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
-    ASSERT_THAT(readbackCommandBuffers, Not(IsEmpty()));
-    auto readbackCommandBuffer = std::move(readbackCommandBuffers[0]);
-    ASSERT_THAT(readbackCommandBuffer, IsValidHandle());
-
-    readbackCommandBuffer->begin(commandBufferBeginInfo);
-    const vkhpp::ImageMemoryBarrier readbackBarrier{
-        .oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
-        .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
-        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-        .image = *image,
-        .subresourceRange =
-            {
-                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
-                .levelCount = 1,
-                .layerCount = 1,
-            },
-    };
-
-    readbackCommandBuffer->pipelineBarrier(
-        vkhpp::PipelineStageFlagBits::eAllCommands, vkhpp::PipelineStageFlagBits::eAllCommands,
-        vkhpp::DependencyFlags(), nullptr, nullptr, readbackBarrier);
-
-    readbackCommandBuffer->copyImageToBuffer(*image, vkhpp::ImageLayout::eTransferSrcOptimal,
-                                             *readbackBuffer, 1, &bufferImageCopy);
-    readbackCommandBuffer->end();
-
-    auto readbackFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
-    ASSERT_THAT(readbackCommandBuffer, IsValidHandle());
-
-    // Execute the command to copy image back to buffer
-    const vkhpp::SubmitInfo readbackSubmitInfo = {
-        .commandBufferCount = 1,
-        .pCommandBuffers = &readbackCommandBuffer.get(),
-    };
-    queue.submit(readbackSubmitInfo, *readbackFence);
-
-    auto readbackWaitResult = device->waitForFences(*readbackFence, VK_TRUE, 3000000000L);
-    ASSERT_THAT(readbackWaitResult, IsVkSuccess());
-
-    // Verify content
-    mapResult = device->mapMemory(*readbackBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{},
-                                  &mapped);
-    ASSERT_THAT(mapResult, IsVkSuccess());
-    ASSERT_THAT(mapped, NotNull());
-    bytes = reinterpret_cast<uint8_t*>(mapped);
+    std::vector<uint8_t> dst(kSize);
+    utils::readImageData(*image, kWidth, kHeight, vkhpp::ImageLayout::eTransferDstOptimal,
+                         dst.data(), kSize, testEnvironment);
 
     for (uint32_t i = 0; i < kSize; ++i) {
-        ASSERT_THAT(bytes[i], Eq(srcBufferContent[i]));
+        ASSERT_THAT(dst[i], Eq(srcBufferContent[i]));
     }
-    device->unmapMemory(*readbackBufferMemory);
 }
 
 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotImageTest,
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
index ec22f3c..b9cc69f 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotPipelineTests.cpp
@@ -14,6 +14,7 @@
 
 #include <string>
 
+#include "GfxstreamEnd2EndTestUtils.h"
 #include "GfxstreamEnd2EndTests.h"
 #include "gfxstream/RutabagaLayerTestUtils.h"
 #include "simple_shader_frag.h"
@@ -236,8 +237,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device.getImageMemoryRequirements(*(res->image), &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
-                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
         .allocationSize = imageMemoryRequirements.size,
@@ -359,8 +360,13 @@
 }
 
 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest, CanSubmitQueue) {
-    auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
-        VK_ASSERT(SetUpTypicalVkTestEnvironment());
+    TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
+    auto& instance = testEnvironment.instance;
+    auto& physicalDevice = testEnvironment.physicalDevice;
+    auto& device = testEnvironment.device;
+    auto& queue = testEnvironment.queue;
+    auto queueFamilyIndex = testEnvironment.queueFamilyIndex;
+
     auto pipelineInfo = createPipeline(device.get());
 
     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
@@ -402,7 +408,7 @@
     auto commandBuffer = std::move(commandBuffers[0]);
     ASSERT_THAT(commandBuffer, IsValidHandle());
 
-    vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.2f, 0.2f, 0.2f, 0.2f});
+    vkhpp::ClearColorValue clearColor(std::array<float, 4>{1.0f, 0.0f, 1.0f, 1.0f});
     vkhpp::ClearValue clearValue{
         .color = clearColor,
     };
@@ -461,21 +467,6 @@
     // Try to draw something.
     // Color attachment layout must be snapshotted, otherwise validation layer will complain.
     commandBuffer->begin(commandBufferBeginInfo);
-    const vkhpp::ImageMemoryBarrier readSrcBarrier{
-        .oldLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
-        .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
-        .srcAccessMask = vkhpp::AccessFlagBits::eColorAttachmentWrite,
-        .dstAccessMask = vkhpp::AccessFlagBits::eTransferRead,
-        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-        .image = *colorAttachmentInfo->image,
-        .subresourceRange =
-            {
-                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
-                .levelCount = 1,
-                .layerCount = 1,
-            },
-    };
     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
 
@@ -497,6 +488,14 @@
 
     waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
     ASSERT_THAT(waitResult, IsVkSuccess());
+
+    std::vector<uint32_t> dst(kFbWidth * kFbHeight);
+    utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
+                         vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
+                         dst.size() * sizeof(uint32_t), testEnvironment);
+    for (int i = 0; i < dst.size(); i++) {
+        ASSERT_THAT(dst[i], Eq(0xffff00ff));
+    }
 }
 
 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotPipelineTest,
diff --git a/common/end2end/GfxstreamEnd2EndVkTests.cpp b/common/end2end/GfxstreamEnd2EndVkTests.cpp
index e09d3bd..d6b836b 100644
--- a/common/end2end/GfxstreamEnd2EndVkTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkTests.cpp
@@ -17,6 +17,7 @@
 #include <atomic>
 #include <thread>
 
+#include "GfxstreamEnd2EndTestUtils.h"
 #include "GfxstreamEnd2EndTests.h"
 
 namespace gfxstream {
@@ -85,7 +86,7 @@
         vkhpp::MemoryRequirements imageMemoryRequirements{};
         device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-        const uint32_t imageMemoryIndex = GetMemoryType(
+        const uint32_t imageMemoryIndex = utils::getMemoryType(
             physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
         ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
@@ -165,8 +166,8 @@
     vkhpp::MemoryRequirements imageMemoryRequirements{};
     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
 
-    const uint32_t imageMemoryIndex =
-        GetMemoryType(physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const uint32_t imageMemoryIndex = utils::getMemoryType(
+        physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
 
     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
@@ -190,11 +191,9 @@
     vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
 
-    const auto stagingBufferMemoryType =
-         GetMemoryType(physicalDevice,
-                       stagingBufferMemoryRequirements,
-                       vkhpp::MemoryPropertyFlagBits::eHostVisible |
-                       vkhpp::MemoryPropertyFlagBits::eHostCoherent);
+    const auto stagingBufferMemoryType = utils::getMemoryType(
+        physicalDevice, stagingBufferMemoryRequirements,
+        vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
 
     const vkhpp::MemoryAllocateInfo stagingBufferMemoryAllocateInfo = {
         .allocationSize = stagingBufferMemoryRequirements.size,
@@ -368,8 +367,8 @@
                     Ne(vkhpp::MemoryPropertyFlagBits::eHostVisible));
     }
 
-    const auto bufferMemoryType = GetMemoryType(physicalDevice, bufferMemoryRequirements,
-                                                vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    const auto bufferMemoryType = utils::getMemoryType(physicalDevice, bufferMemoryRequirements,
+                                                       vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
     ASSERT_THAT(bufferMemoryType, Ne(-1));
 
     const vkhpp::ImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {
@@ -883,7 +882,7 @@
     vkhpp::MemoryRequirements bufferMemoryRequirements{};
     device->getBufferMemoryRequirements(*buffer, &bufferMemoryRequirements);
 
-    const uint32_t bufferMemoryIndex = GetMemoryType(
+    const uint32_t bufferMemoryIndex = utils::getMemoryType(
         physicalDevice, bufferMemoryRequirements,
         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
     if (bufferMemoryIndex == -1) {