Merge "Fix missing include after libc++ update" into main
diff --git a/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py b/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
index 4ff97a1..a966082 100644
--- a/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
+++ b/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
@@ -112,6 +112,8 @@
 
 apiModifies = {
     "vkMapMemoryIntoAddressSpaceGOOGLE" : ["memory"],
+    "vkGetBlobGOOGLE" : ["memory"],
+    "vkBindImageMemory": ["image"],
 }
 
 def is_modify_operation(api, param):
@@ -121,9 +123,6 @@
     return False
 
 def emit_impl(typeInfo, api, cgen):
-
-    cgen.line("// TODO: Implement")
-
     for p in api.parameters:
         if not (p.isHandleType):
             continue
diff --git a/codegen/vulkan/vulkan-docs-next/scripts/cereal/functable.py b/codegen/vulkan/vulkan-docs-next/scripts/cereal/functable.py
index 5e05530..2d34150 100644
--- a/codegen/vulkan/vulkan-docs-next/scripts/cereal/functable.py
+++ b/codegen/vulkan/vulkan-docs-next/scripts/cereal/functable.py
@@ -49,6 +49,7 @@
     "vkCreateSamplerYcbcrConversionKHR",
     "vkDestroySamplerYcbcrConversionKHR",
     "vkUpdateDescriptorSetWithTemplate",
+    "vkUpdateDescriptorSetWithTemplateKHR",
     "vkGetPhysicalDeviceImageFormatProperties2",
     "vkGetPhysicalDeviceImageFormatProperties2KHR",
     "vkBeginCommandBuffer",
diff --git a/codegen/vulkan/vulkan-docs-next/scripts/cerealgenerator.py b/codegen/vulkan/vulkan-docs-next/scripts/cerealgenerator.py
index 24225fd..25866cf 100644
--- a/codegen/vulkan/vulkan-docs-next/scripts/cerealgenerator.py
+++ b/codegen/vulkan/vulkan-docs-next/scripts/cerealgenerator.py
@@ -166,6 +166,15 @@
     "VK_KHR_swapchain" : HOST_MODULES,
 }
 
+REQUIRED_TYPES = {
+    "int",
+    "uint16_t",
+    "int64_t",
+    "double",
+    "VkPresentScalingFlagsEXT",
+    "VkPresentGravityFlagsEXT",
+}
+
 copyrightHeader = """// Copyright (C) 2018 The Android Open Source Project
 // Copyright (C) 2018 Google Inc.
 //
@@ -812,23 +821,9 @@
     def genType(self, typeinfo: TypeInfo, name, alias):
         OutputGenerator.genType(self, typeinfo, name, alias)
 
-        if self.featureSupported == False and name == "int":
-            self.typeInfo.onGenType(typeinfo, name, alias)
-            return
-
-        if self.featureSupported == False and name == "int64_t":
-            self.typeInfo.onGenType(typeinfo, name, alias)
-            return
-
-        if self.featureSupported == False and name == "double":
-            self.typeInfo.onGenType(typeinfo, name, alias)
-            return
-
-        if self.featureSupported == False and name == "VkPresentScalingFlagsEXT":
-            self.typeInfo.onGenType(typeinfo, name, alias)
-            return
-
-        if self.featureSupported == False and name == "VkPresentGravityFlagsEXT":
+        # Maybe this check can be removed if we refactor other things inside
+        # the cereal subdirectory.
+        if self.featureSupported == False and name in REQUIRED_TYPES:
             self.typeInfo.onGenType(typeinfo, name, alias)
             return
 
diff --git a/common/end2end/Android.bp b/common/end2end/Android.bp
index b802ad6..50ce851 100644
--- a/common/end2end/Android.bp
+++ b/common/end2end/Android.bp
@@ -23,14 +23,18 @@
         "libGLESv2_emulation_with_host",
         "libEGL_emulation_with_host",
         "libgfxstream_guest_vulkan_with_host",
+        "libplatform_rutabaga_server",
     ],
     shared_libs: [
+        "libandroidemu",
+        "libOpenglCodecCommon",
+        "libOpenglSystemCommonWithHost",
         "libbase",
+        "libcutils",
         "libdrm",
         "liblog",
         "libgfxstream_backend",
-        "libplatform_rutabaga",
-        "libOpenglSystemCommonWithHost",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
         "gfxstream_base",
@@ -38,8 +42,10 @@
         "gfxstream_snapshot",
         "gfxstream_vulkan_cereal_host",
         "libc++fs",
+        "libgfxstream_guest_android_with_host",
         "libgmock",
         "liblz4",
+        "libplatform_rutabaga",
     ],
     cflags: [
         // TODO: remove
diff --git a/common/end2end/GfxstreamEnd2EndGlTests.cpp b/common/end2end/GfxstreamEnd2EndGlTests.cpp
index d3dedfc..6b6934f 100644
--- a/common/end2end/GfxstreamEnd2EndGlTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndGlTests.cpp
@@ -88,10 +88,9 @@
     constexpr const int width = 32;
     constexpr const int height = 32;
 
-    auto anw = CreateEmulatedANW(width, height);
-    auto anwEgl = anw->asEglNativeWindowType();
+    auto anw = mAnwHelper->createNativeWindowForTesting(mGralloc.get(), width, height);
 
-    EGLSurface surface = mGl->eglCreateWindowSurface(display, config, anwEgl, nullptr);
+    EGLSurface surface = mGl->eglCreateWindowSurface(display, config, anw, nullptr);
     ASSERT_THAT(surface, Not(Eq(EGL_NO_SURFACE)));
 
     ASSERT_THAT(mGl->eglMakeCurrent(display, surface, surface, context), IsTrue());
@@ -108,7 +107,7 @@
     ASSERT_THAT(mGl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), IsTrue());
     ASSERT_THAT(mGl->eglDestroyContext(display, context), IsTrue());
     ASSERT_THAT(mGl->eglDestroySurface(display, surface), IsTrue());
-    anw.reset();
+    mAnwHelper->release(anw);
 
     TearDownGuest();
 }
diff --git a/common/end2end/GfxstreamEnd2EndTests.cpp b/common/end2end/GfxstreamEnd2EndTests.cpp
index 5baa9da..4c09ec9 100644
--- a/common/end2end/GfxstreamEnd2EndTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndTests.cpp
@@ -22,7 +22,6 @@
 
 #include <filesystem>
 
-#include "Gralloc.h"
 #include "ProcessPipe.h"
 #include "aemu/base/files/StdioStream.h"
 #include "aemu/base/system/System.h"
@@ -42,6 +41,7 @@
 using testing::AnyOf;
 using testing::Eq;
 using testing::Gt;
+using testing::IsFalse;
 using testing::IsTrue;
 using testing::Not;
 using testing::NotNull;
@@ -177,13 +177,9 @@
         ASSERT_THAT(mVk, NotNull());
     }
 
-    mAnwHelper = std::make_unique<TestingVirtGpuANativeWindowHelper>();
-    HostConnection::get()->setANativeWindowHelperForTesting(mAnwHelper.get());
-
-    mGralloc = std::make_unique<TestingVirtGpuGralloc>();
-    HostConnection::get()->setGrallocHelperForTesting(mGralloc.get());
-
-    mSync = HostConnection::get()->syncHelper();
+    mAnwHelper.reset(createPlatformANativeWindowHelper());
+    mGralloc.reset(createPlatformGralloc());
+    mSync.reset(createPlatformSyncHelper());
 }
 
 void GfxstreamEnd2EndTest::TearDownGuest() {
@@ -198,10 +194,10 @@
     }
     mVk.reset();
 
-    mGralloc.reset();
     mAnwHelper.reset();
+    mGralloc.reset();
+    mSync.reset();
 
-    HostConnection::exit();
     processPipeRestart();
 
     // Figure out more reliable way for guest shutdown to complete...
@@ -209,7 +205,15 @@
 }
 
 void GfxstreamEnd2EndTest::TearDownHost() {
-    ResetEmulatedVirtioGpu();
+    const uint32_t users = GetNumActiveEmulatedVirtioGpuUsers();
+    if (users != 0) {
+        ALOGE("The EmulationVirtioGpu was found to still be active by %" PRIu32
+              " after the "
+              "end of the test. Please ensure you have fully destroyed all objects created "
+              "during the test (Gralloc allocations, ANW allocations, etc).",
+              users);
+        abort();
+    }
 }
 
 void GfxstreamEnd2EndTest::TearDown() {
@@ -217,16 +221,6 @@
     TearDownHost();
 }
 
-std::unique_ptr<TestingANativeWindow> GfxstreamEnd2EndTest::CreateEmulatedANW(
-        uint32_t width,
-        uint32_t height) {
-    std::vector<std::unique_ptr<TestingAHardwareBuffer>> buffers;
-    for (int i = 0; i < 3; i++) {
-        buffers.push_back(mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888));
-    }
-    return std::make_unique<TestingANativeWindow>(width, height, DRM_FORMAT_ABGR8888, std::move(buffers));
-}
-
 void GfxstreamEnd2EndTest::SetUpEglContextAndSurface(
         uint32_t contextVersion,
         uint32_t width,
diff --git a/common/end2end/GfxstreamEnd2EndTests.h b/common/end2end/GfxstreamEnd2EndTests.h
index b989a3f..34fd728 100644
--- a/common/end2end/GfxstreamEnd2EndTests.h
+++ b/common/end2end/GfxstreamEnd2EndTests.h
@@ -12,16 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <future>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/expected.h>
 #include <inttypes.h>
+
+#include <future>
 #include <memory>
 #include <string>
 #include <thread>
 #include <variant>
 
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
 // clang-format off
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -37,13 +39,11 @@
 #define VULKAN_HPP_NO_EXCEPTIONS
 #include <vulkan/vulkan.hpp>
 #include <vulkan/vk_android_native_buffer.h>
-
 // clang-format on
 
-#include <android-base/expected.h>
-
-#include "HostConnection.h"
-#include "TestingAndroidWsi.h"
+#include "gfxstream/guest/ANativeWindow.h"
+#include "gfxstream/guest/Gralloc.h"
+#include "Sync.h"
 
 namespace gfxstream {
 namespace tests {
@@ -125,22 +125,13 @@
     std::move(vkhpp_result_value.value);                                      \
   })
 
-#define VK_TRY(x)                                                                   \
-    ({                                                                              \
-        auto vk_try_android_base_expected = (x);                                    \
-        if (!vk_try_android_base_expected.ok()) {                                   \
-            return android::base::unexpected(vk_try_android_base_expected.error()); \
-        }                                                                           \
-        std::move(vk_try_android_base_expected.value());                            \
-    })
-
-#define VK_TRY_RESULT(x)                               \
-    ({                                                 \
-        auto vkhpp_result = (x);                       \
-        if (vkhpp_result != vkhpp::Result::eSuccess) { \
-            return vkhpp_result;                       \
-        }                                              \
-    })
+#define VK_TRY(x)                                                             \
+  ({                                                                          \
+    auto vkhpp_result = (x);                                                  \
+    if (vkhpp_result != vkhpp::Result::eSuccess) {                            \
+        return vkhpp_result;                                                  \
+    }                                                                         \
+  })
 
 #define VK_TRY_RV(x)                                                          \
   ({                                                                          \
@@ -191,8 +182,6 @@
     void TearDownHost();
     void TearDown() override;
 
-    std::unique_ptr<TestingANativeWindow> CreateEmulatedANW(uint32_t width, uint32_t height);
-
     void SetUpEglContextAndSurface(uint32_t contextVersion,
                                    uint32_t width,
                                    uint32_t height,
@@ -225,9 +214,9 @@
 
     void SnapshotSaveAndLoad();
 
-    std::unique_ptr<TestingVirtGpuANativeWindowHelper> mAnwHelper;
-    std::unique_ptr<TestingVirtGpuGralloc> mGralloc;
-    SyncHelper* mSync;
+    std::unique_ptr<ANativeWindowHelper> mAnwHelper;
+    std::unique_ptr<Gralloc> mGralloc;
+    std::unique_ptr<SyncHelper> mSync;
     std::unique_ptr<GuestGlDispatchTable> mGl;
     std::unique_ptr<vkhpp::DynamicLoader> mVk;
 };
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
index eeb03c8..1028d68 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
@@ -30,7 +30,7 @@
 
 class GfxstreamEnd2EndVkSnapshotImageTest : public GfxstreamEnd2EndTest {};
 
-TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, BasicSaveLoad) {
+TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, PreserveImageHandle) {
     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
         VK_ASSERT(SetUpTypicalVkTestEnvironment());
 
@@ -75,6 +75,251 @@
     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
 }
 
+TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageContent) {
+    static constexpr int kWidth = 256;
+    static constexpr int kHeight = 256;
+    static constexpr vkhpp::DeviceSize kSize = 4 * kWidth * kHeight;
+
+    std::vector<uint8_t> srcBufferContent(kSize);
+    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());
+
+    // Staging buffer
+    const vkhpp::BufferCreateInfo bufferCreateInfo = {
+        .size = static_cast<VkDeviceSize>(kSize),
+        .usage = vkhpp::BufferUsageFlagBits::eTransferSrc,
+        .sharingMode = vkhpp::SharingMode::eExclusive,
+    };
+    auto stagingBuffer = device->createBufferUnique(bufferCreateInfo).value;
+    ASSERT_THAT(stagingBuffer, IsValidHandle());
+
+    vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
+    device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
+
+    const auto stagingBufferMemoryType = GetMemoryType(
+        physicalDevice, stagingBufferMemoryRequirements,
+        vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
+
+    // Staging memory
+    const vkhpp::MemoryAllocateInfo stagingBufferMemoryAllocateInfo = {
+        .allocationSize = stagingBufferMemoryRequirements.size,
+        .memoryTypeIndex = stagingBufferMemoryType,
+    };
+    auto stagingBufferMemory = device->allocateMemoryUnique(stagingBufferMemoryAllocateInfo).value;
+    ASSERT_THAT(stagingBufferMemory, IsValidHandle());
+    ASSERT_THAT(device->bindBufferMemory(*stagingBuffer, *stagingBufferMemory, 0), IsVkSuccess());
+
+    // Fill memory content
+    void* mapped = nullptr;
+    auto mapResult =
+        device->mapMemory(*stagingBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{}, &mapped);
+    ASSERT_THAT(mapResult, IsVkSuccess());
+    ASSERT_THAT(mapped, NotNull());
+
+    auto* bytes = reinterpret_cast<uint8_t*>(mapped);
+    std::memcpy(bytes, srcBufferContent.data(), kSize);
+
+    const vkhpp::MappedMemoryRange range = {
+        .memory = *stagingBufferMemory,
+        .offset = 0,
+        .size = kSize,
+    };
+    device->unmapMemory(*stagingBufferMemory);
+
+    // Image
+    const vkhpp::ImageCreateInfo imageCreateInfo = {
+        .pNext = nullptr,
+        .imageType = vkhpp::ImageType::e2D,
+        .extent.width = kWidth,
+        .extent.height = kHeight,
+        .extent.depth = 1,
+        .mipLevels = 1,
+        .arrayLayers = 1,
+        .format = vkhpp::Format::eR8G8B8A8Unorm,
+        .tiling = vkhpp::ImageTiling::eOptimal,
+        .initialLayout = vkhpp::ImageLayout::eUndefined,
+        .usage = vkhpp::ImageUsageFlagBits::eTransferDst | vkhpp::ImageUsageFlagBits::eTransferSrc,
+        .sharingMode = vkhpp::SharingMode::eExclusive,
+        .samples = vkhpp::SampleCountFlagBits::e1,
+    };
+    auto image = device->createImageUnique(imageCreateInfo).value;
+
+    vkhpp::MemoryRequirements imageMemoryRequirements{};
+    device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
+
+    const uint32_t imageMemoryIndex = GetMemoryType(physicalDevice, imageMemoryRequirements,
+                                                    vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
+    ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
+
+    const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
+        .allocationSize = imageMemoryRequirements.size,
+        .memoryTypeIndex = imageMemoryIndex,
+    };
+
+    auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
+    ASSERT_THAT(imageMemory, IsValidHandle());
+
+    ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 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 commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
+    ASSERT_THAT(commandBuffers, Not(IsEmpty()));
+    auto commandBuffer = std::move(commandBuffers[0]);
+    ASSERT_THAT(commandBuffer, IsValidHandle());
+
+    const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
+        .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
+    };
+    commandBuffer->begin(commandBufferBeginInfo);
+
+    const vkhpp::ImageMemoryBarrier barrier{
+        .oldLayout = vkhpp::ImageLayout::eUndefined,
+        .newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
+        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+        .image = *image,
+        .subresourceRange =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .levelCount = 1,
+                .layerCount = 1,
+            },
+    };
+
+    commandBuffer->pipelineBarrier(vkhpp::PipelineStageFlagBits::eAllCommands,
+                                   vkhpp::PipelineStageFlagBits::eAllCommands,
+                                   vkhpp::DependencyFlags(), nullptr, nullptr, barrier);
+
+    const vkhpp::BufferImageCopy bufferImageCopy = {
+        .imageSubresource =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .layerCount = 1,
+            },
+        .imageExtent =
+            {
+                .width = kWidth,
+                .height = kHeight,
+                .depth = 1,
+            },
+    };
+    commandBuffer->copyBufferToImage(*stagingBuffer, *image,
+                                     vkhpp::ImageLayout::eTransferDstOptimal, 1, &bufferImageCopy);
+
+    commandBuffer->end();
+
+    auto transferFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
+    ASSERT_THAT(transferFence, IsValidHandle());
+
+    // Execute the command to copy image
+    const vkhpp::SubmitInfo submitInfo = {
+        .commandBufferCount = 1,
+        .pCommandBuffers = &commandBuffer.get(),
+    };
+    queue.submit(submitInfo, *transferFence);
+
+    auto waitResult = device->waitForFences(*transferFence, VK_TRUE, 3000000000L);
+    ASSERT_THAT(waitResult, IsVkSuccess());
+
+    // 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);
+
+    for (uint32_t i = 0; i < kSize; ++i) {
+        ASSERT_THAT(bytes[i], Eq(srcBufferContent[i]));
+    }
+    device->unmapMemory(*readbackBufferMemory);
+}
+
 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotImageTest,
                         ::testing::ValuesIn({
                             TestParams{
diff --git a/common/end2end/GfxstreamEnd2EndVkTests.cpp b/common/end2end/GfxstreamEnd2EndVkTests.cpp
index 9ed8f76..087e829 100644
--- a/common/end2end/GfxstreamEnd2EndVkTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkTests.cpp
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "GfxstreamEnd2EndTests.h"
+#include <log/log.h>
 
+#include "GfxstreamEnd2EndTests.h"
 #include "drm_fourcc.h"
 
 namespace gfxstream {
@@ -33,87 +34,7 @@
     return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count());
 }
 
-class GfxstreamEnd2EndVkTest : public GfxstreamEnd2EndTest {
-   protected:
-    // Gfxstream uses a vkQueueSubmit() to signal the VkFence and VkSemaphore used
-    // in vkAcquireImageANDROID() calls. The guest is not aware of this and may try
-    // to vkDestroyFence() and vkDestroySemaphore() (because the VkImage, VkFence,
-    // and VkSemaphore may have been unused from the guest point of view) while the
-    // host's command buffer is running. Gfxstream needs to ensure that it performs
-    // the necessary tracking to not delete the VkFence and VkSemaphore while they
-    // are in use on the host.
-    void DoAcquireImageAndroidWithSync(bool withFence, bool withSemaphore) {
-        auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
-            VK_ASSERT(SetUpTypicalVkTestEnvironment());
-
-        const uint32_t width = 32;
-        const uint32_t height = 32;
-        auto ahb = mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888);
-
-        const VkNativeBufferANDROID imageNativeBufferInfo = {
-            .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
-            .handle = ahb->asBufferHandle(),
-        };
-
-        auto vkAcquireImageANDROID =
-            PFN_vkAcquireImageANDROID(device->getProcAddr("vkAcquireImageANDROID"));
-        ASSERT_THAT(vkAcquireImageANDROID, NotNull());
-
-        const vkhpp::ImageCreateInfo imageCreateInfo = {
-            .pNext = &imageNativeBufferInfo,
-            .imageType = vkhpp::ImageType::e2D,
-            .extent.width = width,
-            .extent.height = height,
-            .extent.depth = 1,
-            .mipLevels = 1,
-            .arrayLayers = 1,
-            .format = vkhpp::Format::eR8G8B8A8Unorm,
-            .tiling = vkhpp::ImageTiling::eOptimal,
-            .initialLayout = vkhpp::ImageLayout::eUndefined,
-            .usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
-                     vkhpp::ImageUsageFlagBits::eTransferSrc,
-            .sharingMode = vkhpp::SharingMode::eExclusive,
-            .samples = vkhpp::SampleCountFlagBits::e1,
-        };
-        auto image = device->createImageUnique(imageCreateInfo).value;
-
-        vkhpp::MemoryRequirements imageMemoryRequirements{};
-        device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
-
-        const uint32_t imageMemoryIndex = GetMemoryType(
-            physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
-        ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
-
-        const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
-            .allocationSize = imageMemoryRequirements.size,
-            .memoryTypeIndex = imageMemoryIndex,
-        };
-
-        auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
-        ASSERT_THAT(imageMemory, IsValidHandle());
-        ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
-
-        vkhpp::UniqueFence fence;
-        if (withFence) {
-            fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
-        }
-
-        vkhpp::UniqueSemaphore semaphore;
-        if (withSemaphore) {
-            semaphore = device->createSemaphoreUnique(vkhpp::SemaphoreCreateInfo()).value;
-        }
-
-        auto result = vkAcquireImageANDROID(*device, *image, -1, *semaphore, *fence);
-        ASSERT_THAT(result, Eq(VK_SUCCESS));
-
-        if (withFence) {
-            fence.reset();
-        }
-        if (withSemaphore) {
-            semaphore.reset();
-        }
-    }
-};
+class GfxstreamEnd2EndVkTest : public GfxstreamEnd2EndTest {};
 
 TEST_P(GfxstreamEnd2EndVkTest, Basic) {
     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
@@ -126,11 +47,12 @@
 
     const uint32_t width = 32;
     const uint32_t height = 32;
-    auto ahb = mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888);
+    AHardwareBuffer* ahb = nullptr;
+    ASSERT_THAT(mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888, -1, &ahb), Eq(0));
 
     const VkNativeBufferANDROID imageNativeBufferInfo = {
         .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
-        .handle = ahb->asBufferHandle(),
+        .handle = mGralloc->getNativeHandle(ahb),
     };
 
     auto vkQueueSignalReleaseImageANDROID = PFN_vkQueueSignalReleaseImageANDROID(
@@ -243,6 +165,8 @@
     ASSERT_THAT(fence, Not(Eq(-1)));
 
     ASSERT_THAT(mSync->wait(fence, 3000), Eq(0));
+
+    mGralloc->release(ahb);
 }
 
 TEST_P(GfxstreamEnd2EndVkTest, DeferredImportAHB) {
@@ -251,7 +175,8 @@
 
     const uint32_t width = 32;
     const uint32_t height = 32;
-    auto ahb = mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888);
+    AHardwareBuffer* ahb = nullptr;
+    ASSERT_THAT(mGralloc->allocate(width, height, DRM_FORMAT_ABGR8888, -1, &ahb), Eq(0));
 
     auto vkQueueSignalReleaseImageANDROID = PFN_vkQueueSignalReleaseImageANDROID(
         device->getProcAddr("vkQueueSignalReleaseImageANDROID"));
@@ -279,7 +204,7 @@
     // NOTE: Binding the VkImage to the AHB happens after the VkImage is created.
     const VkNativeBufferANDROID imageNativeBufferInfo = {
         .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
-        .handle = ahb->asBufferHandle(),
+        .handle = mGralloc->getNativeHandle(ahb),
     };
 
     const vkhpp::BindImageMemoryInfo imageBindMemoryInfo = {
@@ -298,6 +223,8 @@
     ASSERT_THAT(fence, Not(Eq(-1)));
 
     ASSERT_THAT(mSync->wait(fence, 3000), Eq(0));
+
+    mGralloc->release(ahb);
 }
 
 TEST_P(GfxstreamEnd2EndVkTest, HostMemory) {
@@ -519,18 +446,6 @@
     EXPECT_THAT(device->freeDescriptorSets(*bundle.descriptorPool, kNumSets, descriptorSetHandles.data()), IsVkSuccess());
 }
 
-TEST_P(GfxstreamEnd2EndVkTest, AcquireImageAndroidWithFence) {
-    DoAcquireImageAndroidWithSync(/*withFence=*/true, /*withSemaphore=*/false);
-}
-
-TEST_P(GfxstreamEnd2EndVkTest, AcquireImageAndroidWithSemaphore) {
-    DoAcquireImageAndroidWithSync(/*withFence=*/false, /*withSemaphore=*/true);
-}
-
-TEST_P(GfxstreamEnd2EndVkTest, AcquireImageAndroidWithFenceAndSemaphore) {
-    DoAcquireImageAndroidWithSync(/*withFence=*/true, /*withSemaphore=*/true);
-}
-
 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkTest,
                         ::testing::ValuesIn({
                             TestParams{
diff --git a/guest/BUILD.gn b/guest/BUILD.gn
index 520a365..553cf63 100644
--- a/guest/BUILD.gn
+++ b/guest/BUILD.gn
@@ -12,6 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/python/python_action.gni")
+import("//build/python/python_binary.gni")
+import("mesa-gn/mesa.gni")
+
 config("common_config") {
   defines = [
     "LOG_TAG=\"gfxstream_vulkan\"",
@@ -30,9 +34,11 @@
     "qemupipe/include",
     "qemupipe/include-types",
     "OpenglCodecCommon",
+    "OpenglCodecCommon/include",
     "OpenglSystemCommon",
     "renderControl_enc",
     "vulkan_enc",
+    "$mesa_source_root/include",
     "platform/include",
   ]
 
@@ -53,31 +59,79 @@
   ]
 }
 
+python_binary("vk_entrypoints_gen_bin") {
+  main_source = "$mesa_source_root/src/vulkan/util/vk_entrypoints_gen.py"
+
+  sources = [
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+  ]
+  deps = [ "//third_party/mako" ]
+}
+
+config("gfxstream_vk_entrypoints_config") {
+  include_dirs = [ "$target_gen_dir" ]
+}
+
+python_action("gfxstream_vk_entrypoints_header") {
+  public_configs = [ ":gfxstream_vk_entrypoints_config" ]
+
+  binary_label = ":vk_entrypoints_gen_bin"
+
+  sources = [ mesa_vk_xml ]
+
+  h_file = "$target_gen_dir/gfxstream_vk_entrypoints.h"
+  c_file = "$target_gen_dir/placeholder.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml, root_build_dir),
+    "--proto",
+    "--weak",
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--prefix",
+    "gfxstream_vk",
+    "--beta",
+    "false",
+  ]
+}
+
 # These sources require access to the Fuchsia goldfish protocols.
 source_set("goldfish_srcs") {
   configs += [ ":common_config" ]
 
   sources = [
-    "vulkan/goldfish_vulkan.cpp",
+    "vulkan/gfxstream_vk_fuchsia.cpp",
     "vulkan_enc/ResourceTracker.cpp",
     "vulkan_enc/ResourceTracker.h",
   ]
 
   public_deps = [
+    "$mesa_build_root/src/vulkan/runtime",
+    "$mesa_build_root/src/vulkan/util",
     "//sdk/fidl/fuchsia.hardware.goldfish:fuchsia.hardware.goldfish_cpp",
     "//sdk/fidl/fuchsia.logger:fuchsia.logger_cpp",
     "//sdk/fidl/fuchsia.sysmem:fuchsia.sysmem_cpp",
+    "//sdk/lib/syslog/structured_backend",
     "//third_party/Vulkan-Headers:vulkan_headers",
-    "//third_party/mesa/include:drm-uapi",
     "//zircon/system/ulib/async:async-cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
-    "//zircon/system/ulib/syslog:syslog-static",
     "//zircon/system/ulib/trace:trace-with-static-engine",
     "//zircon/system/ulib/trace-provider:trace-provider-with-static-engine",
     "//zircon/system/ulib/zx",
     "//zircon/system/ulib/zxio",
   ]
 
+  deps = [ ":gfxstream_vk_entrypoints_header" ]
+
   defines = [
     "QEMU_PIPE_PATH=\"/loader-gpu-devices/class/goldfish-pipe/000\"",
     "GOLDFISH_ADDRESS_SPACE_DEVICE_NAME=\"/loader-gpu-devices/class/goldfish-address-space/000\"",
@@ -87,6 +141,11 @@
 loadable_module("libvulkan_gfxstream") {
   configs += [ ":common_config" ]
 
+  deps = [
+    ":gfxstream_vk_entrypoints_header",
+    "$mesa_build_root/src/vulkan/runtime",
+  ]
+
   sources = [
     "GoldfishAddressSpace/AddressSpaceStream.cpp",
     "GoldfishAddressSpace/VirtioGpuAddressSpaceStream.cpp",
@@ -94,14 +153,12 @@
     "OpenglCodecCommon/ChecksumCalculator.h",
     "OpenglCodecCommon/glUtils.cpp",
     "OpenglCodecCommon/glUtils.h",
-    "OpenglCodecCommon/goldfish_dma.cpp",
-    "OpenglCodecCommon/goldfish_dma.h",
     "OpenglSystemCommon/HostConnection.cpp",
     "OpenglSystemCommon/HostConnection.h",
     "OpenglSystemCommon/ProcessPipe.cpp",
     "OpenglSystemCommon/ProcessPipe.h",
-    "OpenglSystemCommon/QemuPipeStream.h",
     "OpenglSystemCommon/QemuPipeStream.cpp",
+    "OpenglSystemCommon/QemuPipeStream.h",
     "OpenglSystemCommon/ThreadInfo.cpp",
     "OpenglSystemCommon/ThreadInfo.h",
     "OpenglSystemCommon/VirtioGpuPipeStream.cpp",
@@ -198,14 +255,12 @@
 
   if (target_os == "fuchsia") {
     sources -= [
-      "OpenglCodecCommon/goldfish_dma.cpp",
-      "OpenglCodecCommon/goldfish_dma.h",
       "OpenglSystemCommon/QemuPipeStream.cpp",
-      "qemupipe/qemu_pipe_common.cpp",
-      "qemupipe/qemu_pipe_guest.cpp",
       "platform/stub/StubVirtGpuBlob.cpp",
       "platform/stub/StubVirtGpuBlobMapping.cpp",
       "platform/stub/StubVirtGpuDevice.cpp",
+      "qemupipe/qemu_pipe_common.cpp",
+      "qemupipe/qemu_pipe_guest.cpp",
     ]
     sources += [
       "OpenglSystemCommon/TraceProviderFuchsia.cpp",
@@ -224,7 +279,7 @@
       "//third_party/Vulkan-Headers/include",
     ]
 
-    deps = [
+    deps += [
       ":goldfish_srcs",
       "//sdk/fidl/fuchsia.logger:fuchsia.logger_cpp",
       "//sdk/fidl/fuchsia.sysmem:fuchsia.sysmem_cpp",
diff --git a/guest/GLESv2_enc/GL2Encoder.cpp b/guest/GLESv2_enc/GL2Encoder.cpp
index 5c44a27..937a18a 100755
--- a/guest/GLESv2_enc/GL2Encoder.cpp
+++ b/guest/GLESv2_enc/GL2Encoder.cpp
@@ -3424,37 +3424,7 @@
     buf->m_mappedOffset = offset;
     buf->m_mappedLength = length;
 
-    if (ctx->hasExtension("ANDROID_EMU_dma_v2")) {
-        if (buf->dma_buffer.get().size < length) {
-            goldfish_dma_context region;
-
-            const int PAGE_BITS = 12;
-            GLsizeiptr aligned_length = (length + (1 << PAGE_BITS) - 1) & ~((1 << PAGE_BITS) - 1);
-
-            if (goldfish_dma_create_region(aligned_length, &region)) {
-                buf->dma_buffer.reset(NULL);
-                return s_glMapBufferRangeAEMUImpl(ctx, target, offset, length, access, buf);
-            }
-
-            if (!goldfish_dma_map(&region)) {
-                buf->dma_buffer.reset(NULL);
-                return s_glMapBufferRangeAEMUImpl(ctx, target, offset, length, access, buf);
-            }
-
-            buf->m_guest_paddr = goldfish_dma_guest_paddr(&region);
-            buf->dma_buffer.reset(&region);
-        }
-
-        ctx->glMapBufferRangeDMA(
-                ctx, target,
-                offset, length,
-                access,
-                buf->m_guest_paddr);
-
-        return reinterpret_cast<void*>(buf->dma_buffer.get().mapped_addr);
-    } else {
-        return s_glMapBufferRangeAEMUImpl(ctx, target, offset, length, access, buf);
-    }
+    return s_glMapBufferRangeAEMUImpl(ctx, target, offset, length, access, buf);
 }
 
 GLboolean GL2Encoder::s_glUnmapBuffer(void* self, GLenum target) {
@@ -3481,37 +3451,23 @@
 
     GLboolean host_res = GL_TRUE;
 
-    if (buf->dma_buffer.get().mapped_addr) {
-        memcpy(&buf->m_fixedBuffer[buf->m_mappedOffset],
-               reinterpret_cast<void*>(buf->dma_buffer.get().mapped_addr),
-               buf->m_mappedLength);
-
-        ctx->glUnmapBufferDMA(
-            ctx, target,
-            buf->m_mappedOffset,
-            buf->m_mappedLength,
-            buf->m_mappedAccess,
-            goldfish_dma_guest_paddr(&buf->dma_buffer.get()),
-            &host_res);
+    if (ctx->m_hasAsyncUnmapBuffer) {
+        ctx->glUnmapBufferAsyncAEMU(
+                ctx, target,
+                buf->m_mappedOffset,
+                buf->m_mappedLength,
+                buf->m_mappedAccess,
+                &buf->m_fixedBuffer[buf->m_mappedOffset],
+                &host_res);
     } else {
-        if (ctx->m_hasAsyncUnmapBuffer) {
-            ctx->glUnmapBufferAsyncAEMU(
+        if (buf->m_mappedAccess & GL_MAP_WRITE_BIT) {
+            ctx->glUnmapBufferAEMU(
                     ctx, target,
                     buf->m_mappedOffset,
                     buf->m_mappedLength,
                     buf->m_mappedAccess,
                     &buf->m_fixedBuffer[buf->m_mappedOffset],
                     &host_res);
-        } else {
-            if (buf->m_mappedAccess & GL_MAP_WRITE_BIT) {
-                ctx->glUnmapBufferAEMU(
-                        ctx, target,
-                        buf->m_mappedOffset,
-                        buf->m_mappedLength,
-                        buf->m_mappedAccess,
-                        &buf->m_fixedBuffer[buf->m_mappedOffset],
-                        &host_res);
-            }
         }
     }
 
diff --git a/guest/GoldfishAddressSpace/include/goldfish_address_space_fuchsia.impl b/guest/GoldfishAddressSpace/include/goldfish_address_space_fuchsia.impl
index 57e394b..c8a7ae9 100644
--- a/guest/GoldfishAddressSpace/include/goldfish_address_space_fuchsia.impl
+++ b/guest/GoldfishAddressSpace/include/goldfish_address_space_fuchsia.impl
@@ -133,7 +133,7 @@
         return false;
     }
     m_phys_addr = result->paddr;
-    // TODO(fxbug.dev/124700): Instead of storing raw handles we should
+    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
     // consider using RAII zx::object instead.
     m_vmo = result->vmo.release();
 
@@ -363,7 +363,7 @@
 
     *offset = 0;
 
-    // TODO(fxbug.dev/124700): Instead of storing raw handles we should
+    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
     // consider using RAII zx::object instead.
     VmoStore::Info info = {
         vmo.release(),
@@ -408,7 +408,7 @@
     }
     vmo = std::move(result->vmo);
 
-    // TODO(fxbug.dev/124700): Instead of storing raw handles we should
+    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
     // consider using RAII zx::object instead.
     VmoStore::Info info = {
         vmo.release(),
diff --git a/guest/OpenglCodecCommon/Android.bp b/guest/OpenglCodecCommon/Android.bp
index 1215b89..2b7b838 100644
--- a/guest/OpenglCodecCommon/Android.bp
+++ b/guest/OpenglCodecCommon/Android.bp
@@ -65,14 +65,12 @@
         "-Wno-unused-private-field",
     ],
     srcs: [
-        "auto_goldfish_dma_context.cpp",
         "ChecksumCalculator.cpp",
         "EncoderDebug.cpp",
         "GLClientState.cpp",
         "GLESTextureUtils.cpp",
         "GLSharedGroup.cpp",
         "glUtils.cpp",
-        "goldfish_dma.cpp",
         "IndexRangeCache.cpp",
     ],
     export_include_dirs: [
diff --git a/guest/OpenglCodecCommon/auto_goldfish_dma_context.cpp b/guest/OpenglCodecCommon/auto_goldfish_dma_context.cpp
deleted file mode 100644
index c638c2b..0000000
--- a/guest/OpenglCodecCommon/auto_goldfish_dma_context.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2018 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 "auto_goldfish_dma_context.h"
-
-namespace {
-goldfish_dma_context empty() {
-    goldfish_dma_context ctx;
-
-    ctx.mapped_addr = 0;
-    ctx.size = 0;
-    ctx.fd = -1;
-
-    return ctx;
-}
-
-void destroy(goldfish_dma_context *ctx) {
-    if (ctx->mapped_addr) {
-        goldfish_dma_unmap(ctx);
-    }
-    if (ctx->fd > 0) {
-        goldfish_dma_free(ctx);
-    }
-}
-}  // namespace
-
-AutoGoldfishDmaContext::AutoGoldfishDmaContext() : m_ctx(empty()) {}
-
-AutoGoldfishDmaContext::AutoGoldfishDmaContext(goldfish_dma_context *ctx)
-    : m_ctx(*ctx) {
-    *ctx = empty();
-}
-
-AutoGoldfishDmaContext::~AutoGoldfishDmaContext() {
-    destroy(&m_ctx);
-}
-
-void AutoGoldfishDmaContext::reset(goldfish_dma_context *ctx) {
-    destroy(&m_ctx);
-    if (ctx) {
-        m_ctx = *ctx;
-        *ctx = empty();
-    } else {
-        m_ctx = empty();
-    }
-}
-
-goldfish_dma_context AutoGoldfishDmaContext::release() {
-    goldfish_dma_context copy = m_ctx;
-    m_ctx = empty();
-    return copy;
-}
-
diff --git a/guest/OpenglCodecCommon/auto_goldfish_dma_context.h b/guest/OpenglCodecCommon/auto_goldfish_dma_context.h
deleted file mode 100644
index c8ee738..0000000
--- a/guest/OpenglCodecCommon/auto_goldfish_dma_context.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef ANDROID_INCLUDE_HARDWARE_AUTO_GOLDFISH_DMA_CONTEXT_H
-#define ANDROID_INCLUDE_HARDWARE_AUTO_GOLDFISH_DMA_CONTEXT_H
-
-#include <inttypes.h>
-#include "goldfish_dma.h"
-
-// A C++ wrapper for goldfish_dma_context that releases resources in dctor.
-class AutoGoldfishDmaContext {
-public:
-    AutoGoldfishDmaContext();
-    explicit AutoGoldfishDmaContext(goldfish_dma_context *ctx);
-    ~AutoGoldfishDmaContext();
-
-    const goldfish_dma_context &get() const { return m_ctx; }
-    void reset(goldfish_dma_context *ctx);
-    goldfish_dma_context release();
-
-private:
-    AutoGoldfishDmaContext(const AutoGoldfishDmaContext &rhs);
-    AutoGoldfishDmaContext& operator=(const AutoGoldfishDmaContext &rhs);
-
-    goldfish_dma_context m_ctx;
-};
-
-#endif  // ANDROID_INCLUDE_HARDWARE_AUTO_GOLDFISH_DMA_CONTEXT_H
diff --git a/guest/OpenglCodecCommon/goldfish_dma.cpp b/guest/OpenglCodecCommon/goldfish_dma.cpp
deleted file mode 100644
index f3cefec..0000000
--- a/guest/OpenglCodecCommon/goldfish_dma.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2016 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 "goldfish_dma.h"
-
-#include <qemu_pipe_bp.h>
-
-#if PLATFORM_SDK_VERSION < 26
-#include <cutils/log.h>
-#else
-#include <log/log.h>
-#endif
-#include <errno.h>
-#ifdef __ANDROID__
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <sys/cdefs.h>
-#endif
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* There is an ioctl associated with goldfish dma driver.
- * Make it conflict with ioctls that are not likely to be used
- * in the emulator.
- * 'G'	00-3F	drivers/misc/sgi-gru/grulib.h	conflict!
- * 'G'	00-0F	linux/gigaset_dev.h	conflict!
- */
-#define GOLDFISH_DMA_IOC_MAGIC	'G'
-
-#define GOLDFISH_DMA_IOC_LOCK			_IOWR(GOLDFISH_DMA_IOC_MAGIC, 0, struct goldfish_dma_ioctl_info)
-#define GOLDFISH_DMA_IOC_UNLOCK			_IOWR(GOLDFISH_DMA_IOC_MAGIC, 1, struct goldfish_dma_ioctl_info)
-#define GOLDFISH_DMA_IOC_GETOFF			_IOWR(GOLDFISH_DMA_IOC_MAGIC, 2, struct goldfish_dma_ioctl_info)
-#define GOLDFISH_DMA_IOC_CREATE_REGION	_IOWR(GOLDFISH_DMA_IOC_MAGIC, 3, struct goldfish_dma_ioctl_info)
-
-struct goldfish_dma_ioctl_info {
-    uint64_t phys_begin;
-    uint64_t size;
-};
-
-int goldfish_dma_create_region(uint32_t sz, struct goldfish_dma_context* res) {
-
-    res->fd = qemu_pipe_open("opengles");
-    res->mapped_addr = 0;
-    res->size = 0;
-
-    if (res->fd > 0) {
-        // now alloc
-        struct goldfish_dma_ioctl_info info;
-        info.size = sz;
-        int alloc_res = ioctl(res->fd, GOLDFISH_DMA_IOC_CREATE_REGION, &info);
-
-        if (alloc_res) {
-            ALOGE("%s: failed to allocate DMA region. errno=%d",
-                  __FUNCTION__, errno);
-            close(res->fd);
-            res->fd = -1;
-            return alloc_res;
-        }
-
-        res->size = sz;
-        ALOGV("%s: successfully allocated goldfish DMA region with size %u cxt=%p fd=%d",
-              __FUNCTION__, sz, res, res->fd);
-        return 0;
-    } else {
-        ALOGE("%s: could not obtain fd to device! fd %d errno=%d\n",
-              __FUNCTION__, res->fd, errno);
-        return ENODEV;
-    }
-}
-
-void* goldfish_dma_map(struct goldfish_dma_context* cxt) {
-    ALOGV("%s: on fd %d errno=%d", __FUNCTION__, cxt->fd, errno);
-    void *mapped = mmap(0, cxt->size, PROT_WRITE, MAP_SHARED, cxt->fd, 0);
-    ALOGV("%s: cxt=%p mapped=%p size=%u errno=%d",
-        __FUNCTION__, cxt, mapped, cxt->size, errno);
-
-    if (mapped == MAP_FAILED) {
-        mapped = NULL;
-    }
-
-    cxt->mapped_addr = reinterpret_cast<uint64_t>(mapped);
-    return mapped;
-}
-
-int goldfish_dma_unmap(struct goldfish_dma_context* cxt) {
-    ALOGV("%s: cxt=%p mapped=0x%" PRIu64, __FUNCTION__, cxt, cxt->mapped_addr);
-    munmap(reinterpret_cast<void *>(cxt->mapped_addr), cxt->size);
-    cxt->mapped_addr = 0;
-    cxt->size = 0;
-    return 0;
-}
-
-void goldfish_dma_write(struct goldfish_dma_context* cxt,
-                               const void* to_write,
-                               uint32_t sz) {
-    ALOGV("%s: cxt=%p mapped=0x%" PRIu64 " to_write=%p size=%u",
-        __FUNCTION__, cxt, cxt->mapped_addr, to_write, sz);
-    memcpy(reinterpret_cast<void *>(cxt->mapped_addr), to_write, sz);
-}
-
-void goldfish_dma_free(goldfish_dma_context* cxt) {
-    close(cxt->fd);
-}
-
-uint64_t goldfish_dma_guest_paddr(const struct goldfish_dma_context* cxt) {
-    struct goldfish_dma_ioctl_info info;
-    ioctl(cxt->fd, GOLDFISH_DMA_IOC_GETOFF, &info);
-    return info.phys_begin;
-}
diff --git a/guest/OpenglCodecCommon/goldfish_dma.h b/guest/OpenglCodecCommon/goldfish_dma.h
deleted file mode 100644
index 278e035..0000000
--- a/guest/OpenglCodecCommon/goldfish_dma.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 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.
-
-#ifndef ANDROID_INCLUDE_HARDWARE_GOLDFISH_DMA_H
-#define ANDROID_INCLUDE_HARDWARE_GOLDFISH_DMA_H
-
-#include <inttypes.h>
-
-// userspace interface
-struct goldfish_dma_context {
-	uint64_t mapped_addr;
-	uint32_t size;
-	int32_t fd;
-};
-
-int goldfish_dma_create_region(uint32_t sz, struct goldfish_dma_context* res);
-
-void* goldfish_dma_map(struct goldfish_dma_context* cxt);
-int goldfish_dma_unmap(struct goldfish_dma_context* cxt);
-
-void goldfish_dma_write(struct goldfish_dma_context* cxt,
-                        const void* to_write,
-                        uint32_t sz);
-
-void goldfish_dma_free(goldfish_dma_context* cxt);
-uint64_t goldfish_dma_guest_paddr(const struct goldfish_dma_context* cxt);
-
-#endif
diff --git a/guest/OpenglCodecCommon/goldfish_dma_host.cpp b/guest/OpenglCodecCommon/goldfish_dma_host.cpp
deleted file mode 100644
index 4ca027b..0000000
--- a/guest/OpenglCodecCommon/goldfish_dma_host.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2018 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 "goldfish_dma.h"
-
-int goldfish_dma_create_region(uint32_t sz, struct goldfish_dma_context* res) {
-    return 0;
-}
-
-void* goldfish_dma_map(struct goldfish_dma_context* cxt) {
-    return 0;
-}
-
-int goldfish_dma_unmap(struct goldfish_dma_context* cxt) {
-    return 0;
-}
-
-void goldfish_dma_write(struct goldfish_dma_context* cxt,
-                               const void* to_write,
-                               uint32_t sz) {
-}
-
-void goldfish_dma_free(goldfish_dma_context* cxt) {
-}
-
-uint64_t goldfish_dma_guest_paddr(const struct goldfish_dma_context* cxt) {
-    return 0;
-}
-
diff --git a/guest/OpenglCodecCommon/include/gfxstream/guest/GLSharedGroup.h b/guest/OpenglCodecCommon/include/gfxstream/guest/GLSharedGroup.h
index d5475d9..86c4d62 100755
--- a/guest/OpenglCodecCommon/include/gfxstream/guest/GLSharedGroup.h
+++ b/guest/OpenglCodecCommon/include/gfxstream/guest/GLSharedGroup.h
@@ -37,7 +37,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "ErrorLog.h"
-#include "auto_goldfish_dma_context.h"
 #include "IndexRangeCache.h"
 #include "StateTrackingSupport.h"
 
@@ -65,9 +64,6 @@
     // Internal bookkeeping
     std::vector<char> m_fixedBuffer; // actual buffer is shadowed here
     IndexRangeCache m_indexRangeCache;
-
-    // DMA support
-    AutoGoldfishDmaContext dma_buffer;
 };
 
 class ProgramData {
diff --git a/guest/OpenglCodecCommon/meson.build b/guest/OpenglCodecCommon/meson.build
index 67463ce..4e11e34 100644
--- a/guest/OpenglCodecCommon/meson.build
+++ b/guest/OpenglCodecCommon/meson.build
@@ -5,7 +5,6 @@
 
 files_lib_codec_common = files(
   'ChecksumCalculator.cpp',
-  'goldfish_dma.cpp',
   'glUtils.cpp',
 )
 
diff --git a/guest/OpenglSystemCommon/Android.bp b/guest/OpenglSystemCommon/Android.bp
index 0cf3aaa..3cb00d6 100644
--- a/guest/OpenglSystemCommon/Android.bp
+++ b/guest/OpenglSystemCommon/Android.bp
@@ -19,50 +19,6 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
-cc_library_static {
-    name: "libgfxstream_guest_gralloc",
-    vendor: true,
-    host_supported: true,
-    defaults: [
-        "libgfxstream_guest_cc_defaults",
-    ],
-    header_libs: [
-        "libdrm_headers",
-        "libgfxstream_guest_graphics_headers",
-        "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
-        "minigbm_headers",
-    ],
-    export_header_lib_headers: [
-        "libgfxstream_guest_graphics_headers",
-        "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
-    ],
-    shared_libs: [
-        "lib_renderControl_enc",
-    ],
-    export_shared_lib_headers: [
-        "lib_renderControl_enc",
-    ],
-    export_include_dirs: [
-        ".",
-    ],
-    target: {
-        android: {
-            shared_libs: [
-                "libnativewindow",
-            ],
-            export_shared_lib_headers: [
-                "libnativewindow",
-            ],
-            srcs: [
-                "GrallocGoldfish.cpp",
-                "GrallocMinigbm.cpp",
-            ],
-        },
-    },
-}
-
 cc_library_headers {
     name: "libgfxstream_guest_sync",
     export_include_dirs: [
@@ -70,6 +26,24 @@
     ],
 }
 
+cc_library_headers {
+    name: "libOpenglSystemCommonHeaders",
+    host_supported: true,
+    vendor: true,
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+    header_libs: [
+        "libgfxstream_guest_rendercontrol_encoder_headers",
+    ],
+    export_header_lib_headers: [
+        "libgfxstream_guest_rendercontrol_encoder_headers",
+    ],
+}
+
 cc_defaults {
     name: "libOpenglSystemCommonDefaults",
     host_supported: true,
@@ -81,15 +55,12 @@
         "gfxstream_vulkan_headers",
         "libgfxstream_guest_graphics_headers",
         "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
         "libhardware_headers",
         "libnativebase_headers",
-        "minigbm_headers",
     ],
     export_header_lib_headers: [
         "libgfxstream_guest_graphics_headers",
         "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
         "libhardware_headers",
         "libnativebase_headers",
     ],
@@ -112,10 +83,6 @@
         "libGoldfishAddressSpace",
         "libringbuffer",
         "libqemupipe.ranchu",
-        "libgfxstream_guest_gralloc",
-    ],
-    export_static_lib_headers: [
-        "libgfxstream_guest_gralloc",
     ],
     cflags: [
         "-Wno-unused-variable",
@@ -140,18 +107,6 @@
             export_header_lib_headers: [
                 "gfxstream_opengl_headers",
             ],
-            shared_libs: [
-                "libnativewindow",
-                "libsync",
-            ],
-            srcs: [
-                "ANativeWindowAndroid.cpp",
-            ],
-        },
-        host: {
-            srcs: [
-                "TestingAndroidWsi.cpp",
-            ],
         },
     },
 }
@@ -165,12 +120,22 @@
         "libandroidemu",
     ],
     static_libs: [
+        "libgfxstream_guest_android",
         "libvulkan_enc",
         "libplatform",
     ],
     export_static_lib_headers: [
+        "libgfxstream_guest_android",
         "libplatform",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libnativewindow",
+                "libsync",
+            ],
+        }
+    },
 }
 
 cc_library_shared {
@@ -179,14 +144,16 @@
         "libOpenglSystemCommonDefaults",
     ],
     shared_libs: [
-        "libgfxstream_backend",
-        "libplatform_rutabaga",
         "libandroidemu",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
+        "libgfxstream_guest_android_with_host",
         "libgfxstream_guest_vulkan_encoder_with_host",
+        "libplatform_rutabaga",
     ],
-    export_shared_lib_headers: [
+    export_static_lib_headers: [
+        "libgfxstream_guest_android_with_host",
         "libplatform_rutabaga",
     ],
     target: {
@@ -209,9 +176,11 @@
     ],
     static_libs: [
         "libandroidemu_static",
+        "libgfxstream_guest_android",
         "libplatform",
     ],
     export_static_lib_headers: [
+        "libgfxstream_guest_android",
         "libplatform",
     ],
 }
@@ -225,13 +194,15 @@
         "vulkan_enc_headers",
     ],
     shared_libs: [
-        "libgfxstream_backend",
-        "libplatform_rutabaga",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
         "libandroidemu_static",
+        "libgfxstream_guest_android_with_host",
+        "libplatform_rutabaga",
     ],
-    export_shared_lib_headers: [
+    export_static_lib_headers: [
+        "libgfxstream_guest_android_with_host",
         "libplatform_rutabaga",
     ],
     target: {
diff --git a/guest/OpenglSystemCommon/HostConnection.cpp b/guest/OpenglSystemCommon/HostConnection.cpp
index 0d855dd..b82cab0 100644
--- a/guest/OpenglSystemCommon/HostConnection.cpp
+++ b/guest/OpenglSystemCommon/HostConnection.cpp
@@ -15,12 +15,7 @@
 */
 #include "HostConnection.h"
 
-#if defined(__ANDROID__)
-#include "ANativeWindowAndroid.h"
-#endif
 #include "GoldfishAddressSpaceStream.h"
-#include "GrallocGoldfish.h"
-#include "GrallocMinigbm.h"
 #include "VirtioGpuAddressSpaceStream.h"
 #include "aemu/base/AndroidHealthMonitor.h"
 #include "aemu/base/AndroidHealthMonitorConsumerBasic.h"
@@ -45,8 +40,6 @@
 using gfxstream::guest::HealthMonitor;
 using gfxstream::guest::HealthMonitorConsumerBasic;
 using gfxstream::guest::IOStream;
-using gfxstream::GoldfishGralloc;
-using gfxstream::MinigbmGralloc;
 
 #ifdef GOLDFISH_NO_GL
 struct gl_client_context_t {
@@ -89,9 +82,9 @@
 
 #include "VirtGpu.h"
 #include "VirtioGpuPipeStream.h"
-#include "virtgpu_drm.h"
 
 #if defined(__linux__) || defined(__ANDROID__)
+#include "virtgpu_drm.h"
 #include <fstream>
 #include <string>
 #include <unistd.h>
@@ -178,29 +171,6 @@
     return value;
 }
 
-static GrallocType getGrallocTypeFromProperty() {
-    std::string value;
-
-#if defined(__ANDROID__)
-    value = android::base::GetProperty("ro.hardware.gralloc", "");
-#endif
-
-    if (value.empty()) {
-        return GRALLOC_TYPE_RANCHU;
-    }
-    if (value == "minigbm") {
-        return GRALLOC_TYPE_MINIGBM;
-    }
-    if (value == "ranchu") {
-        return GRALLOC_TYPE_RANCHU;
-    }
-    return GRALLOC_TYPE_RANCHU;
-}
-
-#if defined(__ANDROID__)
-static GoldfishGralloc m_goldfishGralloc;
-#endif
-
 HostConnection::HostConnection()
     : exitUncleanly(false),
       m_checksumHelper(),
@@ -216,10 +186,6 @@
         (void)m_rcEnc->rcGetRendererVersion(m_rcEnc.get());
     }
 
-    if (m_grallocType == GRALLOC_TYPE_MINIGBM) {
-        delete m_grallocHelper;
-    }
-
     if (m_vkEnc) {
         m_vkEnc->decRef();
     }
@@ -252,10 +218,7 @@
             ALOGE("Fatal: HOST_CONNECTION_ADDRESS_SPACE not supported on this host.");
             abort();
 #endif
-            con->m_grallocType = GRALLOC_TYPE_RANCHU;
-#if defined(__ANDROID__)
-            con->m_grallocHelper = &m_goldfishGralloc;
-#endif
+
             break;
         }
 #if !defined(__Fuchsia__)
@@ -269,11 +232,7 @@
                 ALOGE("Failed to connect to host (QemuPipeStream)\n");
                 return nullptr;
             }
-            con->m_grallocType = GRALLOC_TYPE_RANCHU;
             con->m_stream = stream;
-#if defined(__ANDROID__)
-            con->m_grallocHelper = &m_goldfishGralloc;
-#endif
             break;
         }
 #endif
@@ -287,26 +246,9 @@
                 ALOGE("Failed to connect to host (VirtioGpu)\n");
                 return nullptr;
             }
-            con->m_grallocType = getGrallocTypeFromProperty();
             auto rendernodeFd = stream->getRendernodeFd();
             con->m_stream = stream;
             con->m_rendernodeFd = rendernodeFd;
-#if defined(__ANDROID__)
-            switch (con->m_grallocType) {
-                case GRALLOC_TYPE_RANCHU:
-                    con->m_grallocHelper = &m_goldfishGralloc;
-                    break;
-                case GRALLOC_TYPE_MINIGBM: {
-                    MinigbmGralloc* m = new MinigbmGralloc;
-                    m->setFd(rendernodeFd);
-                    con->m_grallocHelper = m;
-                    break;
-                }
-                default:
-                    ALOGE("Fatal: Unknown gralloc type 0x%x\n", con->m_grallocType);
-                    abort();
-            }
-#endif
             break;
         }
         case HOST_CONNECTION_VIRTIO_GPU_ADDRESS_SPACE: {
@@ -320,36 +262,28 @@
                 ALOGE("Failed to create virtgpu AddressSpaceStream\n");
                 return nullptr;
             }
-            con->m_grallocType = getGrallocTypeFromProperty();
             con->m_stream = stream;
             con->m_rendernodeFd = deviceHandle;
-#if defined(__ANDROID__)
-            switch (con->m_grallocType) {
-                case GRALLOC_TYPE_RANCHU:
-                    con->m_grallocHelper = &m_goldfishGralloc;
-                    break;
-                case GRALLOC_TYPE_MINIGBM: {
-                    MinigbmGralloc* m = new gfxstream::MinigbmGralloc;
-                    m->setFd(deviceHandle);
-                    con->m_grallocHelper = m;
-                    break;
-                }
-                default:
-                    ALOGE("Fatal: Unknown gralloc type 0x%x\n", con->m_grallocType);
-                    abort();
-            }
-#endif
             break;
         }
         default:
             break;
     }
 
-#if defined(__ANDROID__)
-    con->m_anwHelper = new gfxstream::ANativeWindowHelperAndroid();
-#else
-    // Host builds are expected to set an ANW helper for testing.
+#if defined(ANDROID)
+    con->m_grallocHelper.reset(gfxstream::createPlatformGralloc(con->m_rendernodeFd));
+    if (!con->m_grallocHelper) {
+        ALOGE("Failed to create platform Gralloc!");
+        abort();
+    }
+
+    con->m_anwHelper.reset(gfxstream::createPlatformANativeWindowHelper());
+    if (!con->m_anwHelper) {
+        ALOGE("Failed to create platform ANativeWindowHelper!");
+        abort();
+    }
 #endif
+
     con->m_syncHelper.reset(gfxstream::createPlatformSyncHelper());
 
     // send zero 'clientFlags' to the host.
@@ -368,11 +302,13 @@
 
     auto fd = (connType == HOST_CONNECTION_VIRTIO_GPU_ADDRESS_SPACE) ? con->m_rendernodeFd : -1;
     processPipeInit(fd, connType, noRenderControlEnc);
+    if (!noRenderControlEnc && capset == kCapsetGfxStreamVulkan) {
+        con->rcEncoder();
+    }
+
     return con;
 }
 
-bool HostConnection::isInit() { return (getEGLThreadInfo()->hostConn != NULL); }
-
 HostConnection* HostConnection::get() { return getWithThreadInfo(getEGLThreadInfo(), kCapsetNone); }
 
 HostConnection* HostConnection::getOrCreate(enum VirtGpuCapset capset) {
diff --git a/guest/OpenglSystemCommon/HostConnection.h b/guest/OpenglSystemCommon/HostConnection.h
index 3f2a2d1..228df4e 100644
--- a/guest/OpenglSystemCommon/HostConnection.h
+++ b/guest/OpenglSystemCommon/HostConnection.h
@@ -16,28 +16,24 @@
 #ifndef __COMMON_HOST_CONNECTION_H
 #define __COMMON_HOST_CONNECTION_H
 
-#include "ANativeWindow.h"
+#if defined(ANDROID)
+#include "gfxstream/guest/ANativeWindow.h"
+#include "gfxstream/guest/Gralloc.h"
+#endif
+
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+
 #include "EmulatorFeatureInfo.h"
-#include "Gralloc.h"
 #include "Sync.h"
 #include "VirtGpu.h"
 #include "gfxstream/guest/ChecksumCalculator.h"
 #include "gfxstream/guest/IOStream.h"
 #include "renderControl_enc.h"
 
-#ifdef __Fuchsia__
-struct goldfish_dma_context;
-#else
-#include "goldfish_dma.h"
-#endif
-
-#include <mutex>
-
-#include <memory>
-#include <optional>
-#include <cstring>
-#include <string>
-
 class GLEncoder;
 struct gl_client_context_t;
 class GL2Encoder;
@@ -55,8 +51,7 @@
 public:
     ExtendedRCEncoderContext(gfxstream::guest::IOStream *stream,
                              gfxstream::guest::ChecksumCalculator *checksumCalculator)
-        : renderControl_encoder_context_t(stream, checksumCalculator),
-          m_dmaCxt(NULL), m_dmaPtr(NULL), m_dmaPhysAddr(0) { }
+        : renderControl_encoder_context_t(stream, checksumCalculator) {}
     void setSyncImpl(SyncImpl syncImpl) { m_featureInfo.syncImpl = syncImpl; }
     void setDmaImpl(DmaImpl dmaImpl) { m_featureInfo.dmaImpl = dmaImpl; }
     void setHostComposition(HostComposition hostComposition) {
@@ -84,8 +79,6 @@
     bool hasHWCMultiConfigs() const {
         return m_featureInfo.hasHWCMultiConfigs;
     }
-    DmaImpl getDmaVersion() const { return m_featureInfo.dmaImpl; }
-    void bindDmaContext(struct goldfish_dma_context* cxt) { m_dmaCxt = cxt; }
     void bindDmaDirectly(void* dmaPtr, uint64_t dmaPhysAddr) {
         m_dmaPtr = dmaPtr;
         m_dmaPhysAddr = dmaPhysAddr;
@@ -96,8 +89,6 @@
                 memcpy(m_dmaPtr, data, size);
             }
             return m_dmaPhysAddr;
-        } else if (m_dmaCxt) {
-            return writeGoldfishDma(data, size, m_dmaCxt);
         } else {
             ALOGE("%s: ERROR: No DMA context bound!", __func__);
             return 0;
@@ -111,27 +102,11 @@
 
     const EmulatorFeatureInfo* featureInfo_const() const { return &m_featureInfo; }
     EmulatorFeatureInfo* featureInfo() { return &m_featureInfo; }
+
 private:
-    static uint64_t writeGoldfishDma(void* data, uint32_t size,
-                                     struct goldfish_dma_context* dmaCxt) {
-#ifdef __Fuchsia__
-        ALOGE("%s Not implemented!", __FUNCTION__);
-        return 0u;
-#else
-        ALOGV("%s(data=%p, size=%u): call", __func__, data, size);
-
-        goldfish_dma_write(dmaCxt, data, size);
-        uint64_t paddr = goldfish_dma_guest_paddr(dmaCxt);
-
-        ALOGV("%s: paddr=0x%llx", __func__, (unsigned long long)paddr);
-        return paddr;
-#endif
-    }
-
     EmulatorFeatureInfo m_featureInfo;
-    struct goldfish_dma_context* m_dmaCxt;
-    void* m_dmaPtr;
-    uint64_t m_dmaPhysAddr;
+    void* m_dmaPtr = nullptr;
+    uint64_t m_dmaPhysAddr = 0;
 };
 
 struct EGLThreadInfo;
@@ -140,7 +115,6 @@
 {
 public:
     static HostConnection *get();
-    static bool isInit();
     static HostConnection* getOrCreate(enum VirtGpuCapset capset = kCapsetNone);
 
     static HostConnection* getWithThreadInfo(EGLThreadInfo* tInfo,
@@ -162,14 +136,11 @@
 
     gfxstream::guest::ChecksumCalculator *checksumHelper() { return &m_checksumHelper; }
 
-    gfxstream::Gralloc* grallocHelper() { return m_grallocHelper; }
-    void setGrallocHelperForTesting(gfxstream::Gralloc* gralloc) { m_grallocHelper = gralloc; }
-
+#if defined(ANDROID)
+    gfxstream::ANativeWindowHelper* anwHelper() { return m_anwHelper.get(); }
+    gfxstream::Gralloc* grallocHelper() { return m_grallocHelper.get(); }
+#endif
     gfxstream::SyncHelper* syncHelper() { return m_syncHelper.get(); }
-    void setSyncHelperForTesting(gfxstream::SyncHelper* sync) { m_syncHelper.reset(sync); }
-
-    gfxstream::ANativeWindowHelper* anwHelper() { return m_anwHelper; }
-    void setANativeWindowHelperForTesting(gfxstream::ANativeWindowHelper* anw) { m_anwHelper = anw; }
 
     void flush() {
         if (m_stream) {
@@ -233,27 +204,28 @@
  GLint queryVersion(ExtendedRCEncoderContext* rcEnc);
 
 private:
-    HostConnectionType m_connectionType;
-    GrallocType m_grallocType;
+ HostConnectionType m_connectionType;
 
-    // intrusively refcounted
-    gfxstream::guest::IOStream* m_stream = nullptr;
+ // intrusively refcounted
+ gfxstream::guest::IOStream* m_stream = nullptr;
 
-    std::unique_ptr<GLEncoder> m_glEnc;
-    std::unique_ptr<GL2Encoder> m_gl2Enc;
+ std::unique_ptr<GLEncoder> m_glEnc;
+ std::unique_ptr<GL2Encoder> m_gl2Enc;
 
-    // intrusively refcounted
-    gfxstream::vk::VkEncoder* m_vkEnc = nullptr;
-    std::unique_ptr<ExtendedRCEncoderContext> m_rcEnc;
+ // intrusively refcounted
+ gfxstream::vk::VkEncoder* m_vkEnc = nullptr;
+ std::unique_ptr<ExtendedRCEncoderContext> m_rcEnc;
 
-    gfxstream::guest::ChecksumCalculator m_checksumHelper;
-    gfxstream::ANativeWindowHelper* m_anwHelper = nullptr;
-    gfxstream::Gralloc* m_grallocHelper = nullptr;
-    std::unique_ptr<gfxstream::SyncHelper> m_syncHelper;
-    std::string m_hostExtensions;
-    bool m_noHostError;
-    mutable std::mutex m_lock;
-    int m_rendernodeFd;
+ gfxstream::guest::ChecksumCalculator m_checksumHelper;
+#if defined(ANDROID)
+ std::unique_ptr<gfxstream::ANativeWindowHelper> m_anwHelper;
+ std::unique_ptr<gfxstream::Gralloc> m_grallocHelper;
+#endif
+ std::unique_ptr<gfxstream::SyncHelper> m_syncHelper;
+ std::string m_hostExtensions;
+ bool m_noHostError;
+ mutable std::mutex m_lock;
+ int m_rendernodeFd;
 };
 
 #endif
diff --git a/guest/OpenglSystemCommon/TestingAndroidWsi.cpp b/guest/OpenglSystemCommon/TestingAndroidWsi.cpp
deleted file mode 100644
index b62ff18..0000000
--- a/guest/OpenglSystemCommon/TestingAndroidWsi.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include <iostream>
-#include <cutils/log.h>
-
-#include "drm_fourcc.h"
-#include "TestingAndroidWsi.h"
-
-namespace gfxstream {
-
-static constexpr int numFds = 0;
-static constexpr int numInts = 1;
-
-std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
-    switch (glFormat) {
-        case kGlRGB:
-            return DRM_FORMAT_BGR888;
-        case kGlRGB565:
-            return DRM_FORMAT_BGR565;
-        case kGlRGBA:
-            return DRM_FORMAT_ABGR8888;
-    }
-    return std::nullopt;
-}
-
-std::optional<uint32_t> DrmToVirglFormat(uint32_t drmFormat) {
-    switch (drmFormat) {
-        case DRM_FORMAT_ABGR8888:
-            return VIRGL_FORMAT_B8G8R8A8_UNORM;
-        case DRM_FORMAT_BGR888:
-            return VIRGL_FORMAT_R8G8B8_UNORM;
-        case DRM_FORMAT_BGR565:
-            return VIRGL_FORMAT_B5G6R5_UNORM;
-    }
-    return std::nullopt;
-}
-
-TestingAHardwareBuffer::TestingAHardwareBuffer(
-        uint32_t width,
-        uint32_t height,
-        VirtGpuBlobPtr resource)
-    : mWidth(width),
-      mHeight(height),
-      mResource(resource) {
-    mHandle = native_handle_create(numFds, numInts);
-    mHandle->data[0] = mResource->getResourceHandle();
-}
-
-TestingAHardwareBuffer::~TestingAHardwareBuffer() {
-    native_handle_close(mHandle);
-}
-
-uint32_t TestingAHardwareBuffer::getResourceId() const {
-    return mResource->getResourceHandle();
-}
-
-uint32_t TestingAHardwareBuffer::getWidth() const {
-    return mWidth;
-}
-
-uint32_t TestingAHardwareBuffer::getHeight() const {
-    return mHeight;
-}
-
-int TestingAHardwareBuffer::getAndroidFormat() const {
-    return /*AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM=*/1;
-}
-
-uint32_t TestingAHardwareBuffer::getDrmFormat() const {
-    return DRM_FORMAT_ABGR8888;
-}
-
-AHardwareBuffer* TestingAHardwareBuffer::asAHardwareBuffer() {
-    return reinterpret_cast<AHardwareBuffer*>(this);
-}
-
-buffer_handle_t TestingAHardwareBuffer::asBufferHandle() {
-    return reinterpret_cast<buffer_handle_t>(mHandle);
-}
-
-EGLClientBuffer TestingAHardwareBuffer::asEglClientBuffer() {
-    return reinterpret_cast<EGLClientBuffer>(this);
-}
-
-TestingVirtGpuGralloc::TestingVirtGpuGralloc() {}
-
-uint32_t TestingVirtGpuGralloc::createColorBuffer(
-        void*,
-        int width,
-        int height,
-        uint32_t glFormat) {
-    auto drmFormat = GlFormatToDrmFormat(glFormat);
-    if (!drmFormat) {
-        ALOGE("Unhandled format");
-    }
-
-    auto ahb = allocate(width, height, *drmFormat);
-
-    uint32_t hostHandle = ahb->getResourceId();
-    mAllocatedColorBuffers.emplace(hostHandle, std::move(ahb));
-    return hostHandle;
-}
-
-int TestingVirtGpuGralloc::allocate(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format,
-        uint64_t usage,
-        AHardwareBuffer** outputAhb) {
-    (void)width;
-    (void)height;
-    (void)format;
-    (void)usage;
-    (void)outputAhb;
-
-    // TODO: support export flow?
-    ALOGE("Unimplemented");
-
-    return 0;
-}
-
-std::unique_ptr<TestingAHardwareBuffer> TestingVirtGpuGralloc::allocate(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format) {
-    ALOGE("Allocating AHB w:%u, h:%u, format %u", width, height, format);
-
-    auto device = VirtGpuDevice::getInstance();
-    if (!device) {
-        ALOGE("Failed to allocate: no virtio gpu device.");
-        return nullptr;
-    }
-
-    auto virglFormat = DrmToVirglFormat(format);
-    if (!virglFormat) {
-        std::cout << "Unhandled DRM format:" << format;
-        return nullptr;
-    }
-
-    auto resource = device->createVirglBlob(width, height, *virglFormat);
-    if (!resource) {
-        ALOGE("Failed to allocate: failed to create virtio resource.");
-        return nullptr;
-    }
-
-    resource->wait();
-
-    return std::make_unique<TestingAHardwareBuffer>(width, height, std::move(resource));
-}
-
-void TestingVirtGpuGralloc::acquire(AHardwareBuffer* ahb) {
-    // TODO
-}
-
-void TestingVirtGpuGralloc::release(AHardwareBuffer* ahb) {
-    // TODO
-}
-
-uint32_t TestingVirtGpuGralloc::getHostHandle(const native_handle_t* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getResourceId();
-}
-
-uint32_t TestingVirtGpuGralloc::getHostHandle(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getResourceId();
-}
-
-int TestingVirtGpuGralloc::getFormat(const native_handle_t* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getAndroidFormat();
-}
-
-int TestingVirtGpuGralloc::getFormat(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getAndroidFormat();
-}
-
-uint32_t TestingVirtGpuGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getDrmFormat();
-}
-
-size_t TestingVirtGpuGralloc::getAllocatedSize(const native_handle_t*) {
-    ALOGE("Unimplemented.");
-    return 0;
-}
-
-size_t TestingVirtGpuGralloc::getAllocatedSize(const AHardwareBuffer*) {
-    ALOGE("Unimplemented.");
-    return 0;
-}
-
-TestingANativeWindow::TestingANativeWindow(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format,
-        std::vector<std::unique_ptr<TestingAHardwareBuffer>> buffers)
-    : mWidth(width),
-      mHeight(height),
-      mFormat(format),
-      mBuffers(std::move(buffers)) {
-    for (auto& buffer : mBuffers) {
-        mBufferQueue.push_back(QueuedAHB{
-            .ahb = buffer.get(),
-            .fence = -1,
-        });
-    }
-}
-
-EGLNativeWindowType TestingANativeWindow::asEglNativeWindowType() {
-    return reinterpret_cast<EGLNativeWindowType>(this);
-}
-
-uint32_t TestingANativeWindow::getWidth() const {
-    return mWidth;
-}
-
-uint32_t TestingANativeWindow::getHeight() const {
-    return mHeight;
-}
-
-int TestingANativeWindow::getFormat() const {
-    return mFormat;
-}
-
-int TestingANativeWindow::queueBuffer(EGLClientBuffer buffer, int fence) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-
-    mBufferQueue.push_back(QueuedAHB{
-        .ahb = ahb,
-        .fence = fence,
-    });
-
-    return 0;
-}
-
-int TestingANativeWindow::dequeueBuffer(EGLClientBuffer* buffer, int* fence) {
-    auto queuedAhb = mBufferQueue.front();
-    mBufferQueue.pop_front();
-
-    *buffer = queuedAhb.ahb->asEglClientBuffer();
-    *fence = queuedAhb.fence;
-    return 0;
-}
-
-int TestingANativeWindow::cancelBuffer(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-
-    mBufferQueue.push_back(QueuedAHB{
-        .ahb = ahb,
-        .fence = -1,
-    });
-
-    return 0;
-}
-
-bool TestingVirtGpuANativeWindowHelper::isValid(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-    return true;
-}
-
-bool TestingVirtGpuANativeWindowHelper::isValid(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-    return true;
-}
-
-void TestingVirtGpuANativeWindowHelper::acquire(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::release(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::acquire(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::release(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-}
-
-int TestingVirtGpuANativeWindowHelper::getConsumerUsage(EGLNativeWindowType window, int* usage) {
-    (void)window;
-    (void)usage;
-    return 0;
-}
-void TestingVirtGpuANativeWindowHelper::setUsage(EGLNativeWindowType window, int usage) {
-    (void)window;
-    (void)usage;
-}
-
-int TestingVirtGpuANativeWindowHelper::getWidth(EGLNativeWindowType window) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->getWidth();
-}
-
-int TestingVirtGpuANativeWindowHelper::getHeight(EGLNativeWindowType window) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->getHeight();
-}
-
-int TestingVirtGpuANativeWindowHelper::getWidth(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getWidth();
-}
-
-int TestingVirtGpuANativeWindowHelper::getHeight(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getHeight();
-}
-
-int TestingVirtGpuANativeWindowHelper::getFormat(EGLClientBuffer buffer, Gralloc* helper) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getAndroidFormat();
-}
-
-void TestingVirtGpuANativeWindowHelper::setSwapInterval(EGLNativeWindowType window, int interval) {
-    ALOGE("Unimplemented");
-}
-
-int TestingVirtGpuANativeWindowHelper::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->queueBuffer(buffer, fence);
-}
-
-int TestingVirtGpuANativeWindowHelper::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->dequeueBuffer(buffer, fence);
-}
-
-int TestingVirtGpuANativeWindowHelper::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->cancelBuffer(buffer);
-}
-
-int TestingVirtGpuANativeWindowHelper::getHostHandle(EGLClientBuffer buffer, Gralloc*) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getResourceId();
-}
-
-}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/TestingAndroidWsi.h b/guest/OpenglSystemCommon/TestingAndroidWsi.h
deleted file mode 100644
index b18455e..0000000
--- a/guest/OpenglSystemCommon/TestingAndroidWsi.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include <optional>
-#include <queue>
-#include <unordered_map>
-#include <vector>
-
-#include "VirtGpu.h"
-#include "ANativeWindow.h"
-
-namespace gfxstream {
-
-class TestingAHardwareBuffer {
-  public:
-    TestingAHardwareBuffer(uint32_t width,
-                           uint32_t height,
-                           VirtGpuBlobPtr resource);
-
-    ~TestingAHardwareBuffer();
-
-    uint32_t getResourceId() const;
-
-    uint32_t getWidth() const;
-
-    uint32_t getHeight() const;
-
-    int getAndroidFormat() const;
-
-    uint32_t getDrmFormat() const;
-
-    AHardwareBuffer* asAHardwareBuffer();
-
-    buffer_handle_t asBufferHandle();
-
-    EGLClientBuffer asEglClientBuffer();
-
-  private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    VirtGpuBlobPtr mResource;
-    native_handle_t *mHandle = nullptr;
-};
-
-class TestingVirtGpuGralloc : public Gralloc {
-   public:
-    TestingVirtGpuGralloc();
-
-    uint32_t createColorBuffer(void*,
-                               int width,
-                               int height,
-                               uint32_t glFormat) override;
-
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
-                 AHardwareBuffer** outputAhb) override;
-
-    std::unique_ptr<TestingAHardwareBuffer> allocate(uint32_t width,
-                                                     uint32_t height,
-                                                     uint32_t format);
-
-    void acquire(AHardwareBuffer* ahb) override;
-    void release(AHardwareBuffer* ahb) override;
-
-    uint32_t getHostHandle(const native_handle_t* handle) override;
-    uint32_t getHostHandle(const AHardwareBuffer* handle) override;
-
-    int getFormat(const native_handle_t* handle) override;
-    int getFormat(const AHardwareBuffer* handle) override;
-
-    uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
-
-    size_t getAllocatedSize(const native_handle_t*) override;
-    size_t getAllocatedSize(const AHardwareBuffer*) override;
-
-  private:
-    std::unordered_map<uint32_t, std::unique_ptr<TestingAHardwareBuffer>> mAllocatedColorBuffers;
-};
-
-class TestingANativeWindow {
-  public:
-    TestingANativeWindow(uint32_t width,
-                         uint32_t height,
-                         uint32_t format,
-                         std::vector<std::unique_ptr<TestingAHardwareBuffer>> buffers);
-
-    EGLNativeWindowType asEglNativeWindowType();
-
-    uint32_t getWidth() const;
-
-    uint32_t getHeight() const;
-
-    int getFormat() const;
-
-    int queueBuffer(EGLClientBuffer buffer, int fence);
-    int dequeueBuffer(EGLClientBuffer* buffer, int* fence);
-    int cancelBuffer(EGLClientBuffer buffer);
-
-  private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint32_t mFormat;
-    std::vector<std::unique_ptr<TestingAHardwareBuffer>> mBuffers;
-
-    struct QueuedAHB {
-        TestingAHardwareBuffer* ahb;
-        int fence = -1;
-    };
-    std::deque<QueuedAHB> mBufferQueue;
-};
-
-class TestingVirtGpuANativeWindowHelper : public ANativeWindowHelper {
-  public:
-    bool isValid(EGLNativeWindowType window) override;
-    bool isValid(EGLClientBuffer buffer) override;
-
-    void acquire(EGLNativeWindowType window) override;
-    void release(EGLNativeWindowType window) override;
-
-    void acquire(EGLClientBuffer buffer) override;
-    void release(EGLClientBuffer buffer) override;
-
-    int getConsumerUsage(EGLNativeWindowType window, int* usage) override;
-    void setUsage(EGLNativeWindowType window, int usage) override;
-
-    int getWidth(EGLNativeWindowType window) override;
-    int getHeight(EGLNativeWindowType window) override;
-
-    int getWidth(EGLClientBuffer buffer) override;
-    int getHeight(EGLClientBuffer buffer) override;
-
-    int getFormat(EGLClientBuffer buffer, Gralloc* helper) override;
-
-    void setSwapInterval(EGLNativeWindowType window, int interval) override;
-
-    int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) override;
-    int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) override;
-    int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) override;
-
-    int getHostHandle(EGLClientBuffer buffer, Gralloc*) override;
-};
-
-}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/VirtioGpuPipeStream.cpp b/guest/OpenglSystemCommon/VirtioGpuPipeStream.cpp
index 643b6c4..6d7add5 100644
--- a/guest/OpenglSystemCommon/VirtioGpuPipeStream.cpp
+++ b/guest/OpenglSystemCommon/VirtioGpuPipeStream.cpp
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "virtgpu_drm.h"
 #include "VirtGpu.h"
 
 static const size_t kTransferBufferSize = (1048576);
diff --git a/guest/android-emu/aemu/base/AndroidHealthMonitor.cpp b/guest/android-emu/aemu/base/AndroidHealthMonitor.cpp
index 54e995d..5549655 100644
--- a/guest/android-emu/aemu/base/AndroidHealthMonitor.cpp
+++ b/guest/android-emu/aemu/base/AndroidHealthMonitor.cpp
@@ -271,7 +271,7 @@
 #else
     (void)consumer;
     (void)heartbeatInterval;
-    ALOGI("HealthMonitor disabled. Returning nullptr");
+    ALOGV("HealthMonitor disabled. Returning nullptr");
     return nullptr;
 #endif
 }
diff --git a/guest/android-emu/aemu/base/fit/Nullable.h b/guest/android-emu/aemu/base/fit/Nullable.h
index 02b3337..06e4527 100644
--- a/guest/android-emu/aemu/base/fit/Nullable.h
+++ b/guest/android-emu/aemu/base/fit/Nullable.h
@@ -72,7 +72,7 @@
 // - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; })
 // - sizeof(std::optional<int>) == sizeof(struct { bool; int; })
 //
-// TODO(fxbug.dev/4681): fit::nullable does not precisely mirror
+// TODO(fxbug.dev/42123486): fit::nullable does not precisely mirror
 // std::optional. This should be corrected to avoid surprises when switching
 // between the types.
 template <typename T,
diff --git a/guest/OpenglSystemCommon/ANativeWindowAndroid.cpp b/guest/android/ANativeWindowAndroid.cpp
similarity index 95%
rename from guest/OpenglSystemCommon/ANativeWindowAndroid.cpp
rename to guest/android/ANativeWindowAndroid.cpp
index 1edae70..6ab4fa6 100644
--- a/guest/OpenglSystemCommon/ANativeWindowAndroid.cpp
+++ b/guest/android/ANativeWindowAndroid.cpp
@@ -68,7 +68,6 @@
 #endif  // defined(__ANDROID__)
 }
 
-
 void ANativeWindowHelperAndroid::acquire(EGLClientBuffer buffer) {
 #if defined(__ANDROID__)
     auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
@@ -169,7 +168,8 @@
 #endif  // defined(__ANDROID__)
 }
 
-int ANativeWindowHelperAndroid::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) {
+int ANativeWindowHelperAndroid::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
+                                            int fence) {
 #if defined(__ANDROID__)
     auto* anw = reinterpret_cast<ANativeWindow*>(window);
     auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
@@ -182,7 +182,8 @@
 #endif  // defined(__ANDROID__)
 }
 
-int ANativeWindowHelperAndroid::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) {
+int ANativeWindowHelperAndroid::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
+                                              int* fence) {
 #if defined(__ANDROID__)
     auto* anw = reinterpret_cast<ANativeWindow*>(window);
     auto* anb = reinterpret_cast<ANativeWindowBuffer**>(buffer);
@@ -218,4 +219,8 @@
 #endif  // defined(__ANDROID__)
 }
 
+ANativeWindowHelper* createPlatformANativeWindowHelper() {
+    return new ANativeWindowHelperAndroid();
+}
+
 }  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/ANativeWindowAndroid.h b/guest/android/ANativeWindowAndroid.h
similarity index 96%
rename from guest/OpenglSystemCommon/ANativeWindowAndroid.h
rename to guest/android/ANativeWindowAndroid.h
index 97eba6a..bac57c3 100644
--- a/guest/OpenglSystemCommon/ANativeWindowAndroid.h
+++ b/guest/android/ANativeWindowAndroid.h
@@ -17,12 +17,12 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include "ANativeWindow.h"
+#include "gfxstream/guest/ANativeWindow.h"
 
 namespace gfxstream {
 
 class ANativeWindowHelperAndroid : public ANativeWindowHelper {
-  public:
+   public:
     ANativeWindowHelperAndroid() = default;
 
     bool isValid(EGLNativeWindowType window);
diff --git a/guest/android/ANativeWindowEmulated.cpp b/guest/android/ANativeWindowEmulated.cpp
new file mode 100644
index 0000000..d3e83c3
--- /dev/null
+++ b/guest/android/ANativeWindowEmulated.cpp
@@ -0,0 +1,199 @@
+// Copyright 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 "ANativeWindowEmulated.h"
+
+#include <log/log.h>
+
+#include "drm_fourcc.h"
+
+namespace gfxstream {
+
+EmulatedANativeWindow::EmulatedANativeWindow(
+    uint32_t width, uint32_t height, uint32_t format,
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers)
+    : mRefCount(1), mWidth(width), mHeight(height), mFormat(format), mBuffers(std::move(buffers)) {
+    for (auto& buffer : mBuffers) {
+        mBufferQueue.push_back(QueuedAHB{
+            .ahb = buffer.get(),
+            .fence = -1,
+        });
+    }
+}
+
+EGLNativeWindowType EmulatedANativeWindow::asEglNativeWindowType() {
+    return reinterpret_cast<EGLNativeWindowType>(this);
+}
+
+uint32_t EmulatedANativeWindow::getWidth() const { return mWidth; }
+
+uint32_t EmulatedANativeWindow::getHeight() const { return mHeight; }
+
+int EmulatedANativeWindow::getFormat() const { return mFormat; }
+
+int EmulatedANativeWindow::queueBuffer(EGLClientBuffer buffer, int fence) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+
+    mBufferQueue.push_back(QueuedAHB{
+        .ahb = ahb,
+        .fence = fence,
+    });
+
+    return 0;
+}
+
+int EmulatedANativeWindow::dequeueBuffer(EGLClientBuffer* buffer, int* fence) {
+    auto queuedAhb = mBufferQueue.front();
+    mBufferQueue.pop_front();
+
+    *buffer = queuedAhb.ahb->asEglClientBuffer();
+    *fence = queuedAhb.fence;
+    return 0;
+}
+
+int EmulatedANativeWindow::cancelBuffer(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+
+    mBufferQueue.push_back(QueuedAHB{
+        .ahb = ahb,
+        .fence = -1,
+    });
+
+    return 0;
+}
+
+void EmulatedANativeWindow::acquire() { ++mRefCount; }
+
+void EmulatedANativeWindow::release() {
+    --mRefCount;
+    if (mRefCount == 0) {
+        delete this;
+    }
+}
+
+bool EmulatedANativeWindowHelper::isValid(EGLNativeWindowType window) {
+    // TODO: maybe a registry of valid EmulatedANativeWindow-s?
+    (void)window;
+    return true;
+}
+
+bool EmulatedANativeWindowHelper::isValid(EGLClientBuffer buffer) {
+    // TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
+    (void)buffer;
+    return true;
+}
+
+void EmulatedANativeWindowHelper::acquire(EGLNativeWindowType window) {
+    auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    anw->acquire();
+}
+
+void EmulatedANativeWindowHelper::release(EGLNativeWindowType window) {
+    auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    anw->release();
+}
+
+void EmulatedANativeWindowHelper::acquire(EGLClientBuffer buffer) {
+    // TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
+    (void)buffer;
+}
+
+void EmulatedANativeWindowHelper::release(EGLClientBuffer buffer) { (void)buffer; }
+
+int EmulatedANativeWindowHelper::getConsumerUsage(EGLNativeWindowType window, int* usage) {
+    (void)window;
+    (void)usage;
+    return 0;
+}
+void EmulatedANativeWindowHelper::setUsage(EGLNativeWindowType window, int usage) {
+    (void)window;
+    (void)usage;
+}
+
+int EmulatedANativeWindowHelper::getWidth(EGLNativeWindowType window) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->getWidth();
+}
+
+int EmulatedANativeWindowHelper::getHeight(EGLNativeWindowType window) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->getHeight();
+}
+
+int EmulatedANativeWindowHelper::getWidth(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getWidth();
+}
+
+int EmulatedANativeWindowHelper::getHeight(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getHeight();
+}
+
+int EmulatedANativeWindowHelper::getFormat(EGLClientBuffer buffer, Gralloc* helper) {
+    (void)helper;
+
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getAndroidFormat();
+}
+
+void EmulatedANativeWindowHelper::setSwapInterval(EGLNativeWindowType window, int interval) {
+    ALOGE("Unimplemented");
+    (void)window;
+    (void)interval;
+}
+
+int EmulatedANativeWindowHelper::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
+                                             int fence) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->queueBuffer(buffer, fence);
+}
+
+int EmulatedANativeWindowHelper::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
+                                               int* fence) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->dequeueBuffer(buffer, fence);
+}
+
+int EmulatedANativeWindowHelper::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->cancelBuffer(buffer);
+}
+
+int EmulatedANativeWindowHelper::getHostHandle(EGLClientBuffer buffer, Gralloc*) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getResourceId();
+}
+
+EGLNativeWindowType EmulatedANativeWindowHelper::createNativeWindowForTesting(Gralloc* gralloc,
+                                                                              uint32_t width,
+                                                                              uint32_t height) {
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers;
+    for (int i = 0; i < 3; i++) {
+        AHardwareBuffer* ahb = nullptr;
+        if (gralloc->allocate(width, height, DRM_FORMAT_ABGR8888, -1, &ahb) != 0) {
+            ALOGE("Failed to allocate gralloc buffer.");
+            return nullptr;
+        }
+        buffers.emplace_back(reinterpret_cast<EmulatedAHardwareBuffer*>(ahb));
+    }
+    return reinterpret_cast<EGLNativeWindowType>(
+        new EmulatedANativeWindow(width, height, DRM_FORMAT_ABGR8888, std::move(buffers)));
+}
+
+ANativeWindowHelper* createPlatformANativeWindowHelper() {
+    return new EmulatedANativeWindowHelper();
+}
+
+}  // namespace gfxstream
diff --git a/guest/android/ANativeWindowEmulated.h b/guest/android/ANativeWindowEmulated.h
new file mode 100644
index 0000000..8794e51
--- /dev/null
+++ b/guest/android/ANativeWindowEmulated.h
@@ -0,0 +1,95 @@
+// Copyright 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 <cstdint>
+#include <memory>
+#include <optional>
+#include <queue>
+#include <unordered_map>
+#include <vector>
+
+#include "gfxstream/guest/ANativeWindow.h"
+#include "GrallocEmulated.h"
+
+namespace gfxstream {
+
+class EmulatedANativeWindow {
+   public:
+    EmulatedANativeWindow(uint32_t width, uint32_t height, uint32_t format,
+                          std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers);
+
+    EGLNativeWindowType asEglNativeWindowType();
+
+    uint32_t getWidth() const;
+
+    uint32_t getHeight() const;
+
+    int getFormat() const;
+
+    int queueBuffer(EGLClientBuffer buffer, int fence);
+    int dequeueBuffer(EGLClientBuffer* buffer, int* fence);
+    int cancelBuffer(EGLClientBuffer buffer);
+
+    void acquire();
+    void release();
+
+   private:
+    uint32_t mRefCount;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mFormat;
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mBuffers;
+
+    struct QueuedAHB {
+        EmulatedAHardwareBuffer* ahb;
+        int fence = -1;
+    };
+    std::deque<QueuedAHB> mBufferQueue;
+};
+
+class EmulatedANativeWindowHelper : public ANativeWindowHelper {
+   public:
+    bool isValid(EGLNativeWindowType window) override;
+    bool isValid(EGLClientBuffer buffer) override;
+
+    void acquire(EGLNativeWindowType window) override;
+    void release(EGLNativeWindowType window) override;
+
+    void acquire(EGLClientBuffer buffer) override;
+    void release(EGLClientBuffer buffer) override;
+
+    int getConsumerUsage(EGLNativeWindowType window, int* usage) override;
+    void setUsage(EGLNativeWindowType window, int usage) override;
+
+    int getWidth(EGLNativeWindowType window) override;
+    int getHeight(EGLNativeWindowType window) override;
+
+    int getWidth(EGLClientBuffer buffer) override;
+    int getHeight(EGLClientBuffer buffer) override;
+
+    int getFormat(EGLClientBuffer buffer, Gralloc* helper) override;
+
+    void setSwapInterval(EGLNativeWindowType window, int interval) override;
+
+    int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) override;
+    int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) override;
+    int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) override;
+
+    int getHostHandle(EGLClientBuffer buffer, Gralloc*) override;
+
+    EGLNativeWindowType createNativeWindowForTesting(Gralloc* gralloc, uint32_t width,
+                                                     uint32_t height) override;
+};
+
+}  // namespace gfxstream
diff --git a/guest/android/ANativeWindowStub.cpp b/guest/android/ANativeWindowStub.cpp
new file mode 100644
index 0000000..4d613b9
--- /dev/null
+++ b/guest/android/ANativeWindowStub.cpp
@@ -0,0 +1,21 @@
+// Copyright 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gfxstream/guest/ANativeWindow.h"
+
+namespace gfxstream {
+
+ANativeWindowHelper* createPlatformANativeWindowHelper() { return nullptr; }
+
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/android/Android.bp b/guest/android/Android.bp
new file mode 100644
index 0000000..4846597
--- /dev/null
+++ b/guest/android/Android.bp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["hardware_google_gfxstream_license"],
+}
+
+cc_defaults {
+    name: "libgfxstream_guest_android_defaults",
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    host_supported: true,
+    vendor: true,
+    header_libs: [
+        "gfxstream_opengl_headers",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libdrm",
+        "liblog",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libdrm",
+    ],
+    export_include_dirs: [
+        "include"
+    ],
+    local_include_dirs: [
+        ".",
+        "include",
+    ],
+}
+
+cc_library_static {
+    name: "libgfxstream_guest_android",
+    defaults: [
+        "libgfxstream_guest_android_defaults",
+    ],
+    static_libs: [
+        "libplatform",
+    ],
+    target: {
+        android: {
+            header_libs: [
+                "libnativebase_headers",
+                "libgralloc_cb.ranchu",
+                "minigbm_headers",
+            ],
+            shared_libs: [
+                "lib_renderControl_enc",
+                "libnativewindow",
+            ],
+            srcs: [
+                "ANativeWindowAndroid.cpp",
+                "Gralloc.cpp",
+                "GrallocGoldfish.cpp",
+                "GrallocMinigbm.cpp",
+            ],
+        },
+        host: {
+            srcs: [
+                "ANativeWindowStub.cpp",
+                "GrallocStub.cpp",
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "libgfxstream_guest_android_with_host",
+    defaults: [
+        "libgfxstream_guest_android_defaults",
+    ],
+    static_libs: [
+        "libplatform_rutabaga",
+    ],
+    target: {
+        host: {
+            compile_multilib: "64",
+        },
+        android: {
+            compile_multilib: "64",
+        },
+    },
+    srcs: [
+        "ANativeWindowEmulated.cpp",
+        "GrallocEmulated.cpp",
+    ],
+}
\ No newline at end of file
diff --git a/guest/android/Gralloc.cpp b/guest/android/Gralloc.cpp
new file mode 100644
index 0000000..500519d
--- /dev/null
+++ b/guest/android/Gralloc.cpp
@@ -0,0 +1,39 @@
+// Copyright 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 "gfxstream/guest/Gralloc.h"
+
+#if defined(__ANDROID__)
+
+#include <string>
+
+#include "GrallocGoldfish.h"
+#include "GrallocMinigbm.h"
+#include "android-base/properties.h"
+
+namespace gfxstream {
+
+Gralloc* createPlatformGralloc(int deviceFd) {
+    const std::string value = android::base::GetProperty("ro.hardware.gralloc", "");
+    if (value == "minigbm") {
+        auto gralloc = new MinigbmGralloc();
+        gralloc->setFd(deviceFd);
+        return gralloc;
+    }
+    return new GoldfishGralloc();
+}
+
+}  // namespace gfxstream
+
+#endif
\ No newline at end of file
diff --git a/guest/android/GrallocEmulated.cpp b/guest/android/GrallocEmulated.cpp
new file mode 100644
index 0000000..e6031b1
--- /dev/null
+++ b/guest/android/GrallocEmulated.cpp
@@ -0,0 +1,204 @@
+// Copyright 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 "GrallocEmulated.h"
+
+#include <cutils/log.h>
+
+#include <optional>
+
+#include "drm_fourcc.h"
+
+namespace gfxstream {
+namespace {
+
+static constexpr int numFds = 0;
+static constexpr int numInts = 1;
+
+std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
+    switch (glFormat) {
+        case kGlRGB:
+            return DRM_FORMAT_BGR888;
+        case kGlRGB565:
+            return DRM_FORMAT_BGR565;
+        case kGlRGBA:
+            return DRM_FORMAT_ABGR8888;
+    }
+    return std::nullopt;
+}
+
+std::optional<uint32_t> DrmToVirglFormat(uint32_t drmFormat) {
+    switch (drmFormat) {
+        case DRM_FORMAT_ABGR8888:
+            return VIRGL_FORMAT_B8G8R8A8_UNORM;
+        case DRM_FORMAT_BGR888:
+            return VIRGL_FORMAT_R8G8B8_UNORM;
+        case DRM_FORMAT_BGR565:
+            return VIRGL_FORMAT_B5G6R5_UNORM;
+    }
+    return std::nullopt;
+}
+
+}  // namespace
+
+EmulatedAHardwareBuffer::EmulatedAHardwareBuffer(uint32_t width, uint32_t height,
+                                                 VirtGpuBlobPtr resource)
+    : mRefCount(1), mWidth(width), mHeight(height), mResource(resource) {}
+
+EmulatedAHardwareBuffer::~EmulatedAHardwareBuffer() {}
+
+uint32_t EmulatedAHardwareBuffer::getResourceId() const { return mResource->getResourceHandle(); }
+
+uint32_t EmulatedAHardwareBuffer::getWidth() const { return mWidth; }
+
+uint32_t EmulatedAHardwareBuffer::getHeight() const { return mHeight; }
+
+int EmulatedAHardwareBuffer::getAndroidFormat() const {
+    return /*AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM=*/1;
+}
+
+uint32_t EmulatedAHardwareBuffer::getDrmFormat() const { return DRM_FORMAT_ABGR8888; }
+
+AHardwareBuffer* EmulatedAHardwareBuffer::asAHardwareBuffer() {
+    return reinterpret_cast<AHardwareBuffer*>(this);
+}
+
+buffer_handle_t EmulatedAHardwareBuffer::asBufferHandle() {
+    return reinterpret_cast<buffer_handle_t>(this);
+}
+
+EGLClientBuffer EmulatedAHardwareBuffer::asEglClientBuffer() {
+    return reinterpret_cast<EGLClientBuffer>(this);
+}
+
+void EmulatedAHardwareBuffer::acquire() { ++mRefCount; }
+
+void EmulatedAHardwareBuffer::release() {
+    --mRefCount;
+    if (mRefCount == 0) {
+        delete this;
+    }
+}
+
+EmulatedGralloc::EmulatedGralloc() {}
+
+uint32_t EmulatedGralloc::createColorBuffer(void*, int width, int height, uint32_t glFormat) {
+    auto drmFormat = GlFormatToDrmFormat(glFormat);
+    if (!drmFormat) {
+        ALOGE("Unhandled format");
+    }
+
+    auto ahb = allocate(width, height, *drmFormat);
+
+    EmulatedAHardwareBuffer* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+
+    mOwned.emplace_back(rahb);
+
+    return rahb->getResourceId();
+}
+
+int EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                              AHardwareBuffer** outputAhb) {
+    (void)usage;
+    *outputAhb = allocate(width, height, format);
+    return 0;
+}
+
+AHardwareBuffer* EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t format) {
+    ALOGE("Allocating AHB w:%u, h:%u, format %u", width, height, format);
+
+    auto device = VirtGpuDevice::getInstance();
+    if (!device) {
+        ALOGE("Failed to allocate: no virtio gpu device.");
+        return nullptr;
+    }
+
+    auto virglFormat = DrmToVirglFormat(format);
+    if (!virglFormat) {
+        ALOGE("Unhandled DRM format:%u", format);
+        return nullptr;
+    }
+
+    auto resource = device->createVirglBlob(width, height, *virglFormat);
+    if (!resource) {
+        ALOGE("Failed to allocate: failed to create virtio resource.");
+        return nullptr;
+    }
+
+    resource->wait();
+
+    return reinterpret_cast<AHardwareBuffer*>(
+        new EmulatedAHardwareBuffer(width, height, std::move(resource)));
+}
+
+void EmulatedGralloc::acquire(AHardwareBuffer* ahb) {
+    auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+    rahb->acquire();
+}
+
+void EmulatedGralloc::release(AHardwareBuffer* ahb) {
+    auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+    rahb->release();
+}
+
+uint32_t EmulatedGralloc::getHostHandle(const native_handle_t* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getResourceId();
+}
+
+uint32_t EmulatedGralloc::getHostHandle(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getResourceId();
+}
+
+const native_handle_t* EmulatedGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return reinterpret_cast<const native_handle_t*>(ahb);
+}
+
+int EmulatedGralloc::getFormat(const native_handle_t* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getAndroidFormat();
+}
+
+int EmulatedGralloc::getFormat(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getAndroidFormat();
+}
+
+uint32_t EmulatedGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getDrmFormat();
+}
+
+size_t EmulatedGralloc::getAllocatedSize(const native_handle_t*) {
+    ALOGE("Unimplemented.");
+    return 0;
+}
+
+size_t EmulatedGralloc::getAllocatedSize(const AHardwareBuffer*) {
+    ALOGE("Unimplemented.");
+    return 0;
+}
+
+int EmulatedGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+    const auto* rahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(ahb);
+    *id = rahb->getResourceId();
+    return 0;
+}
+
+Gralloc* createPlatformGralloc(int /*deviceFd*/) {
+    return new EmulatedGralloc();
+}
+
+}  // namespace gfxstream
diff --git a/guest/android/GrallocEmulated.h b/guest/android/GrallocEmulated.h
new file mode 100644
index 0000000..8a48fef
--- /dev/null
+++ b/guest/android/GrallocEmulated.h
@@ -0,0 +1,91 @@
+// Copyright 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 <cstdint>
+#include <memory>
+#include <vector>
+
+#include "gfxstream/guest/Gralloc.h"
+#include "VirtGpu.h"
+
+namespace gfxstream {
+
+using EGLClientBuffer = void*;
+
+class EmulatedAHardwareBuffer {
+   public:
+    EmulatedAHardwareBuffer(uint32_t width, uint32_t height, VirtGpuBlobPtr resource);
+
+    ~EmulatedAHardwareBuffer();
+
+    uint32_t getResourceId() const;
+
+    uint32_t getWidth() const;
+
+    uint32_t getHeight() const;
+
+    int getAndroidFormat() const;
+
+    uint32_t getDrmFormat() const;
+
+    AHardwareBuffer* asAHardwareBuffer();
+
+    buffer_handle_t asBufferHandle();
+
+    EGLClientBuffer asEglClientBuffer();
+
+    void acquire();
+    void release();
+
+   private:
+    uint32_t mRefCount;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    VirtGpuBlobPtr mResource;
+};
+
+class EmulatedGralloc : public Gralloc {
+   public:
+    EmulatedGralloc();
+
+    uint32_t createColorBuffer(void*, int width, int height, uint32_t glFormat) override;
+
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                 AHardwareBuffer** outputAhb) override;
+
+    AHardwareBuffer* allocate(uint32_t width, uint32_t height, uint32_t format);
+
+    void acquire(AHardwareBuffer* ahb) override;
+    void release(AHardwareBuffer* ahb) override;
+
+    uint32_t getHostHandle(const native_handle_t* handle) override;
+    uint32_t getHostHandle(const AHardwareBuffer* handle) override;
+
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
+    int getFormat(const native_handle_t* handle) override;
+    int getFormat(const AHardwareBuffer* handle) override;
+
+    uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
+
+    size_t getAllocatedSize(const native_handle_t*) override;
+    size_t getAllocatedSize(const AHardwareBuffer*) override;
+
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
+   private:
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mOwned;
+};
+
+}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocGoldfish.cpp b/guest/android/GrallocGoldfish.cpp
similarity index 75%
rename from guest/OpenglSystemCommon/GrallocGoldfish.cpp
rename to guest/android/GrallocGoldfish.cpp
index b32562a..f07abde 100644
--- a/guest/OpenglSystemCommon/GrallocGoldfish.cpp
+++ b/guest/android/GrallocGoldfish.cpp
@@ -21,18 +21,13 @@
 
 namespace gfxstream {
 
-uint32_t GoldfishGralloc::createColorBuffer(void* rcEnc, int width,
-                                            int height, uint32_t glformat) {
+uint32_t GoldfishGralloc::createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) {
     auto* rc = reinterpret_cast<renderControl_client_context_t*>(rcEnc);
     return rc->rcCreateColorBuffer(rc, width, height, glformat);
 }
 
-int GoldfishGralloc::allocate(uint32_t width,
-                              uint32_t height,
-                              uint32_t format,
-                              uint64_t usage,
-                              AHardwareBuffer** outputAhb)  {
-
+int GoldfishGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                              AHardwareBuffer** outputAhb) {
     struct AHardwareBuffer_Desc desc = {
         .width = width,
         .height = height,
@@ -44,13 +39,9 @@
     return AHardwareBuffer_allocate(&desc, outputAhb);
 }
 
-void GoldfishGralloc::acquire(AHardwareBuffer* ahb) {
-    AHardwareBuffer_acquire(ahb);
-}
+void GoldfishGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
 
-void GoldfishGralloc::release(AHardwareBuffer* ahb) {
-    AHardwareBuffer_release(ahb);
-}
+void GoldfishGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
 
 uint32_t GoldfishGralloc::getHostHandle(native_handle_t const* handle) {
     return cb_handle_t::from(handle)->hostHandle;
@@ -61,6 +52,10 @@
     return getHostHandle(handle);
 }
 
+const native_handle_t* GoldfishGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return AHardwareBuffer_getNativeHandle(ahb);
+}
+
 int GoldfishGralloc::getFormat(const native_handle_t* handle) {
     return cb_handle_t::from(handle)->format;
 }
@@ -79,6 +74,16 @@
     return getAllocatedSize(handle);
 }
 
+int GoldfishGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+#if ANDROID_API_LEVEL >= 31
+    return AHardwareBuffer_getId(ahb, id);
+#else
+    (void)ahb;
+    *id = 0;
+    return 0;
+#endif
+}
+
 bool GoldfishGralloc::treatBlobAsImage() { return true; }
 
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocGoldfish.h b/guest/android/GrallocGoldfish.h
similarity index 81%
rename from guest/OpenglSystemCommon/GrallocGoldfish.h
rename to guest/android/GrallocGoldfish.h
index 2d7b9fa..560550f 100644
--- a/guest/OpenglSystemCommon/GrallocGoldfish.h
+++ b/guest/android/GrallocGoldfish.h
@@ -14,19 +14,15 @@
 
 #pragma once
 
-#include "Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 
 namespace gfxstream {
 
 class GoldfishGralloc : public Gralloc {
    public:
-    uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                               uint32_t glformat) override;
+    uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) override;
 
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                  AHardwareBuffer** outputAhb) override;
 
     void acquire(AHardwareBuffer* ahb) override;
@@ -35,14 +31,17 @@
     uint32_t getHostHandle(native_handle_t const* handle) override;
     uint32_t getHostHandle(const AHardwareBuffer* handle) override;
 
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
     int getFormat(const native_handle_t* handle) override;
     int getFormat(const AHardwareBuffer* handle) override;
 
     size_t getAllocatedSize(const native_handle_t* handle) override;
     size_t getAllocatedSize(const AHardwareBuffer* handle) override;
 
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
     bool treatBlobAsImage() override;
 };
 
-
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocMinigbm.cpp b/guest/android/GrallocMinigbm.cpp
similarity index 90%
rename from guest/OpenglSystemCommon/GrallocMinigbm.cpp
rename to guest/android/GrallocMinigbm.cpp
index a4057c8..a3daf43 100644
--- a/guest/OpenglSystemCommon/GrallocMinigbm.cpp
+++ b/guest/android/GrallocMinigbm.cpp
@@ -14,16 +14,16 @@
 
 #include "GrallocMinigbm.h"
 
-#include <cinttypes>
-#include <cstring>
-#include <stdlib.h>
-
 #include <cros_gralloc/cros_gralloc_handle.h>
 #include <errno.h>
 #include <log/log.h>
+#include <stdlib.h>
 #include <sys/user.h>
-#include <xf86drm.h>
 #include <vndk/hardware_buffer.h>
+#include <xf86drm.h>
+
+#include <cinttypes>
+#include <cstring>
 
 #include "virtgpu_drm.h"
 
@@ -34,7 +34,6 @@
 static const size_t kPageSize = getpagesize();
 #endif
 
-
 namespace gfxstream {
 namespace {
 
@@ -61,8 +60,7 @@
         return false;
     }
     struct ManagedDrmGem {
-        ManagedDrmGem(int fd, uint32_t handle)
-            : m_fd(fd), m_prime_handle(handle) {}
+        ManagedDrmGem(int fd, uint32_t handle) : m_fd(fd), m_prime_handle(handle) {}
         ManagedDrmGem(const ManagedDrmGem&) = delete;
         ~ManagedDrmGem() {
             struct drm_gem_close gem_close {
@@ -114,8 +112,7 @@
 
 }  // namespace
 
-uint32_t MinigbmGralloc::createColorBuffer(void*, int width, int height,
-                                           uint32_t glformat) {
+uint32_t MinigbmGralloc::createColorBuffer(void*, int width, int height, uint32_t glformat) {
     // Only supported format for pbuffers in gfxstream should be RGBA8
     const uint32_t kVirglFormatRGBA = 67;  // VIRGL_FORMAT_R8G8B8A8_UNORM;
     uint32_t virtgpu_format = 0;
@@ -162,12 +159,8 @@
     return res_create.res_handle;
 }
 
-int MinigbmGralloc::allocate(uint32_t width,
-                             uint32_t height,
-                             uint32_t format,
-                             uint64_t usage,
+int MinigbmGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                              AHardwareBuffer** outputAhb) {
-
     struct AHardwareBuffer_Desc desc = {
         .width = width,
         .height = height,
@@ -179,13 +172,9 @@
     return AHardwareBuffer_allocate(&desc, outputAhb);
 }
 
-void MinigbmGralloc::acquire(AHardwareBuffer* ahb) {
-    AHardwareBuffer_acquire(ahb);
-}
+void MinigbmGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
 
-void MinigbmGralloc::release(AHardwareBuffer* ahb) {
-    AHardwareBuffer_release(ahb);
-}
+void MinigbmGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
 
 uint32_t MinigbmGralloc::getHostHandle(const native_handle_t* handle) {
     struct drm_virtgpu_resource_info info;
@@ -202,6 +191,10 @@
     return getHostHandle(handle);
 }
 
+const native_handle_t* MinigbmGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return AHardwareBuffer_getNativeHandle(ahb);
+}
+
 int MinigbmGralloc::getFormat(const native_handle_t* handle) {
     return ((cros_gralloc_handle*)handle)->droid_format;
 }
@@ -235,4 +228,14 @@
     return getAllocatedSize(handle);
 }
 
+int MinigbmGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+#if ANDROID_API_LEVEL >= 31
+    return AHardwareBuffer_getId(ahb, id);
+#else
+    (void)ahb;
+    *id = 0;
+    return 0;
+#endif
+}
+
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocMinigbm.h b/guest/android/GrallocMinigbm.h
similarity index 83%
rename from guest/OpenglSystemCommon/GrallocMinigbm.h
rename to guest/android/GrallocMinigbm.h
index d165a03..c02aaeb 100644
--- a/guest/OpenglSystemCommon/GrallocMinigbm.h
+++ b/guest/android/GrallocMinigbm.h
@@ -14,19 +14,15 @@
 
 #pragma once
 
-#include "Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 
 namespace gfxstream {
 
 class MinigbmGralloc : public Gralloc {
    public:
-    uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                               uint32_t glformat) override;
+    uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) override;
 
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                  AHardwareBuffer** outputAhb) override;
 
     void acquire(AHardwareBuffer* ahb) override;
@@ -35,6 +31,8 @@
     uint32_t getHostHandle(native_handle_t const* handle) override;
     uint32_t getHostHandle(const AHardwareBuffer* handle) override;
 
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
     int getFormat(const native_handle_t* handle) override;
     int getFormat(const AHardwareBuffer* handle) override;
 
@@ -44,6 +42,8 @@
     size_t getAllocatedSize(const native_handle_t* handle) override;
     size_t getAllocatedSize(const AHardwareBuffer* handle) override;
 
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
     void setFd(int fd) { m_fd = fd; }
 
    private:
diff --git a/guest/android/GrallocStub.cpp b/guest/android/GrallocStub.cpp
new file mode 100644
index 0000000..e92faff
--- /dev/null
+++ b/guest/android/GrallocStub.cpp
@@ -0,0 +1,21 @@
+// Copyright 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gfxstream/guest/Gralloc.h"
+
+namespace gfxstream {
+
+Gralloc* createPlatformGralloc(int) { return nullptr; }
+
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/ANativeWindow.h b/guest/android/include/gfxstream/guest/ANativeWindow.h
similarity index 79%
rename from guest/OpenglSystemCommon/ANativeWindow.h
rename to guest/android/include/gfxstream/guest/ANativeWindow.h
index a50d9c2..0c59d19 100644
--- a/guest/OpenglSystemCommon/ANativeWindow.h
+++ b/guest/android/include/gfxstream/guest/ANativeWindow.h
@@ -14,23 +14,27 @@
 
 #pragma once
 
+#if defined(ANDROID)
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
 #include "Gralloc.h"
 
+using EGLClientBuffer = void*;
+
 namespace gfxstream {
 
 // Abstraction around libnativewindow to support testing.
 class ANativeWindowHelper {
-  public:
+   public:
     virtual ~ANativeWindowHelper() {}
 
     virtual bool isValid(EGLNativeWindowType window) = 0;
     virtual bool isValid(EGLClientBuffer buffer) = 0;
 
     virtual void acquire(EGLNativeWindowType window) = 0;
-    virtual void release(EGLNativeWindowType window)= 0;
+    virtual void release(EGLNativeWindowType window) = 0;
 
     virtual void acquire(EGLClientBuffer buffer) = 0;
     virtual void release(EGLClientBuffer buffer) = 0;
@@ -51,6 +55,16 @@
     virtual int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) = 0;
     virtual int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) = 0;
     virtual int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) = 0;
+
+    virtual EGLNativeWindowType createNativeWindowForTesting(Gralloc* /*gralloc*/,
+                                                             uint32_t /*width*/,
+                                                             uint32_t /*height*/) {
+        return (EGLNativeWindowType)0;
+    }
 };
 
+ANativeWindowHelper* createPlatformANativeWindowHelper();
+
 }  // namespace gfxstream
+
+#endif  // defined(ANDROID)
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/Gralloc.h b/guest/android/include/gfxstream/guest/Gralloc.h
similarity index 81%
rename from guest/OpenglSystemCommon/Gralloc.h
rename to guest/android/include/gfxstream/guest/Gralloc.h
index 8d59f0b..5eb9139 100644
--- a/guest/OpenglSystemCommon/Gralloc.h
+++ b/guest/android/include/gfxstream/guest/Gralloc.h
@@ -14,10 +14,11 @@
 
 #pragma once
 
-#include <stddef.h>
-#include <stdint.h>
+#if defined(ANDROID)
 
 #include <cutils/native_handle.h>
+#include <stddef.h>
+#include <stdint.h>
 
 typedef struct AHardwareBuffer AHardwareBuffer;
 
@@ -32,18 +33,16 @@
    public:
     virtual ~Gralloc() {}
 
-    virtual uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                                       uint32_t glformat) = 0;
+    virtual uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) = 0;
 
     virtual void acquire(AHardwareBuffer* ahb) = 0;
     virtual void release(AHardwareBuffer* ahb) = 0;
 
-    virtual int allocate(uint32_t width,
-                         uint32_t height,
-                         uint32_t format,
-                         uint64_t usage,
+    virtual int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                          AHardwareBuffer** outputAhb) = 0;
 
+    virtual const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) = 0;
+
     virtual uint32_t getHostHandle(const native_handle_t* handle) = 0;
     virtual uint32_t getHostHandle(const AHardwareBuffer* handle) = 0;
 
@@ -62,7 +61,13 @@
     virtual size_t getAllocatedSize(const native_handle_t* handle) = 0;
     virtual size_t getAllocatedSize(const AHardwareBuffer* handle) = 0;
 
-    virtual bool treatBlobAsImage() { return false; };
+    virtual int getId(const AHardwareBuffer* ahb, uint64_t* id) = 0;
+
+    virtual bool treatBlobAsImage() { return false; }
 };
 
+Gralloc* createPlatformGralloc(int deviceFd = -1);
+
 }  // namespace gfxstream
+
+#endif  // defined(ANDROID)
\ No newline at end of file
diff --git a/guest/egl/Android.bp b/guest/egl/Android.bp
index 7a1819b..3b07122 100644
--- a/guest/egl/Android.bp
+++ b/guest/egl/Android.bp
@@ -89,8 +89,6 @@
     ],
     shared_libs: [
         "libOpenglSystemCommonWithHost",
-        "libplatform_rutabaga",
-        "libgfxstream_backend",
     ],
     target: {
         host: {
diff --git a/guest/fuchsia/include/cutils/log.h b/guest/fuchsia/include/cutils/log.h
index de0895c..2d011b6 100644
--- a/guest/fuchsia/include/cutils/log.h
+++ b/guest/fuchsia/include/cutils/log.h
@@ -5,6 +5,44 @@
 #define LOG_TAG nullptr
 #endif
 
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * Use __VA_ARGS__ if running a static analyzer,
+ * to avoid warnings of unused variables in __VA_ARGS__.
+ * Use constexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
+ */
+#ifdef __clang_analyzer__
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+  return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif /* __clang_analyzer__ */
+
 enum {
   ANDROID_LOG_UNKNOWN = 0,
   ANDROID_LOG_DEFAULT,
@@ -38,16 +76,41 @@
 #define LOG_ALWAYS_FATAL(...) \
   (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
 
-#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
+  } while (false)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+
 #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
 #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
 #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
 #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
 
+#if LOG_NDEBUG
+
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+
+#else
+
 #define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
 
 #define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
 
+#endif
+
 #define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
 
 extern "C" {
diff --git a/guest/fuchsia/port.cc b/guest/fuchsia/port.cc
index 7c0a338..ffe89da 100644
--- a/guest/fuchsia/port.cc
+++ b/guest/fuchsia/port.cc
@@ -33,8 +33,9 @@
     return;
 
   snprintf(buf, sizeof(buf), "[%s(%d)]", basename(dup), line);
-  fprintf(stderr, "%s\n", buf);
+  fprintf(stderr, "%s", buf);
   vfprintf(stderr, format, ap);
+  fprintf(stderr, "\n");
 
   free(dup);
 }
diff --git a/guest/magma/magma.cpp b/guest/magma/magma.cpp
index ca4c12e..6f256a6 100644
--- a/guest/magma/magma.cpp
+++ b/guest/magma/magma.cpp
@@ -154,7 +154,7 @@
     }
     auto& info = it->second;
 
-    // TODO(fxbug.dev/122604): Evaluate deferred guest resource creation.
+    // TODO(fxbug.dev/42073573): Evaluate deferred guest resource creation.
     auto blob = VirtGpuDevice::getInstance(VirtGpuCapset::kCapsetGfxStreamMagma)
                     ->createBlob({.size = info.size,
                                   .flags = kBlobFlagMappable | kBlobFlagShareable,
diff --git a/guest/magma/magma_enc/magma_enc.cpp b/guest/magma/magma_enc/magma_enc.cpp
index f181eee..7f865c6 100644
--- a/guest/magma/magma_enc/magma_enc.cpp
+++ b/guest/magma/magma_enc/magma_enc.cpp
@@ -12,7 +12,7 @@
 
 #include <stdio.h>
 
-// TODO(fxbug.dev/122605): Why is modification needed for these two includes?
+// TODO(fxbug.dev/42073574): Why is modification needed for these two includes?
 #include "aemu/base/Tracing.h"
 #include "cutils/log.h"
 
diff --git a/guest/mesa-gn/include/BUILD.gn b/guest/mesa-gn/include/BUILD.gn
new file mode 100644
index 0000000..fbc4dea
--- /dev/null
+++ b/guest/mesa-gn/include/BUILD.gn
@@ -0,0 +1,49 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import("../mesa.gni")
+
+config("include_config") {
+  include_dirs = [ "$mesa_source_root/include" ]
+}
+
+config("vulkan_public_config") {
+  defines = [ "VK_USE_PLATFORM_FUCHSIA=1" ]
+}
+
+mesa_source_set("vulkan") {
+  public_configs = [
+    ":include_config",
+    ":vulkan_public_config",
+  ]
+
+  # Don't bother listing all the Vulkan headers
+  sources = [ "vulkan/vulkan.h" ]
+}
+
+mesa_source_set("c_compat") {
+  public_configs = [ ":include_config" ]
+  sources = [
+    "c99_alloca.h",
+    "c99_compat.h",
+    "no_extern_c.h",
+  ]
+}
diff --git a/guest/mesa-gn/mesa.gni b/guest/mesa-gn/mesa.gni
new file mode 100644
index 0000000..0a83224
--- /dev/null
+++ b/guest/mesa-gn/mesa.gni
@@ -0,0 +1,39 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+mesa_build_root = "//third_party/gfxstream/src/guest/mesa-gn"
+mesa_source_root = "//third_party/gfxstream/src/guest/mesa"
+mesa_vk_xml = "$mesa_source_root/src/vulkan/registry/vk.xml"
+
+template("mesa_source_set") {
+  source_set(target_name) {
+    # Can't forward configs because doing so overwrites the default
+    # configs specified by the build system.
+    forward_variables_from(invoker, "*", [ "configs" ])
+    if (!defined(public_configs)) {
+      public_configs = []
+    }
+    public_configs += [ "$mesa_build_root/src:common_config" ]
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+  }
+}
diff --git a/guest/mesa-gn/src/BUILD.gn b/guest/mesa-gn/src/BUILD.gn
new file mode 100644
index 0000000..54a8987
--- /dev/null
+++ b/guest/mesa-gn/src/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import("../mesa.gni")
+
+config("common_config") {
+  # Version file not present
+  mesa_version = "unknown"
+
+  defines = [
+    "HAVE_ENDIAN_H=1",
+    "HAVE_PTHREAD=1",
+    "HAVE_STRUCT_TIMESPEC=1",
+    "HAVE_TIMESPEC_GET=1",
+    "HAVE_UNISTD_H=1",
+    "HAVE_DLFCN_H=1",
+    "HAVE___BUILTIN_BSWAP32=1",
+    "HAVE___BUILTIN_BSWAP64=1",
+    "HAVE___BUILTIN_CLZ=1",
+    "HAVE___BUILTIN_CLZLL=1",
+    "HAVE___BUILTIN_CTZ=1",
+    "HAVE___BUILTIN_EXPECT=1",
+    "HAVE___BUILTIN_FFS=1",
+    "HAVE___BUILTIN_FFSLL=1",
+    "HAVE___BUILTIN_POPCOUNT=1",
+    "HAVE___BUILTIN_POPCOUNTLL=1",
+    "HAVE___BUILTIN_UNREACHABLE=1",
+    "HAVE_FUNC_ATTRIBUTE_CONST=1",
+    "HAVE_FUNC_ATTRIBUTE_FLATTEN=1",
+    "HAVE_FUNC_ATTRIBUTE_FORMAT=1",
+    "HAVE_FUNC_ATTRIBUTE_MALLOC=1",
+    "HAVE_FUNC_ATTRIBUTE_PACKED=1",
+    "HAVE_FUNC_ATTRIBUTE_PURE=1",
+    "HAVE_FUNC_ATTRIBUTE_UNUSED=1",
+    "HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT=1",
+    "HAVE_FUNC_ATTRIBUTE_NORETURN=1",
+    "HAVE_DLADDR=1",
+    "HAVE_DL_ITERATE_PHDR=1",
+    "PACKAGE_VERSION=\"$mesa_version\" ",
+    "_DEFAULT_SOURCE",
+    "_GNU_SOURCE=1",
+  ]
+
+  if (is_debug) {
+    defines += [ "DEBUG=1" ]
+  }
+
+  cflags = [
+    "-Wno-sign-compare",
+    "-Wno-unused-function",
+    "-Wno-extra-semi",
+    "-Wno-newline-eof",
+    "-Wno-tautological-constant-out-of-range-compare",
+    "-Wno-unknown-pragmas",
+    "-Wno-int-in-bool-context",
+    "-Wno-conversion",
+    "-Wno-deprecated-anon-enum-enum-conversion",
+  ]
+}
diff --git a/guest/mesa-gn/src/util/BUILD.gn b/guest/mesa-gn/src/util/BUILD.gn
new file mode 100644
index 0000000..fb23e6c
--- /dev/null
+++ b/guest/mesa-gn/src/util/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import("../../mesa.gni")
+
+config("util_public_config") {
+  include_dirs = [
+    "$mesa_source_root/src",
+    "$mesa_source_root/src/util",
+  ]
+}
+
+# Add source files here as necessary
+mesa_source_set("util") {
+  public_configs = [ ":util_public_config" ]
+}
diff --git a/guest/mesa-gn/src/vulkan/runtime/BUILD.gn b/guest/mesa-gn/src/vulkan/runtime/BUILD.gn
new file mode 100644
index 0000000..765fc57
--- /dev/null
+++ b/guest/mesa-gn/src/vulkan/runtime/BUILD.gn
@@ -0,0 +1,389 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import("//build/python/python_action.gni")
+import("//build/python/python_binary.gni")
+import("../../../mesa.gni")
+
+runtime = "$mesa_source_root/src/vulkan/runtime"
+
+config("config") {
+  include_dirs = [
+    "$target_gen_dir",
+    "$runtime",
+  ]
+
+  defines = [ "VK_NO_NIR=1" ]
+}
+
+mesa_source_set("runtime") {
+  public_configs = [ ":config" ]
+
+  public_deps = [
+    "$mesa_build_root/include:c_compat",
+    "$mesa_build_root/include:vulkan",
+  ]
+
+  deps = [
+    ":vk_cmd_enqueue_entrypoints",
+    ":vk_cmd_queue",
+    ":vk_common_entrypoints",
+    ":vk_dispatch_trampolines",
+    ":vk_format_info",
+    ":vk_physical_device_features",
+    ":vk_physical_device_properties",
+    "$mesa_build_root/src/util",
+    "$mesa_build_root/src/vulkan/util",
+  ]
+
+  sources = [
+    "$runtime/vk_buffer.c",
+    "$runtime/vk_buffer.h",
+    "$runtime/vk_cmd_copy.c",
+    "$runtime/vk_cmd_enqueue.c",
+    "$runtime/vk_command_buffer.c",
+    "$runtime/vk_command_buffer.h",
+    "$runtime/vk_command_pool.c",
+    "$runtime/vk_command_pool.h",
+    "$runtime/vk_debug_report.c",
+    "$runtime/vk_debug_report.h",
+    "$runtime/vk_debug_utils.c",
+    "$runtime/vk_debug_utils.h",
+    "$runtime/vk_deferred_operation.c",
+    "$runtime/vk_deferred_operation.h",
+    "$runtime/vk_descriptor_set_layout.c",
+    "$runtime/vk_descriptor_set_layout.h",
+    "$runtime/vk_descriptor_update_template.c",
+    "$runtime/vk_descriptor_update_template.h",
+    "$runtime/vk_descriptors.c",
+    "$runtime/vk_descriptors.h",
+    "$runtime/vk_device.c",
+    "$runtime/vk_device.h",
+    "$runtime/vk_fence.c",
+    "$runtime/vk_fence.h",
+    "$runtime/vk_framebuffer.c",
+    "$runtime/vk_framebuffer.h",
+    "$runtime/vk_graphics_state.c",
+    "$runtime/vk_graphics_state.h",
+    "$runtime/vk_image.c",
+    "$runtime/vk_image.h",
+    "$runtime/vk_instance.c",
+    "$runtime/vk_instance.h",
+    "$runtime/vk_log.c",
+    "$runtime/vk_log.h",
+    "$runtime/vk_object.c",
+    "$runtime/vk_object.h",
+    "$runtime/vk_physical_device.c",
+    "$runtime/vk_physical_device.h",
+    "$runtime/vk_queue.c",
+    "$runtime/vk_queue.h",
+    "$runtime/vk_render_pass.c",
+    "$runtime/vk_sampler.c",
+    "$runtime/vk_sampler.h",
+    "$runtime/vk_semaphore.c",
+    "$runtime/vk_semaphore.h",
+    "$runtime/vk_standard_sample_locations.c",
+    "$runtime/vk_standard_sample_locations.h",
+    "$runtime/vk_sync.c",
+    "$runtime/vk_sync.h",
+    "$runtime/vk_sync_binary.c",
+    "$runtime/vk_sync_binary.h",
+    "$runtime/vk_sync_dummy.c",
+    "$runtime/vk_sync_dummy.h",
+    "$runtime/vk_sync_timeline.c",
+    "$runtime/vk_sync_timeline.h",
+    "$runtime/vk_synchronization2.c",
+    "$target_gen_dir/vk_cmd_enqueue_entrypoints.c",
+    "$target_gen_dir/vk_cmd_enqueue_entrypoints.h",
+    "$target_gen_dir/vk_cmd_queue.c",
+    "$target_gen_dir/vk_cmd_queue.h",
+    "$target_gen_dir/vk_common_entrypoints.c",
+    "$target_gen_dir/vk_common_entrypoints.h",
+    "$target_gen_dir/vk_dispatch_trampolines.c",
+    "$target_gen_dir/vk_dispatch_trampolines.h",
+    "$target_gen_dir/vk_physical_device_features.c",
+  ]
+
+  configs = [ "//build/config:Wno-strict-prototypes" ]
+}
+
+python_binary("vk_cmd_queue_gen_bin") {
+  main_source = "$mesa_source_root/src/vulkan/util/vk_cmd_queue_gen.py"
+
+  sources = [
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+  ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_cmd_queue") {
+  binary_label = ":vk_cmd_queue_gen_bin"
+
+  h_file = "$target_gen_dir/vk_cmd_queue.h"
+  c_file = "$target_gen_dir/vk_cmd_queue.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  inputs = [
+    "$mesa_source_root/src/vulkan/util/vk_dispatch_table_gen.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_entrypoints_gen_bin") {
+  main_source = "$mesa_source_root/src/vulkan/util/vk_entrypoints_gen.py"
+
+  sources = [
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+  ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_common_entrypoints") {
+  binary_label = ":vk_entrypoints_gen_bin"
+
+  h_file = "$target_gen_dir/vk_common_entrypoints.h"
+  c_file = "$target_gen_dir/vk_common_entrypoints.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  inputs = [
+    "$mesa_source_root/src/vulkan/util/vk_dispatch_table_gen.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--proto",
+    "--weak",
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--prefix",
+    "vk_common",
+    "--beta",
+    "false",
+  ]
+}
+
+python_action("vk_cmd_enqueue_entrypoints") {
+  binary_label = ":vk_entrypoints_gen_bin"
+
+  h_file = "$target_gen_dir/vk_cmd_enqueue_entrypoints.h"
+  c_file = "$target_gen_dir/vk_cmd_enqueue_entrypoints.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  inputs = [
+    "$mesa_source_root/src/vulkan/util/vk_dispatch_table_gen.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--proto",
+    "--weak",
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--prefix",
+    "vk_cmd_enqueue",
+    "--prefix",
+    "vk_cmd_enqueue_unless_primary",
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_dispatch_trampolines_gen_bin") {
+  main_source =
+      "$mesa_source_root/src/vulkan/util/vk_dispatch_trampolines_gen.py"
+
+  sources = [
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+  ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_dispatch_trampolines") {
+  binary_label = ":vk_dispatch_trampolines_gen_bin"
+
+  h_file = "$target_gen_dir/vk_dispatch_trampolines.h"
+  c_file = "$target_gen_dir/vk_dispatch_trampolines.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  inputs = [
+    "$mesa_source_root/src/vulkan/util/vk_dispatch_table_gen.py",
+    "$mesa_source_root/src/vulkan/util/vk_extensions.py",
+    "$mesa_source_root/src/vulkan/util/vk_entrypoints.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_physical_device_features_gen_bin") {
+  main_source =
+      "$mesa_source_root/src/vulkan/util/vk_physical_device_features_gen.py"
+
+  sources = [ "$mesa_source_root/src/vulkan/util/vk_extensions.py" ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_physical_device_features") {
+  binary_label = ":vk_physical_device_features_gen_bin"
+
+  c_file = "$target_gen_dir/vk_physical_device_features.c"
+  h_file = "$target_gen_dir/vk_physical_device_features.h"
+
+  outputs = [
+    c_file,
+    h_file,
+  ]
+
+  inputs = [ mesa_vk_xml ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_physical_device_properties_gen_bin") {
+  main_source =
+      "$mesa_source_root/src/vulkan/util/vk_physical_device_properties_gen.py"
+
+  sources = [ "$mesa_source_root/src/vulkan/util/vk_extensions.py" ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_physical_device_properties") {
+  binary_label = ":vk_physical_device_properties_gen_bin"
+
+  c_file = "$target_gen_dir/vk_physical_device_properties.c"
+  h_file = "$target_gen_dir/vk_physical_device_properties.h"
+
+  outputs = [
+    c_file,
+    h_file,
+  ]
+
+  inputs = [ mesa_vk_xml ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_format_info_gen_bin") {
+  main_source = "$mesa_source_root/src/vulkan/runtime/vk_format_info_gen.py"
+
+  sources = [ "$mesa_source_root/src/vulkan/util/vk_extensions.py" ]
+
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_format_info") {
+  binary_label = ":vk_format_info_gen_bin"
+
+  c_file = "$target_gen_dir/vk_format_info.c"
+  h_file = "$target_gen_dir/vk_format_info.h"
+
+  outputs = [
+    c_file,
+    h_file,
+  ]
+
+  inputs = [ mesa_vk_xml ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+  ]
+}
diff --git a/guest/mesa-gn/src/vulkan/util/BUILD.gn b/guest/mesa-gn/src/vulkan/util/BUILD.gn
new file mode 100644
index 0000000..80440c4
--- /dev/null
+++ b/guest/mesa-gn/src/vulkan/util/BUILD.gn
@@ -0,0 +1,201 @@
+# Copyright 2024 Google, LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import("//build/python/python_action.gni")
+import("//build/python/python_binary.gni")
+import("../../../mesa.gni")
+
+util = "$mesa_source_root/src/vulkan/util"
+
+config("config_util") {
+  include_dirs = [
+    target_gen_dir,
+    util,
+  ]
+
+  defines = [ "VK_NO_NIR=1" ]
+}
+
+mesa_source_set("util") {
+  public_configs = [ ":config_util" ]
+
+  public_deps = [
+    "$mesa_build_root/include:c_compat",
+    "$mesa_build_root/include:vulkan",
+    "$mesa_build_root/src/util",
+  ]
+
+  sources = [
+    "$target_gen_dir/vk_dispatch_table.c",
+    "$target_gen_dir/vk_dispatch_table.h",
+    "$target_gen_dir/vk_enum_to_str.c",
+    "$target_gen_dir/vk_extensions.c",
+    "$target_gen_dir/vk_extensions.h",
+    "$util/vk_alloc.c",
+    "$util/vk_alloc.h",
+    "$util/vk_enum_to_str.h",
+    "$util/vk_format.c",
+    "$util/vk_format.h",
+    "$util/vk_util.c",
+    "$util/vk_util.h",
+  ]
+
+  deps = [
+    ":gen_enum_to_str",
+    ":vk_dispatch_table",
+    ":vk_extensions",
+    ":vk_struct_type_cast_header",
+  ]
+}
+
+python_binary("gen_enum_to_str_gen_bin") {
+  main_source = "$util/gen_enum_to_str.py"
+
+  sources = [ "$util/vk_extensions.py" ]
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("gen_enum_to_str") {
+  binary_label = ":gen_enum_to_str_gen_bin"
+
+  outputs = [
+    "$target_gen_dir/vk_enum_to_str.h",
+    "$target_gen_dir/vk_enum_to_str.c",
+    "$target_gen_dir/vk_enum_defines.h",
+  ]
+
+  inputs = [ mesa_vk_xml ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml, root_build_dir),
+    "--outdir",
+    rebase_path(target_gen_dir, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_dispatch_table_gen_bin") {
+  main_source = "$util/vk_dispatch_table_gen.py"
+
+  sources = [
+    "$util/vk_entrypoints.py",
+    "$util/vk_extensions.py",
+  ]
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_dispatch_table") {
+  binary_label = ":vk_dispatch_table_gen_bin"
+
+  h_file = "$target_gen_dir/vk_dispatch_table.h"
+  c_file = "$target_gen_dir/vk_dispatch_table.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  script = "$util/vk_dispatch_table_gen.py"
+
+  inputs = [
+    "$util/vk_extensions.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml, root_build_dir),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
+
+python_binary("vk_extensions_gen_bin") {
+  main_source = "$util/vk_extensions_gen.py"
+
+  sources = [
+    "$util/vk_entrypoints.py",
+    "$util/vk_extensions.py",
+  ]
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_extensions") {
+  binary_label = ":vk_extensions_gen_bin"
+
+  h_file = "$target_gen_dir/vk_extensions.h"
+  c_file = "$target_gen_dir/vk_extensions.c"
+
+  outputs = [
+    h_file,
+    c_file,
+  ]
+
+  inputs = [
+    "$util/vk_extensions.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml, root_build_dir),
+    "--out-h",
+    rebase_path(h_file, root_build_dir),
+    "--out-c",
+    rebase_path(c_file, root_build_dir),
+  ]
+}
+
+python_binary("vk_struct_type_cast_gen_bin") {
+  main_source = "$util/vk_struct_type_cast_gen.py"
+
+  sources = [
+    "$util/vk_entrypoints.py",
+    "$util/vk_extensions.py",
+  ]
+  deps = [ "//third_party/mako" ]
+}
+
+python_action("vk_struct_type_cast_header") {
+  binary_label = ":vk_struct_type_cast_gen_bin"
+
+  outputs = [ "$target_gen_dir/vk_struct_type_cast_gen.h" ]
+
+  inputs = [
+    "$util/vk_extensions.py",
+    mesa_vk_xml,
+  ]
+
+  args = [
+    "--xml",
+    rebase_path(mesa_vk_xml, root_build_dir),
+    "--outdir",
+    rebase_path("$target_gen_dir", root_build_dir),
+    "--beta",
+    "false",
+  ]
+}
diff --git a/guest/mesa/src/vulkan/runtime/vk_android.c b/guest/mesa/src/vulkan/runtime/vk_android.c
index 677e278..39ea2a5 100644
--- a/guest/mesa/src/vulkan/runtime/vk_android.c
+++ b/guest/mesa/src/vulkan/runtime/vk_android.c
@@ -94,6 +94,8 @@
       return VK_FORMAT_D32_SFLOAT_S8_UINT;
    case AHARDWAREBUFFER_FORMAT_S8_UINT:
       return VK_FORMAT_S8_UINT;
+   case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+      return VK_FORMAT_R8_UNORM;
    default:
       return VK_FORMAT_UNDEFINED;
    }
@@ -131,6 +133,8 @@
       return AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT;
    case VK_FORMAT_S8_UINT:
       return AHARDWAREBUFFER_FORMAT_S8_UINT;
+   case VK_FORMAT_R8_UNORM:
+      return AHARDWAREBUFFER_FORMAT_R8_UNORM;
    default:
       return 0;
    }
diff --git a/guest/mesa/src/vulkan/runtime/vk_image.c b/guest/mesa/src/vulkan/runtime/vk_image.c
index 48e00dc..e83ef58 100644
--- a/guest/mesa/src/vulkan/runtime/vk_image.c
+++ b/guest/mesa/src/vulkan/runtime/vk_image.c
@@ -23,10 +23,6 @@
 
 #include "vk_image.h"
 
-#ifndef _WIN32
-#include <drm-uapi/drm_fourcc.h>
-#endif
-
 #include "vk_alloc.h"
 #include "vk_common_entrypoints.h"
 #include "vk_device.h"
diff --git a/guest/meson.build b/guest/meson.build
index 6bc5bd5..c35651f 100644
--- a/guest/meson.build
+++ b/guest/meson.build
@@ -34,6 +34,10 @@
 drm_dep = dependency('libdrm')
 thread_dep = dependency('threads')
 
+if with_guest_test
+  gfxstream_backend_dep = dependency('gfxstream_backend')
+endif
+
 #===============#
 # Includes      #
 #===============#
diff --git a/guest/platform/Android.bp b/guest/platform/Android.bp
index 9ecc24e..e356124 100644
--- a/guest/platform/Android.bp
+++ b/guest/platform/Android.bp
@@ -19,10 +19,19 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
+cc_library_headers {
+    name: "libplatform_headers",
+    host_supported: true,
+    vendor_available: true,
+    export_include_dirs: [
+        "include"
+    ],
+}
+
 cc_library_static {
     name: "libplatform",
     host_supported: true,
-    vendor_available: true,
+    vendor: true,
     srcs: [
         "VirtGpu.cpp",
         "linux/LinuxVirtGpuBlob.cpp",
@@ -56,17 +65,46 @@
             shared_libs: [
                 "libsync",
             ],
-        }
-    }
+        },
+    },
 }
 
 cc_library_shared {
+    name: "libplatform_rutabaga_server",
+    host_supported: true,
+    vendor_available: true,
+    srcs: [
+        "rutabaga/RutabagaLayer.cpp",
+    ],
+    header_libs: [
+        "libplatform_headers",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgfxstream_backend",
+        "liblog",
+        "libutils",
+    ],
+    export_include_dirs: [
+        // TODO: restrict to just RutabagaLayer.h
+        "rutabaga/include",
+    ],
+    target: {
+        host: {
+            compile_multilib: "64",
+        },
+        android: {
+            compile_multilib: "64",
+        },
+    },
+}
+
+cc_library_static {
     name: "libplatform_rutabaga",
     host_supported: true,
     vendor_available: true,
     srcs: [
         "VirtGpu.cpp",
-        "rutabaga/RutabagaLayer.cpp",
         "rutabaga/RutabagaVirtGpuBlob.cpp",
         "rutabaga/RutabagaVirtGpuBlobMapping.cpp",
         "rutabaga/RutabagaVirtGpuDevice.cpp",
@@ -75,7 +113,7 @@
     shared_libs: [
         "libcutils",
         "libdrm",
-        "libgfxstream_backend",
+        "libplatform_rutabaga_server",
         "liblog",
         "libutils",
     ],
diff --git a/guest/platform/linux/LinuxVirtGpuDevice.cpp b/guest/platform/linux/LinuxVirtGpuDevice.cpp
index 569e02e..0157115 100644
--- a/guest/platform/linux/LinuxVirtGpuDevice.cpp
+++ b/guest/platform/linux/LinuxVirtGpuDevice.cpp
@@ -78,7 +78,7 @@
 
         ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_GETPARAM, &get_param);
         if (ret) {
-            ALOGE("virtgpu backend not enabling %s", params[i].name);
+            ALOGV("virtgpu backend not enabling %s", params[i].name);
             continue;
         }
 
diff --git a/guest/platform/meson.build b/guest/platform/meson.build
index 67e5ac6..0561499 100644
--- a/guest/platform/meson.build
+++ b/guest/platform/meson.build
@@ -2,5 +2,9 @@
 # SPDX-License-Identifier: MIT
 
 inc_platform = include_directories('include')
-subdir('linux')
-subdir('stub')
+if with_guest_test
+  subdir('rutabaga')
+else
+  subdir('linux')
+  subdir('stub')
+endif
diff --git a/guest/platform/rutabaga/RutabagaLayer.cpp b/guest/platform/rutabaga/RutabagaLayer.cpp
index 42fb57d..fb225f9 100644
--- a/guest/platform/rutabaga/RutabagaLayer.cpp
+++ b/guest/platform/rutabaga/RutabagaLayer.cpp
@@ -322,6 +322,7 @@
                 [kParamCrossDevice] = 0,
                 [kParamContextInit] = 1,
                 [kParamSupportedCapsetIds] = 0,
+                [kParamExplicitDebugName] = 0,
                 [kParamCreateGuestHandle] = 0,
             },
     };
@@ -928,55 +929,62 @@
     }
 }
 
-namespace {
-
-EmulatedVirtioGpu* sInstance = nullptr;
-
-}  // namespace
-
 EmulatedVirtioGpu::EmulatedVirtioGpu()
     : mImpl{std::make_unique<EmulatedVirtioGpu::EmulatedVirtioGpuImpl>()} {}
 
+namespace {
+
+static std::mutex sInstanceMutex;
+static std::weak_ptr<EmulatedVirtioGpu> sInstance;
+
+}  // namespace
+
 /*static*/
-EmulatedVirtioGpu& EmulatedVirtioGpu::Get() {
-    if (sInstance == nullptr) {
-        sInstance = new EmulatedVirtioGpu();
+std::shared_ptr<EmulatedVirtioGpu> EmulatedVirtioGpu::Get() {
+    std::lock_guard<std::mutex> lock(sInstanceMutex);
 
-        bool withGl = false;
-        bool withVk = true;
-        bool withVkSnapshots = false;
+    std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
+    if (instance != nullptr) {
+        return instance;
+    }
 
-        struct Option {
-            std::string env;
-            bool* val;
-        };
-        const std::vector<Option> options = {
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots},
-        };
-        for (const Option option : options) {
-            const char* val = std::getenv(option.env.c_str());
-            if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
-                *option.val = true;
-            }
-        }
+    instance = std::shared_ptr<EmulatedVirtioGpu>(new EmulatedVirtioGpu());
 
-        ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk,
-              withVkSnapshots);
-        if (!sInstance->Init(withGl, withVk, withVkSnapshots)) {
-            ALOGE("Failed to initialize EmulatedVirtioGpu.");
+    bool withGl = false;
+    bool withVk = true;
+    bool withVkSnapshots = false;
+
+    struct Option {
+        std::string env;
+        bool* val;
+    };
+    const std::vector<Option> options = {
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots},
+    };
+    for (const Option option : options) {
+        const char* val = std::getenv(option.env.c_str());
+        if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
+            *option.val = true;
         }
     }
-    return *sInstance;
+
+    ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk, withVkSnapshots);
+    if (!instance->Init(withGl, withVk, withVkSnapshots)) {
+        ALOGE("Failed to initialize EmulatedVirtioGpu.");
+        return nullptr;
+    }
+    ALOGE("Successfully initialized EmulatedVirtioGpu.");
+    sInstance = instance;
+    return instance;
 }
 
 /*static*/
-void EmulatedVirtioGpu::Reset() {
-    if (sInstance != nullptr) {
-        delete sInstance;
-        sInstance = nullptr;
-    }
+uint32_t EmulatedVirtioGpu::GetNumActiveUsers() {
+    std::lock_guard<std::mutex> lock(sInstanceMutex);
+    std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
+    return instance.use_count();
 }
 
 bool EmulatedVirtioGpu::Init(bool withGl, bool withVk, bool withVkSnapshots) {
@@ -1030,6 +1038,6 @@
 
 void EmulatedVirtioGpu::SignalEmulatedFence(int fenceId) { mImpl->SignalEmulatedFence(fenceId); }
 
-void ResetEmulatedVirtioGpu() { EmulatedVirtioGpu::Reset(); }
+bool GetNumActiveEmulatedVirtioGpuUsers() { return EmulatedVirtioGpu::GetNumActiveUsers(); }
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaLayer.h b/guest/platform/rutabaga/RutabagaLayer.h
index 057b2f7..7c6443b 100644
--- a/guest/platform/rutabaga/RutabagaLayer.h
+++ b/guest/platform/rutabaga/RutabagaLayer.h
@@ -19,6 +19,8 @@
 #include <memory>
 #include <optional>
 
+// TODO: switch to virtgpu_drm.h to avoid circular dep between
+// libplatform_rutabaga and libplatform_rutabaga_server.
 #include "VirtGpu.h"
 
 namespace gfxstream {
@@ -28,47 +30,38 @@
 // host server via rutabaga.
 class EmulatedVirtioGpu {
   public:
-    static EmulatedVirtioGpu& Get();
-    static void Reset();
+   static std::shared_ptr<EmulatedVirtioGpu> Get();
+   static uint32_t GetNumActiveUsers();
 
-    bool Init(bool withGl, bool withVk, bool withVkSnapshots);
+   bool Init(bool withGl, bool withVk, bool withVkSnapshots);
 
-    VirtGpuCaps GetCaps(VirtGpuCapset capset);
+   VirtGpuCaps GetCaps(VirtGpuCapset capset);
 
-    std::optional<uint32_t> CreateContext();
-    void DestroyContext(uint32_t contextId);
+   std::optional<uint32_t> CreateContext();
+   void DestroyContext(uint32_t contextId);
 
-    std::optional<uint32_t> CreateBlob(uint32_t contextId,
-                                       const struct VirtGpuCreateBlob& params);
-    std::optional<uint32_t> CreateVirglBlob(uint32_t contextId,
-                                            uint32_t width,
-                                            uint32_t height,
-                                            uint32_t virglFormat);
+   std::optional<uint32_t> CreateBlob(uint32_t contextId, const struct VirtGpuCreateBlob& params);
+   std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, uint32_t width, uint32_t height,
+                                           uint32_t virglFormat);
 
-    void DestroyResource(uint32_t contextId,
-                         uint32_t resourceId);
+   void DestroyResource(uint32_t contextId, uint32_t resourceId);
 
-    uint8_t* Map(uint32_t resourceId);
-    void Unmap(uint32_t resourceId);
+   uint8_t* Map(uint32_t resourceId);
+   void Unmap(uint32_t resourceId);
 
-    int ExecBuffer(uint32_t contextId,
-                   struct VirtGpuExecBuffer& execbuffer,
-                   std::optional<uint32_t> blobResourceId);
+   int ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer,
+                  std::optional<uint32_t> blobResourceId);
 
-    int Wait(uint32_t resourceId);
+   int Wait(uint32_t resourceId);
 
-    int TransferFromHost(uint32_t contextId,
-                         uint32_t resourceId,
-                         uint32_t transferOffset,
-                         uint32_t transferSize);
-    int TransferToHost(uint32_t contextId,
-                       uint32_t resourceId,
-                       uint32_t transferOffset,
-                       uint32_t transferSize);
+   int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
+                        uint32_t transferSize);
+   int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
+                      uint32_t transferSize);
 
-    void SignalEmulatedFence(int fenceId);
+   void SignalEmulatedFence(int fenceId);
 
-    int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
+   int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
 
   private:
     EmulatedVirtioGpu();
diff --git a/guest/platform/rutabaga/RutabagaVirtGpu.h b/guest/platform/rutabaga/RutabagaVirtGpu.h
index 8cf07f5..64568ab 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpu.h
+++ b/guest/platform/rutabaga/RutabagaVirtGpu.h
@@ -28,14 +28,16 @@
 
 class RutabagaVirtGpuBlobMapping : public VirtGpuBlobMapping {
   public:
-    RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped);
-    ~RutabagaVirtGpuBlobMapping();
+   RutabagaVirtGpuBlobMapping(std::shared_ptr<EmulatedVirtioGpu> emulation, VirtGpuBlobPtr blob,
+                              uint8_t* mapped);
+   ~RutabagaVirtGpuBlobMapping();
 
-    uint8_t* asRawPtr(void) override;
+   uint8_t* asRawPtr(void) override;
 
   private:
-    VirtGpuBlobPtr mBlob;
-    uint8_t* mMapped = nullptr;
+   const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
+   const VirtGpuBlobPtr mBlob;
+   uint8_t* mMapped = nullptr;
 };
 
 class RutabagaVirtGpuResource : public std::enable_shared_from_this<RutabagaVirtGpuResource>, public VirtGpuBlob {
@@ -61,11 +63,10 @@
         kPipe,
     };
 
-    RutabagaVirtGpuResource(uint32_t resourceId,
-                            ResourceType resourceType,
-                            uint32_t contextId);
+    RutabagaVirtGpuResource(std::shared_ptr<EmulatedVirtioGpu> emulation, uint32_t resourceId,
+                            ResourceType resourceType, uint32_t contextId);
 
-
+    const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
     const uint32_t mContextId;
     const uint32_t mResourceId;
     const ResourceType mResourceType;
@@ -73,27 +74,29 @@
 
 class RutabagaVirtGpuDevice : public std::enable_shared_from_this<RutabagaVirtGpuDevice>, public VirtGpuDevice {
   public:
-    RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset);
-    ~RutabagaVirtGpuDevice();
+   RutabagaVirtGpuDevice(std::shared_ptr<EmulatedVirtioGpu> emulation, uint32_t contextId,
+                         VirtGpuCapset capset);
+   ~RutabagaVirtGpuDevice();
 
-    int64_t getDeviceHandle() override;
+   int64_t getDeviceHandle() override;
 
-    VirtGpuCaps getCaps() override;
+   VirtGpuCaps getCaps() override;
 
-    VirtGpuBlobPtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
+   VirtGpuBlobPtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
 
-    VirtGpuBlobPtr createVirglBlob(uint32_t width, uint32_t height, uint32_t virglFormat) override;
+   VirtGpuBlobPtr createVirglBlob(uint32_t width, uint32_t height, uint32_t virglFormat) override;
 
-    VirtGpuBlobPtr importBlob(const struct VirtGpuExternalHandle& handle) override;
+   VirtGpuBlobPtr importBlob(const struct VirtGpuExternalHandle& handle) override;
 
-    int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuBlob* blob) override;
+   int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuBlob* blob) override;
 
-   private:
-    const uint32_t mContextId;
-    const VirtGpuCapset mCapset;
+  private:
+   const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
+   const uint32_t mContextId;
+   const VirtGpuCapset mCapset;
 
-    friend class RutabagaVirtGpuResource;
-    uint32_t GetContextId() const { return mContextId; }
+   friend class RutabagaVirtGpuResource;
+   uint32_t GetContextId() const { return mContextId; }
 };
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp b/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
index 0b7db44..6f97ffb 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
@@ -21,17 +21,21 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuResource::RutabagaVirtGpuResource(uint32_t resourceId, ResourceType resourceType,
+RutabagaVirtGpuResource::RutabagaVirtGpuResource(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                                 uint32_t resourceId, ResourceType resourceType,
                                                  uint32_t contextId)
-    : mContextId(contextId), mResourceId(resourceId), mResourceType(resourceType) {}
+    : mEmulation(emulation),
+      mContextId(contextId),
+      mResourceId(resourceId),
+      mResourceType(resourceType) {}
 
 RutabagaVirtGpuResource::~RutabagaVirtGpuResource() {
-    EmulatedVirtioGpu::Get().DestroyResource(mContextId, mResourceId);
+    mEmulation->DestroyResource(mContextId, mResourceId);
 }
 
 VirtGpuBlobMappingPtr RutabagaVirtGpuResource::createMapping(void) {
-    uint8_t* mapped = EmulatedVirtioGpu::Get().Map(mResourceId);
-    return std::make_shared<RutabagaVirtGpuBlobMapping>(shared_from_this(), mapped);
+    uint8_t* mapped = mEmulation->Map(mResourceId);
+    return std::make_shared<RutabagaVirtGpuBlobMapping>(mEmulation, shared_from_this(), mapped);
 }
 
 uint32_t RutabagaVirtGpuResource::getResourceHandle() const { return mResourceId; }
@@ -56,7 +60,7 @@
     return -1;
 }
 
-int RutabagaVirtGpuResource::wait() { return EmulatedVirtioGpu::Get().Wait(mResourceId); }
+int RutabagaVirtGpuResource::wait() { return mEmulation->Wait(mResourceId); }
 
 int RutabagaVirtGpuResource::transferFromHost(uint32_t offset, uint32_t size) {
     if (mResourceType != ResourceType::kPipe) {
@@ -64,7 +68,7 @@
         return -1;
     }
 
-    return EmulatedVirtioGpu::Get().TransferFromHost(mContextId, mResourceId, offset, size);
+    return mEmulation->TransferFromHost(mContextId, mResourceId, offset, size);
 }
 
 int RutabagaVirtGpuResource::transferToHost(uint32_t offset, uint32_t size) {
@@ -73,7 +77,7 @@
         return -1;
     }
 
-    return EmulatedVirtioGpu::Get().TransferToHost(mContextId, mResourceId, offset, size);
+    return mEmulation->TransferToHost(mContextId, mResourceId, offset, size);
 }
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp b/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
index 4f82f7c..750ddee 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
@@ -19,11 +19,12 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuBlobMapping::RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped)
-    : mBlob(blob), mMapped(mapped) {}
+RutabagaVirtGpuBlobMapping::RutabagaVirtGpuBlobMapping(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                                       VirtGpuBlobPtr blob, uint8_t* mapped)
+    : mEmulation(emulation), mBlob(blob), mMapped(mapped) {}
 
 RutabagaVirtGpuBlobMapping::~RutabagaVirtGpuBlobMapping(void) {
-    EmulatedVirtioGpu::Get().Unmap(mBlob->getResourceHandle());
+    mEmulation->Unmap(mBlob->getResourceHandle());
 }
 
 uint8_t* RutabagaVirtGpuBlobMapping::asRawPtr(void) { return mMapped; }
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp b/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
index 999ac2f..615e81e 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
@@ -21,38 +21,35 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuDevice::RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset)
-    : VirtGpuDevice(capset), mContextId(contextId), mCapset(capset) {}
+RutabagaVirtGpuDevice::RutabagaVirtGpuDevice(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                             uint32_t contextId, VirtGpuCapset capset)
+    : VirtGpuDevice(capset), mEmulation(emulation), mContextId(contextId), mCapset(capset) {}
 
-RutabagaVirtGpuDevice::~RutabagaVirtGpuDevice() {
-    EmulatedVirtioGpu::Get().DestroyContext(mContextId);
-    EmulatedVirtioGpu::Reset();
-}
+RutabagaVirtGpuDevice::~RutabagaVirtGpuDevice() { mEmulation->DestroyContext(mContextId); }
 
 int64_t RutabagaVirtGpuDevice::getDeviceHandle() { return -1; }
 
-VirtGpuCaps RutabagaVirtGpuDevice::getCaps() { return EmulatedVirtioGpu::Get().GetCaps(mCapset); }
+VirtGpuCaps RutabagaVirtGpuDevice::getCaps() { return mEmulation->GetCaps(mCapset); }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
-    const auto resourceIdOpt = EmulatedVirtioGpu::Get().CreateBlob(mContextId, blobCreate);
+    const auto resourceIdOpt = mEmulation->CreateBlob(mContextId, blobCreate);
     if (!resourceIdOpt) {
         return nullptr;
     }
 
     return VirtGpuBlobPtr(new RutabagaVirtGpuResource(
-        *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kBlob, mContextId));
+        mEmulation, *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kBlob, mContextId));
 }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::createVirglBlob(uint32_t width, uint32_t height,
                                                       uint32_t virglFormat) {
-    const auto resourceIdOpt =
-        EmulatedVirtioGpu::Get().CreateVirglBlob(mContextId, width, height, virglFormat);
+    const auto resourceIdOpt = mEmulation->CreateVirglBlob(mContextId, width, height, virglFormat);
     if (!resourceIdOpt) {
         return nullptr;
     }
 
     return VirtGpuBlobPtr(new RutabagaVirtGpuResource(
-        *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kPipe, mContextId));
+        mEmulation, *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kPipe, mContextId));
 }
 
 int RutabagaVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
@@ -61,7 +58,7 @@
     if (blob) {
         blobResourceId = blob->getResourceHandle();
     }
-    return EmulatedVirtioGpu::Get().ExecBuffer(mContextId, execbuffer, blobResourceId);
+    return mEmulation->ExecBuffer(mContextId, execbuffer, blobResourceId);
 }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle&) {
@@ -72,10 +69,16 @@
 }  // namespace gfxstream
 
 VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset, int) {
-    const auto contextIdOp = gfxstream::EmulatedVirtioGpu::Get().CreateContext();
+    std::shared_ptr<gfxstream::EmulatedVirtioGpu> emulation = gfxstream::EmulatedVirtioGpu::Get();
+    if (!emulation) {
+        ALOGE("Failed to create RutabagaVirtGpuDevice: failed to get emulation layer.");
+        return nullptr;
+    }
+
+    const auto contextIdOp = emulation->CreateContext();
     if (!contextIdOp) {
         ALOGE("Failed to create RutabagaVirtGpuDevice: failed to create context.");
         return nullptr;
     }
-    return new gfxstream::RutabagaVirtGpuDevice(*contextIdOp, capset);
+    return new gfxstream::RutabagaVirtGpuDevice(emulation, *contextIdOp, capset);
 }
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
index 30b5f3d..2f97ea8 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
@@ -16,14 +16,21 @@
 
 #include "RutabagaVirtGpuSyncHelper.h"
 
+#include <log/log.h>
+
 #include "RutabagaLayer.h"
 
 namespace gfxstream {
 
 RutabagaVirtGpuSyncHelper::RutabagaVirtGpuSyncHelper() {}
 
+bool RutabagaVirtGpuSyncHelper::Init() {
+    mEmulation = EmulatedVirtioGpu::Get();
+    return mEmulation != nullptr;
+}
+
 int RutabagaVirtGpuSyncHelper::wait(int syncFd, int timeoutMilliseconds) {
-    return EmulatedVirtioGpu::Get().WaitOnEmulatedFence(syncFd, timeoutMilliseconds);
+    return mEmulation->WaitOnEmulatedFence(syncFd, timeoutMilliseconds);
 }
 
 int RutabagaVirtGpuSyncHelper::dup(int syncFd) {
@@ -33,6 +40,13 @@
 
 int RutabagaVirtGpuSyncHelper::close(int) { return 0; }
 
-SyncHelper* createPlatformSyncHelper() { return new RutabagaVirtGpuSyncHelper(); }
+SyncHelper* createPlatformSyncHelper() {
+    RutabagaVirtGpuSyncHelper* sync = new RutabagaVirtGpuSyncHelper();
+    if (!sync->Init()) {
+        ALOGE("Failed to initialize RutabagaVirtGpuSyncHelper: Failed to get emulation layer.");
+        return nullptr;
+    }
+    return sync;
+}
 
-}  // namespace gfxstream
\ No newline at end of file
+}  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
index c163eb3..6b50e71 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
+++ b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <memory>
+
+#include "RutabagaLayer.h"
 #include "Sync.h"
 
 namespace gfxstream {
@@ -22,11 +25,16 @@
   public:
     RutabagaVirtGpuSyncHelper();
 
+    bool Init();
+
     int wait(int syncFd, int timeoutMilliseconds) override;
 
     int dup(int syncFd) override;
 
     int close(int) override;
+
+   private:
+    std::shared_ptr<EmulatedVirtioGpu> mEmulation;
 };
 
 SyncHelper* createPlatformSyncHelper();
diff --git a/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h b/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
index 4817a9c..8f4536d 100644
--- a/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
+++ b/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
@@ -18,6 +18,6 @@
 
 namespace gfxstream {
 
-void ResetEmulatedVirtioGpu();
+uint32_t GetNumActiveEmulatedVirtioGpuUsers();
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/meson.build b/guest/platform/rutabaga/meson.build
new file mode 100644
index 0000000..a941197
--- /dev/null
+++ b/guest/platform/rutabaga/meson.build
@@ -0,0 +1,20 @@
+# Copyright 2022 Android Open Source Project
+# SPDX-License-Identifier: MIT
+
+files_lib_platform = files(
+  '../VirtGpu.cpp',
+  'RutabagaLayer.cpp',
+  'RutabagaVirtGpuBlob.cpp',
+  'RutabagaVirtGpuBlobMapping.cpp',
+  'RutabagaVirtGpuSyncHelper.cpp',
+  'RutabagaVirtGpuDevice.cpp',
+)
+
+lib_platform = static_library(
+   'platform',
+   files_lib_platform,
+   cpp_args: gfxstream_guest_args,
+   include_directories: [inc_platform, inc_android_compat],
+   link_with: [lib_android_compat],
+   dependencies: [drm_dep, gfxstream_backend_dep],
+)
diff --git a/guest/renderControl_enc/Android.bp b/guest/renderControl_enc/Android.bp
index 2ffb2b4..5f346a3 100644
--- a/guest/renderControl_enc/Android.bp
+++ b/guest/renderControl_enc/Android.bp
@@ -19,6 +19,18 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
+cc_library_headers {
+    name: "libgfxstream_guest_rendercontrol_encoder_headers",
+    host_supported: true,
+    vendor_available: true,
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+}
+
 cc_library_shared {
     name: "lib_renderControl_enc",
     host_supported: true,
@@ -32,6 +44,7 @@
     ],
     export_header_lib_headers: [
         "libgfxstream_guest_graphics_headers",
+        "libgfxstream_guest_iostream",
     ],
     shared_libs: [
         "libandroidemu",
diff --git a/guest/vulkan/Android.bp b/guest/vulkan/Android.bp
index 4db968f..03b4e96 100644
--- a/guest/vulkan/Android.bp
+++ b/guest/vulkan/Android.bp
@@ -47,7 +47,6 @@
         "libmesa_util_gfxstream",
         "libmesa_util_format_gfxstream",
         "libGoldfishAddressSpace",
-        "libgfxstream_guest_gralloc",
         "libringbuffer",
         "libqemupipe.ranchu",
         "libarect",
@@ -114,6 +113,7 @@
     ],
     static_libs: [
         "libplatform",
+        "libgfxstream_guest_android",
         "libgfxstream_guest_system_common",
     ],
 }
@@ -124,11 +124,12 @@
         "libgfxstream_guest_vulkan_defaults",
     ],
     shared_libs: [
-        "libplatform_rutabaga",
-        "libgfxstream_backend",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
+        "libgfxstream_guest_android_with_host",
         "libgfxstream_guest_system_common_with_host",
+        "libplatform_rutabaga",
     ],
     cflags: [
         "-DEND2END_TESTS",
diff --git a/guest/vulkan/gfxstream_vk_device.cpp b/guest/vulkan/gfxstream_vk_device.cpp
index 5d26a70..d095583 100644
--- a/guest/vulkan/gfxstream_vk_device.cpp
+++ b/guest/vulkan/gfxstream_vk_device.cpp
@@ -37,79 +37,9 @@
 
 namespace {
 
-static bool process_initialized = false;
-static uint32_t no_render_control_enc = 0;
 static bool instance_extension_table_initialized = false;
 static struct vk_instance_extension_table gfxstream_vk_instance_extensions_supported = {0};
 
-static VkResult SetupInstanceForThread() {
-    HostConnection* hostCon = HostConnection::getOrCreate(kCapsetGfxStreamVulkan);
-    if (!hostCon) {
-        ALOGE("vulkan: Failed to get host connection\n");
-        return VK_ERROR_DEVICE_LOST;
-    }
-
-    // Legacy goldfish path: could be deleted once goldfish not used guest-side.
-    if (!no_render_control_enc) {
-        // Implicitly sets up sequence number
-        ExtendedRCEncoderContext* rcEnc = hostCon->rcEncoder();
-        if (!rcEnc) {
-            ALOGE("vulkan: Failed to get renderControl encoder context\n");
-            return VK_ERROR_DEVICE_LOST;
-        }
-
-        // This is technically per-process, but it should not differ
-        // per-rcEncoder on a process.
-        gfxstream::vk::ResourceTracker::get()->setupFeatures(rcEnc->featureInfo_const());
-    }
-
-    gfxstream::vk::VkEncoder* vkEnc = hostCon->vkEncoder();
-    if (!vkEnc) {
-        ALOGE("vulkan: Failed to get Vulkan encoder\n");
-        return VK_ERROR_DEVICE_LOST;
-    }
-
-    return VK_SUCCESS;
-}
-
-static HostConnection* getConnection() {
-    if (!process_initialized) {
-        // The process must be initialized prior to this call.
-        ALOGE("Call to get a host connection before process initialization!");
-        return nullptr;
-    }
-    if (!HostConnection::isInit()) {
-        ALOGW("Call to getConnection when HostConnection is not initialized - treating as normal.");
-        if (SetupInstanceForThread() != VK_SUCCESS) {
-            ALOGE("Failed to initialize HostConnection! Aborting!");
-            return nullptr;
-        }
-    }
-    // This ::get call should already be initialized with the proper caps
-    // thanks to SetupInstanceForThread, but this should be made explicit.
-    auto hostCon = HostConnection::get();
-    return hostCon;
-}
-
-static gfxstream::vk::VkEncoder* getVkEncoder(HostConnection* con) { return con->vkEncoder(); }
-
-static VkResult SetupInstanceForProcess() {
-    process_initialized = true;
-    gfxstream::vk::ResourceTracker::get()->setupCaps(no_render_control_enc);
-
-    // To get the SeqnoPtr, we need the Process info, and for that we need the
-    // rcEncoder to be initialized for this thread.
-    auto thread_return = SetupInstanceForThread();
-
-    gfxstream::vk::ResourceTracker::get()->setSeqnoPtr(getSeqnoPtrForProcess());
-    gfxstream::vk::ResourceTracker::get()->setThreadingCallbacks({
-        .hostConnectionGetFunc = getConnection,
-        .vkEncoderGetFunc = getVkEncoder,
-    });
-
-    return thread_return;
-}
-
 // Provided by Mesa components only; never encoded/decoded through gfxstream
 static const char* const kMesaOnlyInstanceExtension[] = {
     VK_KHR_SURFACE_EXTENSION_NAME,
@@ -123,6 +53,48 @@
     VK_KHR_SWAPCHAIN_EXTENSION_NAME,
 };
 
+static HostConnection* getConnection(void) {
+    auto hostCon = HostConnection::getOrCreate(kCapsetGfxStreamVulkan);
+    return hostCon;
+}
+
+static gfxstream::vk::VkEncoder* getVkEncoder(HostConnection* con) { return con->vkEncoder(); }
+
+static VkResult SetupInstanceForProcess(void) {
+    uint32_t noRenderControlEnc = 0;
+    HostConnection* hostCon = getConnection();
+    if (!hostCon) {
+        ALOGE("vulkan: Failed to get host connection\n");
+        return VK_ERROR_DEVICE_LOST;
+    }
+
+    gfxstream::vk::ResourceTracker::get()->setupCaps(noRenderControlEnc);
+    // Legacy goldfish path: could be deleted once goldfish not used guest-side.
+    if (!noRenderControlEnc) {
+        // Implicitly sets up sequence number
+        ExtendedRCEncoderContext* rcEnc = hostCon->rcEncoder();
+        if (!rcEnc) {
+            ALOGE("vulkan: Failed to get renderControl encoder context\n");
+            return VK_ERROR_DEVICE_LOST;
+        }
+
+        gfxstream::vk::ResourceTracker::get()->setupFeatures(rcEnc->featureInfo_const());
+    }
+
+    gfxstream::vk::ResourceTracker::get()->setThreadingCallbacks({
+        .hostConnectionGetFunc = getConnection,
+        .vkEncoderGetFunc = getVkEncoder,
+    });
+    gfxstream::vk::ResourceTracker::get()->setSeqnoPtr(getSeqnoPtrForProcess());
+    gfxstream::vk::VkEncoder* vkEnc = getVkEncoder(hostCon);
+    if (!vkEnc) {
+        ALOGE("vulkan: Failed to get Vulkan encoder\n");
+        return VK_ERROR_DEVICE_LOST;
+    }
+
+    return VK_SUCCESS;
+}
+
 static bool isMesaOnlyInstanceExtension(const char* name) {
     for (auto mesaExt : kMesaOnlyInstanceExtension) {
         if (!strncmp(mesaExt, name, VK_MAX_EXTENSION_NAME_SIZE)) return true;
@@ -339,7 +311,7 @@
     VkResult result = VK_SUCCESS;
     /* Encoder call */
     {
-        ALOGE("calling setup instance internally");
+        ALOGV("calling setup instance internally");
         result = SetupInstanceForProcess();
         if (VK_SUCCESS != result) {
             return vk_error(NULL, result);
diff --git a/guest/vulkan_enc/Android.bp b/guest/vulkan_enc/Android.bp
index f129878..a672423 100644
--- a/guest/vulkan_enc/Android.bp
+++ b/guest/vulkan_enc/Android.bp
@@ -113,6 +113,7 @@
     ],
     header_libs: [
         "gfxstream_vulkan_headers",
+        "libOpenglSystemCommonHeaders",
         "libgfxstream_guest_iostream",
         "libnativewindow_headers",
         "mesa_common_headers_gfxstream",
@@ -133,9 +134,9 @@
         "libdrm",
         "libandroidemu_static",
         "libGoldfishAddressSpace",
-        "libgfxstream_guest_gralloc",
         "libmesa_vulkan_util_gfxstream",
         "libmesa_vulkan_runtime_gfxstream",
+        "libgfxstream_guest_android",
         "libgfxstream_guest_vulkan_entrypoints",
         "libmesa_util_gfxstream",
         "libmesa_util_format_gfxstream",
@@ -196,9 +197,11 @@
     defaults: [
         "libgfxstream_guest_vulkan_encoder_defaults",
     ],
-    shared_libs: [
+    static_libs: [
         "libplatform_rutabaga",
-        "libgfxstream_backend",
+    ],
+    export_static_lib_headers: [
+        "libplatform_rutabaga",
     ],
     target: {
         host: {
diff --git a/guest/vulkan_enc/AndroidHardwareBuffer.cpp b/guest/vulkan_enc/AndroidHardwareBuffer.cpp
index 0cb7794..814a780 100644
--- a/guest/vulkan_enc/AndroidHardwareBuffer.cpp
+++ b/guest/vulkan_enc/AndroidHardwareBuffer.cpp
@@ -20,8 +20,9 @@
 #endif
 
 #include <assert.h>
+#include <log/log.h>
 
-#include "../OpenglSystemCommon/HostConnection.h"
+#include "gfxstream/guest/Gralloc.h"
 #include "vk_format_info.h"
 #include "vk_util.h"
 
@@ -65,6 +66,9 @@
     const auto format = grallocHelper->getFormat(buffer);
     if (ahbFormatProps) {
         switch (format) {
+            case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+                ahbFormatProps->format = VK_FORMAT_R8_UNORM;
+                break;
             case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
                 ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM;
                 break;
diff --git a/guest/vulkan_enc/AndroidHardwareBuffer.h b/guest/vulkan_enc/AndroidHardwareBuffer.h
index 0389f09..5b2bef5 100644
--- a/guest/vulkan_enc/AndroidHardwareBuffer.h
+++ b/guest/vulkan_enc/AndroidHardwareBuffer.h
@@ -16,7 +16,7 @@
 
 #include <vulkan/vulkan.h>
 
-#include "../OpenglSystemCommon/Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 #include "HostVisibleMemoryVirtualization.h"
 
 // Structure similar to
diff --git a/guest/vulkan_enc/ResourceTracker.cpp b/guest/vulkan_enc/ResourceTracker.cpp
index eef58e6..7a568fd 100644
--- a/guest/vulkan_enc/ResourceTracker.cpp
+++ b/guest/vulkan_enc/ResourceTracker.cpp
@@ -489,7 +489,7 @@
     }
 }
 
-// TODO(fxbug.dev/90856): This is currently only used for allocating
+// TODO(fxbug.dev/42172354): This is currently only used for allocating
 // memory for dedicated external images. It should be migrated to use
 // SetBufferCollectionImageConstraintsFUCHSIA.
 VkResult ResourceTracker::setBufferCollectionConstraintsFUCHSIA(
@@ -702,8 +702,9 @@
 
 uint64_t getAHardwareBufferId(AHardwareBuffer* ahw) {
     uint64_t id = 0;
-#if defined(__ANDROID__) && ANDROID_API_LEVEL >= 31
-    AHardwareBuffer_getId(ahw, &id);
+#if defined(ANDROID)
+    auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper();
+    gralloc->getId(ahw, &id);
 #else
     (void)ahw;
 #endif
@@ -2299,7 +2300,7 @@
         // Importing read-only host memory into the Vulkan driver should not
         // work, but it is not an error to try to do so. Returning a
         // VkMemoryZirconHandlePropertiesFUCHSIA with no available
-        // memoryType bits should be enough for clients. See fxbug.dev/24225
+        // memoryType bits should be enough for clients. See fxbug.dev/42098398
         // for other issues this this flow.
         ALOGW("GetBufferHandleInfo failed: %d", result.value().error_value());
         pProperties->memoryTypeBits = 0;
@@ -2531,7 +2532,7 @@
                 ALOGW(
                     "%s: Non-zero flags (%08x) in image format "
                     "constraints; this is currently not supported, see "
-                    "fxbug.dev/68833.",
+                    "fxbug.dev/42147900.",
                     __func__, formatConstraints->flags);
             }
         }
@@ -3155,6 +3156,8 @@
     {
         AutoLock<RecursiveLock> lock(mLock);
         for (const auto& [memory, info] : info_VkDeviceMemory) {
+            if (info.device != device) continue;
+
             if (info.memoryTypeIndex != pAllocateInfo->memoryTypeIndex) continue;
 
             if (info.dedicated || dedicated) continue;
@@ -3560,7 +3563,7 @@
             fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
                 std::move(collection_ends->client));
             if (hasDedicatedImage) {
-                // TODO(fxbug.dev/90856): Use setBufferCollectionImageConstraintsFUCHSIA.
+                // TODO(fxbug.dev/42172354): Use setBufferCollectionImageConstraintsFUCHSIA.
                 VkResult res = setBufferCollectionConstraintsFUCHSIA(enc, device, &collection,
                                                                      pImageCreateInfo);
                 if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
@@ -5781,7 +5784,7 @@
                     post_wait_events.push_back({semInfo.eventHandle, semInfo.eventKoid});
 #ifndef FUCHSIA_NO_TRACE
                     if (semInfo.eventKoid != ZX_KOID_INVALID) {
-                        // TODO(fxbug.dev/66098): Remove the "semaphore"
+                        // TODO(fxbug.dev/42144867): Remove the "semaphore"
                         // FLOW_END events once it is removed from clients
                         // (for example, gfx Engine).
                         TRACE_FLOW_END("gfx", "semaphore", semInfo.eventKoid);
@@ -5936,13 +5939,7 @@
 
     auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper();
     const native_handle_t* nativeHandle = (const native_handle_t*)inputNativeInfo->handle;
-
-#if defined(END2END_TESTS)
-    // This is valid since the testing backend creates the handle and we know the layout.
-    *(uint32_t*)(outputNativeInfo->handle) = (uint32_t)nativeHandle->data[0];
-#else
     *(uint32_t*)(outputNativeInfo->handle) = gralloc->getHostHandle(nativeHandle);
-#endif
 }
 
 void ResourceTracker::unwrap_VkBindImageMemorySwapchainInfoKHR(
@@ -6374,6 +6371,13 @@
         bufferViews, inlineUniformBlockBuffer, true /* do lock */);
 }
 
+void ResourceTracker::on_vkUpdateDescriptorSetWithTemplateKHR(
+    void* context, VkDevice device, VkDescriptorSet descriptorSet,
+    VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) {
+    on_vkUpdateDescriptorSetWithTemplate(context, device, descriptorSet, descriptorUpdateTemplate,
+                                         pData);
+}
+
 VkResult ResourceTracker::on_vkGetPhysicalDeviceImageFormatProperties2_common(
     bool isKhr, void* context, VkResult input_result, VkPhysicalDevice physicalDevice,
     const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
@@ -6490,6 +6494,7 @@
     VkExternalBufferProperties* pExternalBufferProperties) {
     VkEncoder* enc = (VkEncoder*)context;
 
+#if defined(ANDROID)
     // Older versions of Goldfish's Gralloc did not support allocating AHARDWAREBUFFER_FORMAT_BLOB
     // with GPU usage (b/299520213).
     if (ResourceTracker::threadingCallbacks.hostConnectionGetFunc()
@@ -6502,6 +6507,7 @@
         pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = 0;
         return;
     }
+#endif
 
     uint32_t supportedHandleType = 0;
 #ifdef VK_USE_PLATFORM_FUCHSIA
diff --git a/guest/vulkan_enc/ResourceTracker.h b/guest/vulkan_enc/ResourceTracker.h
index 8173579..a40ea58 100644
--- a/guest/vulkan_enc/ResourceTracker.h
+++ b/guest/vulkan_enc/ResourceTracker.h
@@ -88,7 +88,7 @@
 #include "AndroidHardwareBuffer.h"
 #endif
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__Fuchsia__)
 #include <android/hardware_buffer.h>
 #endif
 
@@ -429,6 +429,11 @@
                                               VkDescriptorUpdateTemplate descriptorUpdateTemplate,
                                               const void* pData);
 
+    void on_vkUpdateDescriptorSetWithTemplateKHR(void* context, VkDevice device,
+                                              VkDescriptorSet descriptorSet,
+                                              VkDescriptorUpdateTemplate descriptorUpdateTemplate,
+                                              const void* pData);
+
     VkResult on_vkGetPhysicalDeviceImageFormatProperties2(
         void* context, VkResult input_result, VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
diff --git a/guest/vulkan_enc/func_table.cpp b/guest/vulkan_enc/func_table.cpp
index 38b0675..4dcfcc4 100644
--- a/guest/vulkan_enc/func_table.cpp
+++ b/guest/vulkan_enc/func_table.cpp
@@ -34,7 +34,6 @@
 
 #include <cstring>
 
-#include "../OpenglSystemCommon/HostConnection.h"
 #include "ResourceTracker.h"
 #include "VkEncoder.h"
 #include "gfxstream_vk_entrypoints.h"
@@ -4016,9 +4015,10 @@
                    descriptorUpdateTemplate);
     {
         auto vkEnc = gfxstream::vk::ResourceTracker::getThreadLocalEncoder();
-        vkEnc->vkUpdateDescriptorSetWithTemplateKHR(
-            gfxstream_device->internal_object, descriptorSet,
-            gfxstream_descriptorUpdateTemplate->internal_object, pData, true /* do lock */);
+        auto resources = gfxstream::vk::ResourceTracker::get();
+        resources->on_vkUpdateDescriptorSetWithTemplateKHR(
+            vkEnc, gfxstream_device->internal_object, descriptorSet,
+            gfxstream_descriptorUpdateTemplate->internal_object, pData);
     }
 }
 #endif
diff --git a/guest/vulkan_enc/vk_format_info.h b/guest/vulkan_enc/vk_format_info.h
index 2d5d84e..5bf26e5 100644
--- a/guest/vulkan_enc/vk_format_info.h
+++ b/guest/vulkan_enc/vk_format_info.h
@@ -35,6 +35,10 @@
     HAL_PIXEL_FORMAT_YV12 = 842094169,
 };
 #endif
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
 #include <vndk/hardware_buffer.h>
 #include <vulkan/vulkan.h>
 
@@ -57,6 +61,8 @@
 // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM.
 static inline VkFormat vk_format_from_android(unsigned android_format) {
     switch (android_format) {
+        case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+            return VK_FORMAT_R8_UNORM;
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
             return VK_FORMAT_R8G8B8A8_UNORM;
         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
@@ -89,6 +95,8 @@
 
 static inline unsigned android_format_from_vk(VkFormat vk_format) {
     switch (vk_format) {
+        case VK_FORMAT_R8_UNORM:
+            return AHARDWAREBUFFER_FORMAT_R8_UNORM;
         case VK_FORMAT_R8G8B8A8_UNORM:
             return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
         case VK_FORMAT_R8G8B8_UNORM:
@@ -111,6 +119,7 @@
 static inline bool android_format_is_yuv(unsigned android_format) {
     switch (android_format) {
         case AHARDWAREBUFFER_FORMAT_BLOB:
+        case AHARDWAREBUFFER_FORMAT_R8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
diff --git a/host/gl/ColorBufferGl.cpp b/host/gl/ColorBufferGl.cpp
index a38f18c..55eb6ae 100644
--- a/host/gl/ColorBufferGl.cpp
+++ b/host/gl/ColorBufferGl.cpp
@@ -226,7 +226,7 @@
     if (!sGetFormatParameters(&p_internalFormat, &texFormat, &pixelType,
                               &bytesPerPixel, &p_sizedInternalFormat,
                               &isBlob)) {
-        fprintf(stderr, "ColorBufferGl::create invalid format 0x%x\n", p_internalFormat);
+        ERR("ColorBufferGl::create invalid format 0x%x", p_internalFormat);
         return nullptr;
     }
     const unsigned long bufsize = ((unsigned long)bytesPerPixel) * p_width
diff --git a/host/gl/glestranslator/EGL/EglImp.cpp b/host/gl/glestranslator/EGL/EglImp.cpp
index f2fc602..c5de1e5 100644
--- a/host/gl/glestranslator/EGL/EglImp.cpp
+++ b/host/gl/glestranslator/EGL/EglImp.cpp
@@ -31,6 +31,7 @@
 #include "aemu/base/system/System.h"
 #include "aemu/base/SharedLibrary.h"
 #include "host-common/GfxstreamFatalError.h"
+#include "host-common/emugl_vm_operations.h"
 #include "host-common/logging.h"
 
 #include "EglWindowSurface.h"
@@ -1387,39 +1388,40 @@
 {
     VALIDATE_DISPLAY(display);
 
-	if (target != EGL_GL_TEXTURE_2D_KHR) {
-		// Create image from underlying and add to registry
-		EGLImage image = dpy->createNativeImage(dpy->getHostDriverDisplay(), 0, target, buffer, attrib_list);
+    if (target != EGL_GL_TEXTURE_2D_KHR) {
+        // Create image from underlying and add to registry
+        EGLImage image = dpy->createNativeImage(dpy->getHostDriverDisplay(), 0, target, buffer, attrib_list);
 
-		if (image == EGL_NO_IMAGE_KHR) {
-			return EGL_NO_IMAGE_KHR;
-		}
+        if (image == EGL_NO_IMAGE_KHR) {
+            return EGL_NO_IMAGE_KHR;
+        }
 
-		ImagePtr img( new EglImage() );
-		img->isNative = true;
-		img->nativeImage = image;
-		img->width = 0;
-		img->height = 0;
-		if (attrib_list) {
-			const EGLint* current = attrib_list;
-			while (EGL_NONE != *current) {
-				switch (*current) {
-					case EGL_WIDTH:
-						img->width = current[1];
-						break;
-					case EGL_HEIGHT:
-						img->height = current[1];
-						break;
-					case EGL_LINUX_DRM_FOURCC_EXT:
-					    // TODO: Translate drm fourcc to internal format
-						// img->fourcc = current[1];
-						break;
-				}
-				current += 2;
-			}
-		}
-		return dpy->addImageKHR(img);
-	}
+        ImagePtr img( new EglImage() );
+        img->isNative = true;
+        img->nativeImage = image;
+        img->width = 0;
+        img->height = 0;
+        if (attrib_list) {
+            const EGLint* current = attrib_list;
+            while (EGL_NONE != *current) {
+                switch (*current) {
+                    case EGL_WIDTH:
+                        img->width = current[1];
+                        break;
+                    case EGL_HEIGHT:
+                        img->height = current[1];
+                        break;
+                    case EGL_LINUX_DRM_FOURCC_EXT:
+                        // TODO: Translate drm fourcc to internal format
+                        // img->fourcc = current[1];
+                        break;
+                }
+                current += 2;
+            }
+        }
+        get_emugl_vm_operations().setSkipSnapshotSave(true);
+        return dpy->addImageKHR(img);
+    }
 
     ThreadInfo* thread  = getThreadInfo();
     ShareGroupPtr sg = thread->shareGroup;
diff --git a/host/meson.build b/host/meson.build
index 73e0c5d..c37eac4 100644
--- a/host/meson.build
+++ b/host/meson.build
@@ -104,7 +104,7 @@
 
 if use_magma
   gfxstream_host_args += '-DGFXSTREAM_ENABLE_HOST_GLES=1'
-  drm_dep = dependency('libdrm')
+  drm_dep = dependency('libdrm', fallback: ['libdrm', 'libdrm_dep'])
 else
   gfxstream_host_args += '-DGFXSTREAM_ENABLE_HOST_GLES=0'
 endif
@@ -123,11 +123,7 @@
 inc_utils = include_directories('../utils/include')
 
 if use_vulkan
-  if cc.has_header('vulkan/vulkan.h')
-    inc_vulkan_headers = include_directories()
-  else
-    inc_vulkan_headers = include_directories('../common/vulkan/include')
-  endif
+  inc_vulkan_headers = include_directories('../common/vulkan/include')
 
   if cc.has_header('renderdoc_app.h')
     inc_renderdoc_external = include_directories()
diff --git a/host/tests/ImageUtils.h b/host/tests/ImageUtils.h
index 3ff83fc..a5593ac 100644
--- a/host/tests/ImageUtils.h
+++ b/host/tests/ImageUtils.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <cctype>
+#include <cstdint>
 #include <string>
 #include <vector>
 
diff --git a/host/vulkan/Android.bp b/host/vulkan/Android.bp
index 4e1443e..d5f1d0f 100644
--- a/host/vulkan/Android.bp
+++ b/host/vulkan/Android.bp
@@ -53,6 +53,7 @@
         "VkDecoderGlobalState.cpp",
         "RenderThreadInfoVk.cpp",
         "VkDecoderSnapshot.cpp",
+        "VkDecoderSnapshotUtils.cpp",
         "VkFormatUtils.cpp",
         "VkReconstruction.cpp",
         "VulkanDispatch.cpp",
diff --git a/host/vulkan/CMakeLists.txt b/host/vulkan/CMakeLists.txt
index 80e6ab1..9f39855 100644
--- a/host/vulkan/CMakeLists.txt
+++ b/host/vulkan/CMakeLists.txt
@@ -17,6 +17,7 @@
             VkDecoder.cpp
             VkDecoderGlobalState.cpp
             VkDecoderSnapshot.cpp
+            VkDecoderSnapshotUtils.cpp
             VkFormatUtils.cpp
             VkReconstruction.cpp
             VulkanDispatch.cpp
diff --git a/host/vulkan/CompositorVk.cpp b/host/vulkan/CompositorVk.cpp
index e861e13..39f7e51 100644
--- a/host/vulkan/CompositorVk.cpp
+++ b/host/vulkan/CompositorVk.cpp
@@ -129,14 +129,13 @@
     }
 }
 
-std::unique_ptr<CompositorVk> CompositorVk::create(const VulkanDispatch& vk, VkDevice vkDevice,
-                                                   VkPhysicalDevice vkPhysicalDevice,
-                                                   VkQueue vkQueue,
-                                                   std::shared_ptr<android::base::Lock> queueLock,
-                                                   uint32_t queueFamilyIndex,
-                                                   uint32_t maxFramesInFlight) {
-    auto res = std::unique_ptr<CompositorVk>(new CompositorVk(
-        vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex, maxFramesInFlight));
+std::unique_ptr<CompositorVk> CompositorVk::create(
+    const VulkanDispatch& vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
+    std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
+    uint32_t maxFramesInFlight, DebugUtilsHelper debugUtils) {
+    auto res = std::unique_ptr<CompositorVk>(new CompositorVk(vk, vkDevice, vkPhysicalDevice,
+                                                              vkQueue, queueLock, queueFamilyIndex,
+                                                              maxFramesInFlight, debugUtils));
     res->setUpCommandPool();
     res->setUpSampler();
     res->setUpGraphicsPipeline();
@@ -152,9 +151,10 @@
 CompositorVk::CompositorVk(const VulkanDispatch& vk, VkDevice vkDevice,
                            VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
                            std::shared_ptr<android::base::Lock> queueLock,
-                           uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
+                           uint32_t queueFamilyIndex, uint32_t maxFramesInFlight,
+                           DebugUtilsHelper debugUtilsHelper)
     : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex,
-                       maxFramesInFlight),
+                       maxFramesInFlight, debugUtilsHelper),
       m_maxFramesInFlight(maxFramesInFlight),
       m_renderTargetCache(k_renderTargetCacheSize) {}
 
@@ -506,6 +506,7 @@
     VkCommandPool commandPool = VK_NULL_HANDLE;
     VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCreateInfo, nullptr, &commandPool));
     m_vkCommandPool = commandPool;
+    m_debugUtilsHelper.addDebugLabel(m_vkCommandPool, "CompositorVk command pool");
 }
 
 void CompositorVk::setUpFences() {
@@ -1016,17 +1017,20 @@
         };
 
         if (layer.props.composeMode == HWC2_COMPOSITION_SOLID_COLOR) {
+            descriptorSetContents.binding0.sampledImageId = 0;
             descriptorSetContents.binding0.sampledImageView = m_defaultImage.m_vkImageView;
             descriptorSetContents.binding1.color =
                 glm::vec4(static_cast<float>(layer.props.color.r) / 255.0f,
                           static_cast<float>(layer.props.color.g) / 255.0f,
                           static_cast<float>(layer.props.color.b) / 255.0f,
                           static_cast<float>(layer.props.color.a) / 255.0f);
+
         } else {
             if (sourceImage == nullptr) {
                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
                     << "CompositorVk failed to find sourceImage.";
             }
+            descriptorSetContents.binding0.sampledImageId = sourceImage->id;
             descriptorSetContents.binding0.sampledImageView = sourceImage->imageView;
             compositionVk->layersSourceImages.emplace_back(sourceImage);
         }
@@ -1068,6 +1072,9 @@
             &postCompositionLayoutTransitionBarriers, &postCompositionQueueTransferBarriers);
     }
 
+    static uint32_t sCompositionNumber = 0;
+    const uint32_t thisCompositionNumber = sCompositionNumber++;
+
     VkCommandBuffer& commandBuffer = frameResources->m_vkCommandBuffer;
     if (commandBuffer != VK_NULL_HANDLE) {
         m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &commandBuffer);
@@ -1081,12 +1088,19 @@
     };
     VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &commandBufferAllocInfo, &commandBuffer));
 
+    m_debugUtilsHelper.addDebugLabel(commandBuffer, "CompositorVk composition:%d command buffer",
+                                     thisCompositionNumber);
+
     const VkCommandBufferBeginInfo beginInfo = {
         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
     };
     VK_CHECK(m_vk.vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    m_debugUtilsHelper.cmdBeginDebugLabel(commandBuffer,
+                                          "CompositorVk composition:%d into ColorBuffer:%d",
+                                          thisCompositionNumber, compositionVk.targetImage->id);
+
     if (!preCompositionQueueTransferBarriers.empty()) {
         m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                                   VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
@@ -1162,6 +1176,9 @@
 
     const uint32_t numLayers = compositionVk.layersDescriptorSets.descriptorSets.size();
     for (uint32_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) {
+        m_debugUtilsHelper.cmdBeginDebugLabel(commandBuffer, "CompositorVk compose layer:%d",
+                                              layerIndex);
+
         VkDescriptorSet layerDescriptorSet = frameResources->m_layerDescriptorSets[layerIndex];
 
         m_vk.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -1172,6 +1189,8 @@
                                      /*pDynamicOffsets=*/nullptr);
 
         m_vk.vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0);
+
+        m_debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
     }
 
     m_vk.vkCmdEndRenderPass(commandBuffer);
@@ -1219,9 +1238,14 @@
             postCompositionQueueTransferBarriers.data());
     }
 
+    m_debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(m_vk.vkEndCommandBuffer(commandBuffer));
 
     VkFence composeCompleteFence = frameResources->m_vkFence;
+    m_debugUtilsHelper.addDebugLabel(
+        composeCompleteFence, "CompositorVk composition:%d complete fence", thisCompositionNumber);
+
     VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence));
 
     const VkPipelineStageFlags submitWaitStages[] = {
@@ -1277,14 +1301,16 @@
 
 bool operator==(const CompositorVkBase::DescriptorSetContents& lhs,
                 const CompositorVkBase::DescriptorSetContents& rhs) {
-    return std::tie(lhs.binding0.sampledImageView,   //
+    return std::tie(lhs.binding0.sampledImageId,     //
+                    lhs.binding0.sampledImageView,   //
                     lhs.binding1.mode,               //
                     lhs.binding1.alpha,              //
                     lhs.binding1.color,              //
                     lhs.binding1.positionTransform,  //
                     lhs.binding1.texCoordTransform)  //
                      ==                              //
-           std::tie(rhs.binding0.sampledImageView,   //
+           std::tie(rhs.binding0.sampledImageId,     //
+                    rhs.binding0.sampledImageView,   //
                     rhs.binding1.mode,               //
                     rhs.binding1.alpha,              //
                     rhs.binding1.color,              //
diff --git a/host/vulkan/CompositorVk.h b/host/vulkan/CompositorVk.h
index cd98e23..a5fc688 100644
--- a/host/vulkan/CompositorVk.h
+++ b/host/vulkan/CompositorVk.h
@@ -15,9 +15,10 @@
 #include "BorrowedImage.h"
 #include "BorrowedImageVk.h"
 #include "Compositor.h"
+#include "DebugUtilsHelper.h"
 #include "Hwc2.h"
-#include "aemu/base/synchronization/Lock.h"
 #include "aemu/base/LruCache.h"
+#include "aemu/base/synchronization/Lock.h"
 #include "goldfish_vk_dispatch.h"
 #include "vulkan/vk_util.h"
 
@@ -41,6 +42,7 @@
     const VkPhysicalDevice m_vkPhysicalDevice;
     const VkQueue m_vkQueue;
     const uint32_t m_queueFamilyIndex;
+    const DebugUtilsHelper m_debugUtilsHelper;
     std::shared_ptr<android::base::Lock> m_vkQueueLock;
     VkDescriptorSetLayout m_vkDescriptorSetLayout;
     VkPipelineLayout m_vkPipelineLayout;
@@ -71,6 +73,9 @@
 
     // Keep in sync with vulkan/Compositor.frag.
     struct SamplerBinding {
+        // Include the image id to trigger a descriptor update to handle the case
+        // that the VkImageView is recycled across different images (b/322998473).
+        uint32_t sampledImageId = 0;
         VkImageView sampledImageView = VK_NULL_HANDLE;
     };
 
@@ -114,12 +119,14 @@
     explicit CompositorVkBase(const VulkanDispatch& vk, VkDevice device,
                               VkPhysicalDevice physicalDevice, VkQueue queue,
                               std::shared_ptr<android::base::Lock> queueLock,
-                              uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
+                              uint32_t queueFamilyIndex, uint32_t maxFramesInFlight,
+                              DebugUtilsHelper debugUtils)
         : m_vk(vk),
           m_vkDevice(device),
           m_vkPhysicalDevice(physicalDevice),
           m_vkQueue(queue),
           m_queueFamilyIndex(queueFamilyIndex),
+          m_debugUtilsHelper(debugUtils),
           m_vkQueueLock(queueLock),
           m_vkDescriptorSetLayout(VK_NULL_HANDLE),
           m_vkPipelineLayout(VK_NULL_HANDLE),
@@ -137,11 +144,11 @@
 
 class CompositorVk : protected CompositorVkBase, public Compositor {
    public:
-    static std::unique_ptr<CompositorVk> create(const VulkanDispatch& vk, VkDevice vkDevice,
-                                                VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
-                                                std::shared_ptr<android::base::Lock> queueLock,
-                                                uint32_t queueFamilyIndex,
-                                                uint32_t maxFramesInFlight);
+    static std::unique_ptr<CompositorVk> create(
+        const VulkanDispatch& vk, VkDevice vkDevice, VkPhysicalDevice vkPhysicalDevice,
+        VkQueue vkQueue, std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
+        uint32_t maxFramesInFlight,
+        DebugUtilsHelper debugUtils = DebugUtilsHelper::withUtilsDisabled());
 
     ~CompositorVk();
 
@@ -156,7 +163,7 @@
    private:
     explicit CompositorVk(const VulkanDispatch&, VkDevice, VkPhysicalDevice, VkQueue,
                           std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
-                          uint32_t maxFramesInFlight);
+                          uint32_t maxFramesInFlight, DebugUtilsHelper debugUtils);
 
     void setUpGraphicsPipeline();
     void setUpVertexBuffers();
diff --git a/host/vulkan/DebugUtilsHelper.h b/host/vulkan/DebugUtilsHelper.h
index fd52e77..7900cea 100644
--- a/host/vulkan/DebugUtilsHelper.h
+++ b/host/vulkan/DebugUtilsHelper.h
@@ -36,25 +36,31 @@
         }
 
         VkObjectType objectType = VK_OBJECT_TYPE_UNKNOWN;
-        if constexpr(std::is_same_v<VkObjectT, VkCommandBuffer>) {
+        if constexpr (std::is_same_v<VkObjectT, VkBuffer>) {
+            objectType = VK_OBJECT_TYPE_BUFFER;
+        } else if constexpr (std::is_same_v<VkObjectT, VkCommandBuffer>) {
             objectType = VK_OBJECT_TYPE_COMMAND_BUFFER;
-        } else if constexpr(std::is_same_v<VkObjectT, VkCommandPool>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkCommandPool>) {
             objectType = VK_OBJECT_TYPE_COMMAND_POOL;
-        } else if constexpr(std::is_same_v<VkObjectT, VkDescriptorSet>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkDescriptorSet>) {
             objectType = VK_OBJECT_TYPE_DESCRIPTOR_SET;
-        } else if constexpr(std::is_same_v<VkObjectT, VkFramebuffer>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkDeviceMemory>) {
+            objectType = VK_OBJECT_TYPE_DEVICE_MEMORY;
+        } else if constexpr (std::is_same_v<VkObjectT, VkFence>) {
+            objectType = VK_OBJECT_TYPE_FENCE;
+        } else if constexpr (std::is_same_v<VkObjectT, VkFramebuffer>) {
             objectType = VK_OBJECT_TYPE_FRAMEBUFFER;
-        } else if constexpr(std::is_same_v<VkObjectT, VkImage>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkImage>) {
             objectType = VK_OBJECT_TYPE_IMAGE;
-        } else if constexpr(std::is_same_v<VkObjectT, VkImageView>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkImageView>) {
             objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
-        } else if constexpr(std::is_same_v<VkObjectT, VkPipeline>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkPipeline>) {
             objectType = VK_OBJECT_TYPE_PIPELINE;
-        } else if constexpr(std::is_same_v<VkObjectT, VkSampler>) {
+        } else if constexpr (std::is_same_v<VkObjectT, VkSampler>) {
             objectType = VK_OBJECT_TYPE_SAMPLER;
         } else {
             static_assert(sizeof(VkObjectT) == 0,
-                                 "Unhandled VkObjectT. Please update DebugUtilsHelper.h.");
+                          "Unhandled VkObjectT. Please update DebugUtilsHelper.h.");
         }
 
         addDebugLabelToHandle((uint64_t)object, objectType, format, std::forward<T>(formatArgs)...);
@@ -63,6 +69,8 @@
     void cmdBeginDebugLabel(VkCommandBuffer commandBuffer, const char* format, ...) const;
     void cmdEndDebugLabel(VkCommandBuffer commandBuffer) const;
 
+    bool isEnabled() const { return m_debugUtilsEnabled; }
+
    private:
     DebugUtilsHelper(bool enabled, VkDevice device, const VulkanDispatch* dispatch);
 
diff --git a/host/vulkan/VkAndroidNativeBuffer.cpp b/host/vulkan/VkAndroidNativeBuffer.cpp
index 095d387..d71875b 100644
--- a/host/vulkan/VkAndroidNativeBuffer.cpp
+++ b/host/vulkan/VkAndroidNativeBuffer.cpp
@@ -498,6 +498,7 @@
                                                 Lock* defaultQueueLock, VkSemaphore semaphore,
                                                 VkFence fence, AndroidNativeBufferInfo* anbInfo) {
     auto fb = FrameBuffer::getFB();
+    auto emu = getGlobalVkEmulation();
 
     bool firstTimeSetup = !anbInfo->everSynced && !anbInfo->everAcquired;
 
@@ -541,6 +542,10 @@
 
             vk->vkBeginCommandBuffer(queueState.cb2, &beginInfo);
 
+            emu->debugUtilsHelper.cmdBeginDebugLabel(queueState.cb2,
+                                                     "vkAcquireImageANDROID(ColorBuffer:%d)",
+                                                     anbInfo->colorBufferHandle);
+
             VkImageMemoryBarrier queueTransferBarrier = {
                 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                 .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
@@ -562,6 +567,9 @@
             vk->vkCmdPipelineBarrier(queueState.cb2, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                                      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
                                      1, &queueTransferBarrier);
+
+            emu->debugUtilsHelper.cmdEndDebugLabel(queueState.cb2);
+
             vk->vkEndCommandBuffer(queueState.cb2);
 
             VkSubmitInfo submitInfo = {
@@ -628,6 +636,8 @@
         queueState.setup(vk, anbInfo->device, queue, queueFamilyIndex, queueLock);
     }
 
+    auto emu = getGlobalVkEmulation();
+
     // Record our synchronization commands.
     VkCommandBufferBeginInfo beginInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@@ -638,6 +648,10 @@
 
     vk->vkBeginCommandBuffer(queueState.cb, &beginInfo);
 
+    emu->debugUtilsHelper.cmdBeginDebugLabel(queueState.cb,
+                                             "vkQueueSignalReleaseImageANDROID(ColorBuffer:%d)",
+                                             anbInfo->colorBufferHandle);
+
     // If using the Vulkan image directly (rather than copying it back to
     // the CPU), change its layout for that use.
     if (anbInfo->useVulkanNativeImage) {
@@ -737,6 +751,8 @@
                                  &backToPresentSrc);
     }
 
+    emu->debugUtilsHelper.cmdEndDebugLabel(queueState.cb);
+
     vk->vkEndCommandBuffer(queueState.cb);
 
     std::vector<VkPipelineStageFlags> pipelineStageFlags;
diff --git a/host/vulkan/VkCommonOperations.cpp b/host/vulkan/VkCommonOperations.cpp
index 83b84e5..990ceaa 100644
--- a/host/vulkan/VkCommonOperations.cpp
+++ b/host/vulkan/VkCommonOperations.cpp
@@ -1300,9 +1300,10 @@
         if (sVkEmulation->compositorVk) {
             ERR("Reset VkEmulation::compositorVk.");
         }
-        sVkEmulation->compositorVk = CompositorVk::create(
-            *sVkEmulation->ivk, sVkEmulation->device, sVkEmulation->physdev, sVkEmulation->queue,
-            sVkEmulation->queueLock, sVkEmulation->queueFamilyIndex, 3);
+        sVkEmulation->compositorVk =
+            CompositorVk::create(*sVkEmulation->ivk, sVkEmulation->device, sVkEmulation->physdev,
+                                 sVkEmulation->queue, sVkEmulation->queueLock,
+                                 sVkEmulation->queueFamilyIndex, 3, sVkEmulation->debugUtilsHelper);
     }
 
     if (features->useVulkanNativeSwapchain) {
@@ -2131,6 +2132,13 @@
     }
 #endif
 
+    sVkEmulation->debugUtilsHelper.addDebugLabel(infoPtr->image, "ColorBuffer:%d",
+                                                 colorBufferHandle);
+    sVkEmulation->debugUtilsHelper.addDebugLabel(infoPtr->imageView, "ColorBuffer:%d",
+                                                 colorBufferHandle);
+    sVkEmulation->debugUtilsHelper.addDebugLabel(infoPtr->memory.memory, "ColorBuffer:%d",
+                                                 colorBufferHandle);
+
     infoPtr->initialized = true;
 
     return true;
@@ -2438,6 +2446,9 @@
 
     VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    sVkEmulation->debugUtilsHelper.cmdBeginDebugLabel(
+        commandBuffer, "readColorBufferToBytes(ColorBuffer:%d)", colorBufferHandle);
+
     const VkImageMemoryBarrier toTransferSrcImageBarrier = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
         .pNext = nullptr,
@@ -2468,6 +2479,8 @@
                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, sVkEmulation->staging.buffer,
                                bufferImageCopies.size(), bufferImageCopies.data());
 
+    sVkEmulation->debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
 
     const VkSubmitInfo submitInfo = {
@@ -2622,16 +2635,15 @@
         std::memcpy(stagingBufferPtr, pixels, dstBufferSize);
     }
 
-    // Avoid transitioning from VK_IMAGE_LAYOUT_UNDEFINED. Unfortunetly, Android does not
-    // yet have a mechanism for sharing the expected VkImageLayout. However, the Vulkan
-    // spec's image layout transition sections says "If the old layout is
-    // VK_IMAGE_LAYOUT_UNDEFINED, the contents of that range may be discarded." Some
-    // Vulkan drivers have been observed to actually perform the discard which leads to
-    // ColorBuffer-s being unintentionally cleared. See go/ahb-vkimagelayout for a more
-    // thorough write up.
-    if (colorBufferInfo->currentLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
-        colorBufferInfo->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
-    }
+    // NOTE: Host vulkan state might not know the correct layout of the
+    // destination image, as guest grallocs are designed to be used by either
+    // GL or Vulkan. Consequently, we typically avoid image transitions from
+    // VK_IMAGE_LAYOUT_UNDEFINED as Vulkan spec allows the contents to be
+    // discarded (and some drivers have been observed doing it). You can
+    // check go/ahb-vkimagelayout for more information. But since this
+    // function does not allow subrects (see above), it will write the
+    // provided contents onto the entirety of the target buffer, meaning this
+    // risk of discarding data should not impact anything.
 
     // Record our synchronization commands.
     const VkCommandBufferBeginInfo beginInfo = {
@@ -2644,6 +2656,9 @@
 
     VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    sVkEmulation->debugUtilsHelper.cmdBeginDebugLabel(
+        commandBuffer, "updateColorBufferFromBytes(ColorBuffer:%d)", colorBufferHandle);
+
     const VkImageMemoryBarrier toTransferDstImageBarrier = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
         .pNext = nullptr,
@@ -2675,6 +2690,8 @@
                                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, bufferImageCopies.size(),
                                bufferImageCopies.data());
 
+    sVkEmulation->debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
 
     const VkSubmitInfo submitInfo = {
@@ -2982,6 +2999,10 @@
     res.glExported = false;
 
     sVkEmulation->buffers[bufferHandle] = res;
+
+    sVkEmulation->debugUtilsHelper.addDebugLabel(res.buffer, "Buffer:%d", bufferHandle);
+    sVkEmulation->debugUtilsHelper.addDebugLabel(res.memory.memory, "Buffer:%d", bufferHandle);
+
     return allocRes;
 }
 
@@ -3052,6 +3073,9 @@
 
     VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    sVkEmulation->debugUtilsHelper.cmdBeginDebugLabel(commandBuffer, "readBufferToBytes(Buffer:%d)",
+                                                      bufferHandle);
+
     const VkBufferCopy bufferCopy = {
         .srcOffset = offset,
         .dstOffset = 0,
@@ -3060,6 +3084,8 @@
     vk->vkCmdCopyBuffer(commandBuffer, bufferInfo->buffer, stagingBufferInfo.buffer, 1,
                         &bufferCopy);
 
+    sVkEmulation->debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
 
     const VkSubmitInfo submitInfo = {
@@ -3154,6 +3180,9 @@
 
     VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    sVkEmulation->debugUtilsHelper.cmdBeginDebugLabel(
+        commandBuffer, "updateBufferFromBytes(Buffer:%d)", bufferHandle);
+
     const VkBufferCopy bufferCopy = {
         .srcOffset = 0,
         .dstOffset = offset,
@@ -3162,6 +3191,8 @@
     vk->vkCmdCopyBuffer(commandBuffer, stagingBufferInfo.buffer, bufferInfo->buffer, 1,
                         &bufferCopy);
 
+    sVkEmulation->debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
 
     const VkSubmitInfo submitInfo = {
@@ -3416,6 +3447,9 @@
     };
     VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
 
+    sVkEmulation->debugUtilsHelper.cmdBeginDebugLabel(
+        commandBuffer, "releaseColorBufferForGuestUse(ColorBuffer:%d)", colorBufferHandle);
+
     if (layoutTransitionBarrier) {
         vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                                  VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
@@ -3427,6 +3461,8 @@
                                  &queueTransferBarrier.value());
     }
 
+    sVkEmulation->debugUtilsHelper.cmdEndDebugLabel(commandBuffer);
+
     VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
 
     const VkSubmitInfo submitInfo = {
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index 30f6022..27cc7da 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -27,7 +27,9 @@
 #include "VkAndroidNativeBuffer.h"
 #include "VkCommonOperations.h"
 #include "VkDecoderContext.h"
+#include "VkDecoderInternalStructs.h"
 #include "VkDecoderSnapshot.h"
+#include "VkDecoderSnapshotUtils.h"
 #include "VulkanDispatch.h"
 #include "VulkanStream.h"
 #include "aemu/base/ManagedDescriptor.hpp"
@@ -52,9 +54,9 @@
 #include "host-common/emugl_vm_operations.h"
 #include "host-common/feature_control.h"
 #include "host-common/vm_operations.h"
-#include "vulkan/VkFormatUtils.h"
 #include "utils/RenderDoc.h"
 #include "vk_util.h"
+#include "vulkan/VkFormatUtils.h"
 #include "vulkan/emulated_textures/AstcTexture.h"
 #include "vulkan/emulated_textures/CompressedImageInfo.h"
 #include "vulkan/emulated_textures/GpuDecompressionPipeline.h"
@@ -189,6 +191,12 @@
 
 static uint64_t hostBlobId = 0;
 
+// b/319729462
+// On snapshot load, thread local data is not available, thus we use a
+// fake context ID. We will eventually need to fix it once we start using
+// snapshot with virtio.
+static uint32_t kTemporaryContextIdForSnapshotLoading = 1;
+
 #define DEFINE_BOXED_HANDLE_TYPE_TAG(type) Tag_##type,
 
 enum BoxedHandleTypeTag {
@@ -398,7 +406,78 @@
 
     bool vkCleanupEnabled() const { return mVkCleanupEnabled; }
 
-    void save(android::base::Stream* stream) { snapshot()->save(stream); }
+    void save(android::base::Stream* stream) {
+        mSnapshotState = SnapshotState::Saving;
+        snapshot()->save(stream);
+        // Save mapped memory
+        uint32_t memoryCount = 0;
+        for (const auto& it : mMemoryInfo) {
+            if (it.second.ptr) {
+                memoryCount++;
+            }
+        }
+        stream->putBe32(memoryCount);
+        for (const auto& it : mMemoryInfo) {
+            if (!it.second.ptr) {
+                continue;
+            }
+            stream->putBe64(reinterpret_cast<uint64_t>(
+                unboxed_to_boxed_non_dispatchable_VkDeviceMemory(it.first)));
+            stream->putBe64(it.second.size);
+            stream->write(it.second.ptr, it.second.size);
+        }
+        // Set up VK structs to snapshot other Vulkan objects
+        // TODO(b/323064243): group all images from the same device and reuse queue / command pool
+
+        VulkanDispatch* ivk = getGlobalVkEmulation()->ivk;
+        VulkanDispatch* dvk = getGlobalVkEmulation()->dvk;
+        for (const auto& imageIte : mImageInfo) {
+            const ImageInfo& imageInfo = imageIte.second;
+            if (imageInfo.memory == VK_NULL_HANDLE) {
+                continue;
+            }
+            const auto& device = imageInfo.device;
+            const auto& deviceInfo = android::base::find(mDeviceInfo, device);
+            const auto physicalDevice = deviceInfo->physicalDevice;
+            const auto& physicalDeviceInfo = android::base::find(mPhysdevInfo, physicalDevice);
+            StateBlock stateBlock{
+                .physicalDevice = physicalDevice,
+                .physicalDeviceInfo = physicalDeviceInfo,
+                .device = device,
+                .queue = VK_NULL_HANDLE,
+                .commandPool = VK_NULL_HANDLE,
+            };
+
+            uint32_t queueFamilyCount = 0;
+            ivk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount,
+                                                          nullptr);
+            std::vector<VkQueueFamilyProperties> queueFamilyProps(queueFamilyCount);
+            ivk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount,
+                                                          queueFamilyProps.data());
+            uint32_t queueFamilyIndex = 0;
+            for (auto queue : deviceInfo->queues) {
+                int idx = queue.first;
+                if ((queueFamilyProps[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0) {
+                    continue;
+                }
+                stateBlock.queue = queue.second[0];
+                queueFamilyIndex = idx;
+                break;
+            }
+
+            VkCommandPoolCreateInfo commandPoolCi = {
+                VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+                0,
+                VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+                queueFamilyIndex,
+            };
+            dvk->vkCreateCommandPool(device, &commandPoolCi, nullptr, &stateBlock.commandPool);
+            // TODO(b/294277842): make sure the queue is empty before using.
+            saveImageContent(stream, &stateBlock, imageIte.first, &imageInfo);
+            dvk->vkDestroyCommandPool(device, stateBlock.commandPool, nullptr);
+        }
+        mSnapshotState = SnapshotState::Normal;
+    }
 
     void load(android::base::Stream* stream, GfxApiLogger& gfxLogger,
               HealthMonitor<>* healthMonitor) {
@@ -407,9 +486,77 @@
 
         // destroy all current internal data structures
         clear();
-
+        mSnapshotState = SnapshotState::Loading;
         // this part will replay in the decoder
         snapshot()->load(stream, gfxLogger, healthMonitor);
+        // load mapped memory
+        uint32_t memoryCount = stream->getBe32();
+        for (uint32_t i = 0; i < memoryCount; i++) {
+            VkDeviceMemory boxedMemory = reinterpret_cast<VkDeviceMemory>(stream->getBe64());
+            VkDeviceMemory unboxedMemory = unbox_VkDeviceMemory(boxedMemory);
+            auto it = mMemoryInfo.find(unboxedMemory);
+            if (it == mMemoryInfo.end()) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "Snapshot load failure: cannot find memory handle for " << boxedMemory;
+            }
+            VkDeviceSize size = stream->getBe64();
+            if (size != it->second.size || !it->second.ptr) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "Snapshot load failure: memory size does not match for " << boxedMemory;
+            }
+            stream->read(it->second.ptr, size);
+        }
+        // Set up VK structs to snapshot other Vulkan objects
+        // TODO(b/323064243): group all images from the same device and reuse queue / command pool
+
+        VulkanDispatch* ivk = getGlobalVkEmulation()->ivk;
+        VulkanDispatch* dvk = getGlobalVkEmulation()->dvk;
+        for (const auto& imageIte : mImageInfo) {
+            const ImageInfo& imageInfo = imageIte.second;
+            if (imageInfo.memory == VK_NULL_HANDLE) {
+                continue;
+            }
+            const auto& device = imageInfo.device;
+            const auto& deviceInfo = android::base::find(mDeviceInfo, device);
+            const auto physicalDevice = deviceInfo->physicalDevice;
+            const auto& physicalDeviceInfo = android::base::find(mPhysdevInfo, physicalDevice);
+            StateBlock stateBlock{
+                .physicalDevice = physicalDevice,
+                .physicalDeviceInfo = physicalDeviceInfo,
+                .device = device,
+                .queue = VK_NULL_HANDLE,
+                .commandPool = VK_NULL_HANDLE,
+            };
+
+            uint32_t queueFamilyCount = 0;
+            ivk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount,
+                                                          nullptr);
+            std::vector<VkQueueFamilyProperties> queueFamilyProps(queueFamilyCount);
+            ivk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount,
+                                                          queueFamilyProps.data());
+            uint32_t queueFamilyIndex = 0;
+            for (auto queue : deviceInfo->queues) {
+                int idx = queue.first;
+                if ((queueFamilyProps[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0) {
+                    continue;
+                }
+                stateBlock.queue = queue.second[0];
+                queueFamilyIndex = idx;
+                break;
+            }
+
+            VkCommandPoolCreateInfo commandPoolCi = {
+                VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+                0,
+                VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+                queueFamilyIndex,
+            };
+            dvk->vkCreateCommandPool(device, &commandPoolCi, nullptr, &stateBlock.commandPool);
+            // TODO(b/294277842): make sure the queue is empty before using.
+            loadImageContent(stream, &stateBlock, imageIte.first, &imageInfo);
+            dvk->vkDestroyCommandPool(device, stateBlock.commandPool, nullptr);
+        }
+        mSnapshotState = SnapshotState::Normal;
     }
 
     void lock() { mLock.lock(); }
@@ -1424,37 +1571,26 @@
     }
 
     void destroyDeviceLocked(VkDevice device, const VkAllocationCallbacks* pAllocator) {
-        auto deviceInfoIt = mDeviceInfo.find(device);
-        if (deviceInfoIt == mDeviceInfo.end()) return;
-        auto& deviceInfo = deviceInfoIt->second;
+        auto* deviceInfo = android::base::find(mDeviceInfo, device);
+        if (!deviceInfo) return;
 
-        VulkanDispatch* deviceDispatch = dispatch_VkDevice(deviceInfo.boxed);
+        deviceInfo->decompPipelines->clear();
 
-        // Destroy corresponding queues:
-        auto queueIt = mQueueInfo.begin();
-        for (; queueIt != mQueueInfo.end();) {
-            VkQueue queue = queueIt->first;
-            QueueInfo& queueInfo = queueIt->second;
-            if (queueInfo.device == device) {
-                // Ensure all command submissions have completed including submissions generated
-                // by Gfxstream (e.g. wait for the submissions used to signal the VkFences and
-                // VkSemaphores used during vkAcquireImageANDROID() before destructing those sync
-                // objects below).
-                {
-                    AutoLock queueLock(*queueInfo.lock);
-                    deviceDispatch->vkQueueWaitIdle(queue);
-                }
-
-                delete queueInfo.lock;
-                delete_VkQueue(queueInfo.boxed);
-                queueIt = mQueueInfo.erase(queueIt);
+        auto eraseIt = mQueueInfo.begin();
+        for (; eraseIt != mQueueInfo.end();) {
+            if (eraseIt->second.device == device) {
+                delete eraseIt->second.lock;
+                delete_VkQueue(eraseIt->second.boxed);
+                eraseIt = mQueueInfo.erase(eraseIt);
             } else {
-                ++queueIt;
+                ++eraseIt;
             }
         }
 
+        VulkanDispatch* deviceDispatch = dispatch_VkDevice(deviceInfo->boxed);
+
         // Destroy pooled external fences
-        auto deviceFences = deviceInfo.externalFencePool->popAll();
+        auto deviceFences = deviceInfo->externalFencePool->popAll();
         for (auto fence : deviceFences) {
             deviceDispatch->vkDestroyFence(device, fence, pAllocator);
             mFenceInfo.erase(fence);
@@ -1465,18 +1601,10 @@
             mFenceInfo.erase(fence);
         }
 
-        for (auto fence : deviceInfo.deferredDestructionFences) {
-            deviceDispatch->vkDestroyFence(device, fence, pAllocator);
-        }
-
-        for (auto semaphore : deviceInfo.deferredDestructionSemaphores) {
-            deviceDispatch->vkDestroySemaphore(device, semaphore, pAllocator);
-        }
-
         // Run the underlying API call.
         m_vk->vkDestroyDevice(device, pAllocator);
 
-        delete_VkDevice(deviceInfo.boxed);
+        delete_VkDevice(deviceInfo->boxed);
     }
 
     void on_vkDestroyDevice(android::base::BumpPool* pool, VkDevice boxed_device,
@@ -1522,12 +1650,21 @@
         mBufferInfo.erase(buffer);
     }
 
-    void setBufferMemoryBindInfoLocked(VkBuffer buffer, VkDeviceMemory memory,
+    void setBufferMemoryBindInfoLocked(VkDevice device, VkBuffer buffer, VkDeviceMemory memory,
                                        VkDeviceSize memoryOffset) {
         auto* bufferInfo = android::base::find(mBufferInfo, buffer);
         if (!bufferInfo) return;
         bufferInfo->memory = memory;
         bufferInfo->memoryOffset = memoryOffset;
+
+        auto* memoryInfo = android::base::find(mMemoryInfo, memory);
+        if (memoryInfo && memoryInfo->boundBuffer) {
+            auto* deviceInfo = android::base::find(mDeviceInfo, device);
+            if (deviceInfo) {
+                deviceInfo->debugUtilsHelper.addDebugLabel(buffer, "Buffer:%d",
+                                                           *memoryInfo->boundBuffer);
+            }
+        }
     }
 
     VkResult on_vkBindBufferMemory(android::base::BumpPool* pool, VkDevice boxed_device,
@@ -1541,7 +1678,7 @@
 
         if (result == VK_SUCCESS) {
             std::lock_guard<std::recursive_mutex> lock(mLock);
-            setBufferMemoryBindInfoLocked(buffer, memory, memoryOffset);
+            setBufferMemoryBindInfoLocked(device, buffer, memory, memoryOffset);
         }
         return result;
     }
@@ -1560,7 +1697,7 @@
         if (result == VK_SUCCESS) {
             std::lock_guard<std::recursive_mutex> lock(mLock);
             for (uint32_t i = 0; i < bindInfoCount; ++i) {
-                setBufferMemoryBindInfoLocked(pBindInfos[i].buffer, pBindInfos[i].memory,
+                setBufferMemoryBindInfoLocked(device, pBindInfos[i].buffer, pBindInfos[i].memory,
                                               pBindInfos[i].memoryOffset);
             }
         }
@@ -1582,7 +1719,7 @@
         if (result == VK_SUCCESS) {
             std::lock_guard<std::recursive_mutex> lock(mLock);
             for (uint32_t i = 0; i < bindInfoCount; ++i) {
-                setBufferMemoryBindInfoLocked(pBindInfos[i].buffer, pBindInfos[i].memory,
+                setBufferMemoryBindInfoLocked(device, pBindInfos[i].buffer, pBindInfos[i].memory,
                                               pBindInfos[i].memoryOffset);
             }
         }
@@ -1655,6 +1792,7 @@
         imageInfo.device = device;
         imageInfo.cmpInfo = std::move(cmpInfo);
         imageInfo.imageCreateInfoShallow = vk_make_orphan_copy(*pCreateInfo);
+        imageInfo.layout = pCreateInfo->initialLayout;
         if (nativeBufferANDROID) imageInfo.anbInfo = std::move(anbInfo);
 
         *pImage = new_boxed_non_dispatchable_VkImage(*pImage);
@@ -1760,11 +1898,6 @@
         auto* memoryInfo = android::base::find(mMemoryInfo, memory);
         if (!memoryInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-        auto* imageInfo = android::base::find(mImageInfo, image);
-        if (imageInfo) {
-            imageInfo->boundColorBuffer = memoryInfo->boundColorBuffer;
-        }
-
 #if defined(__APPLE__) && defined(VK_MVK_moltenvk)
         if (memoryInfo->mtlTexture) {
             result = m_vk->vkSetMTLTextureMVK(image, memoryInfo->mtlTexture);
@@ -1774,12 +1907,20 @@
             }
         }
 #endif
+
+        auto* imageInfo = android::base::find(mImageInfo, image);
+        if (!imageInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
+        imageInfo->boundColorBuffer = memoryInfo->boundColorBuffer;
+        if (imageInfo->boundColorBuffer) {
+            deviceInfo->debugUtilsHelper.addDebugLabel(image, "ColorBuffer:%d",
+                                                       *imageInfo->boundColorBuffer);
+        }
+        imageInfo->memory = memory;
+
         if (!deviceInfo->emulateTextureEtc2 && !deviceInfo->emulateTextureAstc) {
             return VK_SUCCESS;
         }
 
-        if (!imageInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
-
         CompressedImageInfo& cmpInfo = imageInfo->cmpInfo;
         if (!deviceInfo->needEmulatedDecompression(cmpInfo)) {
             return VK_SUCCESS;
@@ -1835,7 +1976,25 @@
             return VK_SUCCESS;
         }
 
-        return vk->vkBindImageMemory2(device, bindInfoCount, pBindInfos);
+        VkResult result = vk->vkBindImageMemory2(device, bindInfoCount, pBindInfos);
+        if (result != VK_SUCCESS) {
+            return result;
+        }
+
+        if (deviceInfo->debugUtilsHelper.isEnabled()) {
+            std::lock_guard<std::recursive_mutex> lock(mLock);
+            for (uint32_t i = 0; i < bindInfoCount; i++) {
+                auto* memoryInfo = android::base::find(mMemoryInfo, pBindInfos[i].memory);
+                if (!memoryInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+                if (memoryInfo->boundColorBuffer) {
+                    deviceInfo->debugUtilsHelper.addDebugLabel(
+                        pBindInfos[i].image, "ColorBuffer:%d", *memoryInfo->boundColorBuffer);
+                }
+            }
+        }
+
+        return result;
     }
 
     VkResult on_vkCreateImageView(android::base::BumpPool* pool, VkDevice boxed_device,
@@ -1887,6 +2046,10 @@
         imageViewInfo.device = device;
         imageViewInfo.needEmulatedAlpha = needEmulatedAlpha;
         imageViewInfo.boundColorBuffer = imageInfo->boundColorBuffer;
+        if (imageViewInfo.boundColorBuffer) {
+            deviceInfo->debugUtilsHelper.addDebugLabel(*pView, "ColorBuffer:%d",
+                                                       *imageViewInfo.boundColorBuffer);
+        }
 
         *pView = new_boxed_non_dispatchable_VkImageView(*pView);
 
@@ -2210,27 +2373,16 @@
 
     void destroySemaphoreLocked(VkDevice device, VulkanDispatch* deviceDispatch,
                                 VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator) {
-        auto semaphoreInfoIt = mSemaphoreInfo.find(semaphore);
-        if (semaphoreInfoIt == mSemaphoreInfo.end()) return;
-        auto& semaphoreInfo = semaphoreInfoIt->second;
-
 #ifndef _WIN32
-        if (semaphoreInfo.externalHandle != VK_EXT_SYNC_HANDLE_INVALID) {
-            close(semaphoreInfo.externalHandle);
+        const auto& ite = mSemaphoreInfo.find(semaphore);
+        if (ite != mSemaphoreInfo.end() &&
+            (ite->second.externalHandle != VK_EXT_SYNC_HANDLE_INVALID)) {
+            close(ite->second.externalHandle);
         }
 #endif
-        if (semaphoreInfo.usedForAcquireImage) {
-            // See comment near DeviceInfo::deferredDestructionSemaphores.
-            auto deviceInfoIt = mDeviceInfo.find(device);
-            if (deviceInfoIt != mDeviceInfo.end()) {
-                auto& deviceInfo = deviceInfoIt->second;
-                deviceInfo.deferredDestructionSemaphores.push_back(semaphore);
-            }
-        } else {
-            deviceDispatch->vkDestroySemaphore(device, semaphore, pAllocator);
-        }
+        deviceDispatch->vkDestroySemaphore(device, semaphore, pAllocator);
 
-        mSemaphoreInfo.erase(semaphoreInfoIt);
+        mSemaphoreInfo.erase(semaphore);
     }
 
     void on_vkDestroySemaphore(android::base::BumpPool* pool, VkDevice boxed_device,
@@ -2245,40 +2397,24 @@
     void on_vkDestroyFence(android::base::BumpPool* pool, VkDevice boxed_device, VkFence fence,
                            const VkAllocationCallbacks* pAllocator) {
         auto device = unbox_VkDevice(boxed_device);
-        auto deviceDispatch = dispatch_VkDevice(boxed_device);
+        auto vk = dispatch_VkDevice(boxed_device);
 
         {
             std::lock_guard<std::recursive_mutex> lock(mLock);
-
-            auto fenceInfoIt = mFenceInfo.find(fence);
-            if (fenceInfoIt != mFenceInfo.end()) return;
-            auto& fenceInfo = fenceInfoIt->second;
-
-            auto deviceInfoIt = mDeviceInfo.find(device);
-            if (deviceInfoIt != mDeviceInfo.end()) {
-                auto& deviceInfo = deviceInfoIt->second;
-
-                fenceInfo.boxed = VK_NULL_HANDLE;
-
-                // External fences are just slated for recycling. This addresses known
-                // behavior where the guest might destroy the fence prematurely. b/228221208
-                if (fenceInfo.external) {
-                    deviceInfo.externalFencePool->add(fence);
-                    return;
-                }
-
-                // Fences used for swapchains have their destruction deferred until device
-                // destruction. See comment near DeviceInfo::deferredDestructionFences.
-                if (fenceInfo.usedForAcquireImage) {
-                    deviceInfo.deferredDestructionFences.push_back(fence);
+            // External fences are just slated for recycling. This addresses known
+            // behavior where the guest might destroy the fence prematurely. b/228221208
+            if (mFenceInfo[fence].external) {
+                auto* deviceInfo = android::base::find(mDeviceInfo, device);
+                if (deviceInfo) {
+                    deviceInfo->externalFencePool->add(fence);
+                    mFenceInfo[fence].boxed = VK_NULL_HANDLE;
                     return;
                 }
             }
-
             mFenceInfo.erase(fence);
         }
 
-        deviceDispatch->vkDestroyFence(device, fence, pAllocator);
+        vk->vkDestroyFence(device, fence, pAllocator);
     }
 
     VkResult on_vkCreateDescriptorSetLayout(android::base::BumpPool* pool, VkDevice boxed_device,
@@ -2551,7 +2687,7 @@
                     // TODO(igorc): Move this to vkQueueSubmit time.
                     auto fb = FrameBuffer::getFB();
                     if (fb) {
-                        fb->invalidateColorBufferForVk(imgViewInfo->boundColorBuffer);
+                        fb->invalidateColorBufferForVk(*imgViewInfo->boundColorBuffer);
                     }
                 }
                 if (descriptorWrite.descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
@@ -3291,6 +3427,7 @@
         DeviceInfo* deviceInfo = android::base::find(mDeviceInfo, cmdBufferInfo->device);
         if (!deviceInfo) return;
 
+        // TODO: update image layout in ImageInfo
         if (!deviceInfo->emulateTextureEtc2 && !deviceInfo->emulateTextureAstc) {
             vk->vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags,
                                      memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount,
@@ -3684,8 +3821,11 @@
         if (createBlobInfoPtr && createBlobInfoPtr->blobMem == STREAM_BLOB_MEM_GUEST &&
             (createBlobInfoPtr->blobFlags & STREAM_BLOB_FLAG_CREATE_GUEST_HANDLE)) {
             DescriptorType rawDescriptor;
+            uint32_t ctx_id = mSnapshotState == SnapshotState::Loading
+                                  ? kTemporaryContextIdForSnapshotLoading
+                                  : tInfo->ctx_id;
             auto descriptorInfoOpt =
-                BlobManager::get()->removeDescriptorInfo(tInfo->ctx_id, createBlobInfoPtr->blobId);
+                BlobManager::get()->removeDescriptorInfo(ctx_id, createBlobInfoPtr->blobId);
             if (descriptorInfoOpt) {
                 auto rawDescriptorOpt = (*descriptorInfoOpt).descriptor.release();
                 if (rawDescriptorOpt) {
@@ -4052,21 +4192,6 @@
             return VK_ERROR_INITIALIZATION_FAILED;
         }
 
-        if (semaphore != VK_NULL_HANDLE) {
-            auto semaphoreInfo = android::base::find(mSemaphoreInfo, semaphore);
-            if (!semaphoreInfo) {
-                return VK_ERROR_INITIALIZATION_FAILED;
-            }
-            semaphoreInfo->usedForAcquireImage = true;
-        }
-        if (fence != VK_NULL_HANDLE) {
-            auto fenceInfo = android::base::find(mFenceInfo, fence);
-            if (!fenceInfo) {
-                return VK_ERROR_INITIALIZATION_FAILED;
-            }
-            fenceInfo->usedForAcquireImage = true;
-        }
-
         AndroidNativeBufferInfo* anbInfo = imageInfo->anbInfo.get();
 
         return setAndroidNativeImageSemaphoreSignaled(vk, device, defaultQueue,
@@ -4144,6 +4269,9 @@
     VkResult vkGetBlobInternal(VkDevice boxed_device, VkDeviceMemory memory, uint64_t hostBlobId) {
         std::lock_guard<std::recursive_mutex> lock(mLock);
         auto* tInfo = RenderThreadInfoVk::get();
+        uint32_t ctx_id = mSnapshotState == SnapshotState::Loading
+                              ? kTemporaryContextIdForSnapshotLoading
+                              : tInfo->ctx_id;
 
         auto* info = android::base::find(mMemoryInfo, memory);
         if (!info) return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -4155,7 +4283,7 @@
             // We transfer ownership of the shared memory handle to the descriptor info.
             // The memory itself is destroyed only when all processes unmap / release their
             // handles.
-            BlobManager::get()->addDescriptorInfo(tInfo->ctx_id, hostBlobId,
+            BlobManager::get()->addDescriptorInfo(ctx_id, hostBlobId,
                                                   info->sharedMemory->releaseHandle(), handleType,
                                                   info->caching, std::nullopt);
         } else if (feature_is_enabled(kFeature_ExternalBlob)) {
@@ -4213,13 +4341,12 @@
 #endif
 
             ManagedDescriptor managedHandle(handle);
-            BlobManager::get()->addDescriptorInfo(
-                tInfo->ctx_id, hostBlobId, std::move(managedHandle), handleType, info->caching,
-                std::optional<VulkanInfo>(vulkanInfo));
+            BlobManager::get()->addDescriptorInfo(ctx_id, hostBlobId, std::move(managedHandle),
+                                                  handleType, info->caching,
+                                                  std::optional<VulkanInfo>(vulkanInfo));
         } else if (!info->needUnmap) {
             auto device = unbox_VkDevice(boxed_device);
             auto vk = dispatch_VkDevice(boxed_device);
-
             VkResult mapResult = vk->vkMapMemory(device, memory, 0, info->size, 0, &info->ptr);
             if (mapResult != VK_SUCCESS) {
                 return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -4243,8 +4370,7 @@
                     "using this blob may be corrupted/offset.",
                     kPageSizeforBlob, hva, alignedHva);
             }
-
-            BlobManager::get()->addMapping(tInfo->ctx_id, hostBlobId, (void*)(uintptr_t)alignedHva,
+            BlobManager::get()->addMapping(ctx_id, hostBlobId, (void*)(uintptr_t)alignedHva,
                                            info->caching);
             info->virtioGpuMapped = true;
             info->hostmemId = hostBlobId;
@@ -5624,48 +5750,48 @@
                 ERR("The VkImageCreateInfo to import %s contains unsupported VkImageCreateFlags. "
                     "All supported VkImageCreateFlags are %s, the input VkImageCreateInfo requires "
                     "support for %s.",
-                    importSource.c_str(),
-                    string_VkImageCreateFlags(colorBufferVkImageCi->flags).c_str(),
-                    string_VkImageCreateFlags(imageCreateInfo.flags).c_str());
+                    importSource.c_str()?:"",
+                    string_VkImageCreateFlags(colorBufferVkImageCi->flags).c_str()?:"",
+                    string_VkImageCreateFlags(imageCreateInfo.flags).c_str()?:"");
             }
             imageCreateInfo.flags |= colorBufferVkImageCi->flags;
             if (imageCreateInfo.imageType != colorBufferVkImageCi->imageType) {
                 ERR("The VkImageCreateInfo to import %s has an unexpected VkImageType: %s, %s "
                     "expected.",
-                    importSource.c_str(), string_VkImageType(imageCreateInfo.imageType),
+                    importSource.c_str()?:"", string_VkImageType(imageCreateInfo.imageType),
                     string_VkImageType(colorBufferVkImageCi->imageType));
             }
             if (imageCreateInfo.extent.depth != colorBufferVkImageCi->extent.depth) {
                 ERR("The VkImageCreateInfo to import %s has an unexpected VkExtent::depth: %" PRIu32
                     ", %" PRIu32 " expected.",
-                    importSource.c_str(), imageCreateInfo.extent.depth,
+                    importSource.c_str()?:"", imageCreateInfo.extent.depth,
                     colorBufferVkImageCi->extent.depth);
             }
             if (imageCreateInfo.mipLevels != colorBufferVkImageCi->mipLevels) {
                 ERR("The VkImageCreateInfo to import %s has an unexpected mipLevels: %" PRIu32
                     ", %" PRIu32 " expected.",
-                    importSource.c_str(), imageCreateInfo.mipLevels,
+                    importSource.c_str()?:"", imageCreateInfo.mipLevels,
                     colorBufferVkImageCi->mipLevels);
             }
             if (imageCreateInfo.arrayLayers != colorBufferVkImageCi->arrayLayers) {
                 ERR("The VkImageCreateInfo to import %s has an unexpected arrayLayers: %" PRIu32
                     ", %" PRIu32 " expected.",
-                    importSource.c_str(), imageCreateInfo.arrayLayers,
+                    importSource.c_str()?:"", imageCreateInfo.arrayLayers,
                     colorBufferVkImageCi->arrayLayers);
             }
             if (imageCreateInfo.samples != colorBufferVkImageCi->samples) {
                 ERR("The VkImageCreateInfo to import %s has an unexpected VkSampleCountFlagBits: "
                     "%s, %s expected.",
-                    importSource.c_str(), string_VkSampleCountFlagBits(imageCreateInfo.samples),
+                    importSource.c_str()?:"", string_VkSampleCountFlagBits(imageCreateInfo.samples),
                     string_VkSampleCountFlagBits(colorBufferVkImageCi->samples));
             }
             if (imageCreateInfo.usage & (~colorBufferVkImageCi->usage)) {
                 ERR("The VkImageCreateInfo to import %s contains unsupported VkImageUsageFlags. "
                     "All supported VkImageUsageFlags are %s, the input VkImageCreateInfo requires "
                     "support for %s.",
-                    importSource.c_str(),
-                    string_VkImageUsageFlags(colorBufferVkImageCi->usage).c_str(),
-                    string_VkImageUsageFlags(imageCreateInfo.usage).c_str());
+                    importSource.c_str()?:"",
+                    string_VkImageUsageFlags(colorBufferVkImageCi->usage).c_str()?:"",
+                    string_VkImageUsageFlags(imageCreateInfo.usage).c_str()?:"");
             }
             imageCreateInfo.usage |= colorBufferVkImageCi->usage;
             // For the AndroidHardwareBuffer binding case VkImageCreateInfo::sharingMode isn't
@@ -5677,31 +5803,31 @@
             if (resolvedFormat != colorBufferVkImageCi->format) {
                 ERR("The VkImageCreateInfo to import %s contains unexpected VkFormat: %s. %s "
                     "expected.",
-                    importSource.c_str(), string_VkFormat(imageCreateInfo.format),
+                    importSource.c_str()?:"", string_VkFormat(imageCreateInfo.format),
                     string_VkFormat(colorBufferVkImageCi->format));
             }
             if (imageCreateInfo.extent.width != colorBufferVkImageCi->extent.width) {
                 ERR("The VkImageCreateInfo to import %s contains unexpected VkExtent::width: "
                     "%" PRIu32 ". %" PRIu32 " expected.",
-                    importSource.c_str(), imageCreateInfo.extent.width,
+                    importSource.c_str()?:"", imageCreateInfo.extent.width,
                     colorBufferVkImageCi->extent.width);
             }
             if (imageCreateInfo.extent.height != colorBufferVkImageCi->extent.height) {
                 ERR("The VkImageCreateInfo to import %s contains unexpected VkExtent::height: "
                     "%" PRIu32 ". %" PRIu32 " expected.",
-                    importSource.c_str(), imageCreateInfo.extent.height,
+                    importSource.c_str()?:"", imageCreateInfo.extent.height,
                     colorBufferVkImageCi->extent.height);
             }
             if (imageCreateInfo.tiling != colorBufferVkImageCi->tiling) {
                 ERR("The VkImageCreateInfo to import %s contains unexpected VkImageTiling: %s. %s "
                     "expected.",
-                    importSource.c_str(), string_VkImageTiling(imageCreateInfo.tiling),
+                    importSource.c_str()?:"", string_VkImageTiling(imageCreateInfo.tiling),
                     string_VkImageTiling(colorBufferVkImageCi->tiling));
             }
             if (imageCreateInfo.sharingMode != colorBufferVkImageCi->sharingMode) {
                 ERR("The VkImageCreateInfo to import %s contains unexpected VkSharingMode: %s. %s "
                     "expected.",
-                    importSource.c_str(), string_VkSharingMode(imageCreateInfo.sharingMode),
+                    importSource.c_str()?:"", string_VkSharingMode(imageCreateInfo.sharingMode),
                     string_VkSharingMode(colorBufferVkImageCi->sharingMode));
             }
         }
@@ -6541,229 +6667,6 @@
 
     std::recursive_mutex mLock;
 
-    // We always map the whole size on host.
-    // This makes it much easier to implement
-    // the memory map API.
-    struct MemoryInfo {
-        // This indicates whether the VkDecoderGlobalState needs to clean up
-        // and unmap the mapped memory; only the owner of the mapped memory
-        // should call unmap.
-        bool needUnmap = false;
-        // When ptr is null, it means the VkDeviceMemory object
-        // was not allocated with the HOST_VISIBLE property.
-        void* ptr = nullptr;
-        VkDeviceSize size;
-        // GLDirectMem info
-        bool directMapped = false;
-        bool virtioGpuMapped = false;
-        uint32_t caching = 0;
-        uint64_t guestPhysAddr = 0;
-        void* pageAlignedHva = nullptr;
-        uint64_t sizeToPage = 0;
-        uint64_t hostmemId = 0;
-        VkDevice device = VK_NULL_HANDLE;
-        MTLTextureRef mtlTexture = nullptr;
-        uint32_t memoryIndex = 0;
-        // Set if the memory is backed by shared memory.
-        std::optional<SharedMemory> sharedMemory;
-
-        // virtio-gpu blobs
-        uint64_t blobId = 0;
-
-        // Color buffer, provided via vkAllocateMemory().
-        HandleType boundColorBuffer = 0;
-    };
-
-    struct InstanceInfo {
-        std::vector<std::string> enabledExtensionNames;
-        uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0);
-        VkInstance boxed = nullptr;
-        bool isAngle = false;
-    };
-
-    struct PhysicalDeviceInfo {
-        VkPhysicalDeviceProperties props;
-        VkPhysicalDeviceMemoryProperties memoryProperties;
-        std::vector<VkQueueFamilyProperties> queueFamilyProperties;
-        VkPhysicalDevice boxed = nullptr;
-    };
-
-    struct DeviceInfo {
-        std::unordered_map<uint32_t, std::vector<VkQueue>> queues;
-        std::vector<std::string> enabledExtensionNames;
-        bool emulateTextureEtc2 = false;
-        bool emulateTextureAstc = false;
-        bool useAstcCpuDecompression = false;
-        VkPhysicalDevice physicalDevice;
-        VkDevice boxed = nullptr;
-        DebugUtilsHelper debugUtilsHelper = DebugUtilsHelper::withUtilsDisabled();
-        std::unique_ptr<ExternalFencePool<VulkanDispatch>> externalFencePool = nullptr;
-        std::set<VkFormat> imageFormats = {};  // image formats used on this device
-        std::unique_ptr<GpuDecompressionPipelineManager> decompPipelines = nullptr;
-        // The host needs to ensure that any additional vkQueueSubmitKHR() used to
-        // signal the semaphore and/or fence used in vkAcquireNextImageKHR() or
-        // vkAcquireImageANDROID() have completed before destroying those semaphores
-        // and fences as the guest is not aware of those additional submits. For simplicity,
-        // defer the destruction of those semaphores and fences until device destruction,
-        // with the assumption that the number of semaphores and fences used for
-        // vkAcquireNextImageKHR() or vkAcquireImageANDROID() will be small as swapchain
-        // implementations typically recycle and re-use semaphores and fences, instead of
-        // trying to create additional sync objects to track when the semaphores and fences
-        // can be safely destroyed.
-        std::vector<VkSemaphore> deferredDestructionSemaphores;
-        std::vector<VkFence> deferredDestructionFences;
-
-        // True if this is a compressed image that needs to be decompressed on the GPU (with our
-        // compute shader)
-        bool needGpuDecompression(const CompressedImageInfo& cmpInfo) {
-            return ((cmpInfo.isEtc2() && emulateTextureEtc2) ||
-                    (cmpInfo.isAstc() && emulateTextureAstc && !useAstcCpuDecompression));
-        }
-        bool needEmulatedDecompression(const CompressedImageInfo& cmpInfo) {
-            return ((cmpInfo.isEtc2() && emulateTextureEtc2) ||
-                    (cmpInfo.isAstc() && emulateTextureAstc));
-        }
-        bool needEmulatedDecompression(VkFormat format) {
-            return (gfxstream::vk::isEtc2(format) && emulateTextureEtc2) || (gfxstream::vk::isAstc(format) && emulateTextureAstc);
-        }
-    };
-
-    struct QueueInfo {
-        Lock* lock = nullptr;
-        VkDevice device;
-        uint32_t queueFamilyIndex;
-        VkQueue boxed = nullptr;
-        uint32_t sequenceNumber = 0;
-    };
-
-    struct BufferInfo {
-        VkDevice device;
-        VkDeviceMemory memory = 0;
-        VkDeviceSize memoryOffset = 0;
-        VkDeviceSize size;
-    };
-
-    struct ImageInfo {
-        VkDevice device;
-        VkImageCreateInfo imageCreateInfoShallow;
-        std::shared_ptr<AndroidNativeBufferInfo> anbInfo;
-        CompressedImageInfo cmpInfo;
-
-        // Color buffer, provided via vkAllocateMemory().
-        HandleType boundColorBuffer = 0;
-    };
-
-    struct ImageViewInfo {
-        VkDevice device;
-        bool needEmulatedAlpha = false;
-
-        // Color buffer, provided via vkAllocateMemory().
-        HandleType boundColorBuffer = 0;
-    };
-
-    struct SamplerInfo {
-        VkDevice device;
-        bool needEmulatedAlpha = false;
-        VkSamplerCreateInfo createInfo = {};
-        VkSampler emulatedborderSampler = VK_NULL_HANDLE;
-        android::base::BumpPool pool = android::base::BumpPool(256);
-        SamplerInfo() = default;
-        SamplerInfo& operator=(const SamplerInfo& other) {
-            deepcopy_VkSamplerCreateInfo(&pool, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
-                                         &other.createInfo, &createInfo);
-            device = other.device;
-            needEmulatedAlpha = other.needEmulatedAlpha;
-            emulatedborderSampler = other.emulatedborderSampler;
-            return *this;
-        }
-        SamplerInfo(const SamplerInfo& other) { *this = other; }
-        SamplerInfo(SamplerInfo&& other) = delete;
-        SamplerInfo& operator=(SamplerInfo&& other) = delete;
-    };
-
-    struct FenceInfo {
-        VkDevice device = VK_NULL_HANDLE;
-        VkFence boxed = VK_NULL_HANDLE;
-        VulkanDispatch* vk = nullptr;
-
-        StaticLock lock;
-        android::base::ConditionVariable cv;
-
-        enum class State {
-            kWaitable,
-            kNotWaitable,
-            kWaiting,
-        };
-        State state = State::kNotWaitable;
-
-        bool external = false;
-
-        // If this fence was used for vkAcquireNextImageKHR() / vkAcquireImageANDROID(),
-        // then a vkDeviceWaitIdle() is needed before destruction to ensure any queue
-        // submits used to signal this semaphore have completed before destructing.
-        bool usedForAcquireImage = false;
-    };
-
-    struct SemaphoreInfo {
-        VkDevice device;
-        int externalHandleId = 0;
-        VK_EXT_SYNC_HANDLE externalHandle = VK_EXT_SYNC_HANDLE_INVALID;
-        // If this semaphore was used for vkAcquireNextImageKHR() / vkAcquireImageANDROID(),
-        // then a vkDeviceWaitIdle() is needed before destruction to ensure any queue
-        // submits used to signal this semaphore have completed before destructing.
-        bool usedForAcquireImage = false;
-    };
-
-    struct DescriptorSetLayoutInfo {
-        VkDevice device = 0;
-        VkDescriptorSetLayout boxed = 0;
-        VkDescriptorSetLayoutCreateInfo createInfo;
-        std::vector<VkDescriptorSetLayoutBinding> bindings;
-    };
-
-    struct DescriptorPoolInfo {
-        VkDevice device = 0;
-        VkDescriptorPool boxed = 0;
-        struct PoolState {
-            VkDescriptorType type;
-            uint32_t descriptorCount;
-            uint32_t used;
-        };
-
-        VkDescriptorPoolCreateInfo createInfo;
-        uint32_t maxSets;
-        uint32_t usedSets;
-        std::vector<PoolState> pools;
-
-        std::unordered_map<VkDescriptorSet, VkDescriptorSet> allocedSetsToBoxed;
-        std::vector<uint64_t> poolIds;
-    };
-
-    struct DescriptorSetInfo {
-        VkDescriptorPool pool;
-        std::vector<VkDescriptorSetLayoutBinding> bindings;
-    };
-
-    struct ShaderModuleInfo {
-        VkDevice device;
-    };
-
-    struct PipelineCacheInfo {
-        VkDevice device;
-    };
-
-    struct PipelineInfo {
-        VkDevice device;
-    };
-
-    struct RenderPassInfo {
-        VkDevice device;
-    };
-
-    struct FramebufferInfo {
-        VkDevice device;
-    };
-
     bool isBindingFeasibleForAlloc(const DescriptorPoolInfo::PoolState& poolState,
                                    const VkDescriptorSetLayoutBinding& binding) {
         if (binding.descriptorCount && (poolState.type != binding.descriptorType)) {
@@ -6984,6 +6887,13 @@
 
     std::unordered_map<LinearImageCreateInfo, LinearImageProperties, LinearImageCreateInfo::Hash>
         mLinearImageProperties;
+
+    enum SnapshotState {
+        Normal,
+        Saving,
+        Loading,
+    };
+    SnapshotState mSnapshotState = SnapshotState::Normal;
 };
 
 VkDecoderGlobalState::VkDecoderGlobalState() : mImpl(new VkDecoderGlobalState::Impl()) {}
diff --git a/host/vulkan/VkDecoderGlobalState.h b/host/vulkan/VkDecoderGlobalState.h
index 4b2d0dc..b7d02da 100644
--- a/host/vulkan/VkDecoderGlobalState.h
+++ b/host/vulkan/VkDecoderGlobalState.h
@@ -922,74 +922,5 @@
     uint64_t** mPreserveBufPtr = nullptr;
 };
 
-template <class TDispatch>
-class ExternalFencePool {
-   public:
-    ExternalFencePool(TDispatch* dispatch, VkDevice device)
-        : m_vk(dispatch), mDevice(device), mMaxSize(5) {}
-
-    ~ExternalFencePool() {
-        if (!mPool.empty()) {
-            GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
-                << "External fence pool for device " << static_cast<void*>(mDevice)
-                << " destroyed but " << mPool.size() << " fences still not destroyed.";
-        }
-    }
-
-    void add(VkFence fence) {
-        AutoLock lock(mLock);
-        mPool.push_back(fence);
-        if (mPool.size() > mMaxSize) {
-            INFO("External fence pool for %p has increased to size %d", mDevice, mPool.size());
-            mMaxSize = mPool.size();
-        }
-    }
-
-    VkFence pop(const VkFenceCreateInfo* pCreateInfo) {
-        VkFence fence = VK_NULL_HANDLE;
-        {
-            AutoLock lock(mLock);
-            auto it = std::find_if(mPool.begin(), mPool.end(), [this](const VkFence& fence) {
-                VkResult status = m_vk->vkGetFenceStatus(mDevice, fence);
-                if (status != VK_SUCCESS) {
-                    if (status != VK_NOT_READY) {
-                        VK_CHECK(status);
-                    }
-
-                    // Status is valid, but fence is not yet signaled
-                    return false;
-                }
-                return true;
-            });
-            if (it == mPool.end()) {
-                return VK_NULL_HANDLE;
-            }
-
-            fence = *it;
-            mPool.erase(it);
-        }
-
-        if (!(pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT)) {
-            VK_CHECK(m_vk->vkResetFences(mDevice, 1, &fence));
-        }
-
-        return fence;
-    }
-
-    std::vector<VkFence> popAll() {
-        AutoLock lock(mLock);
-        std::vector<VkFence> popped = mPool;
-        mPool.clear();
-        return popped;
-    }
-
-   private:
-    TDispatch* m_vk;
-    VkDevice mDevice;
-    Lock mLock;
-    std::vector<VkFence> mPool;
-    int mMaxSize;
-};
-
 }  // namespace vk
 }  // namespace gfxstream
diff --git a/host/vulkan/VkDecoderInternalStructs.h b/host/vulkan/VkDecoderInternalStructs.h
new file mode 100644
index 0000000..6a90923
--- /dev/null
+++ b/host/vulkan/VkDecoderInternalStructs.h
@@ -0,0 +1,310 @@
+// Copyright 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+#include <set>
+#include <string>
+
+#include "Handle.h"
+#include "aemu/base/files/Stream.h"
+#include "aemu/base/memory/SharedMemory.h"
+#include "aemu/base/synchronization/ConditionVariable.h"
+#include "aemu/base/synchronization/Lock.h"
+#include "common/goldfish_vk_deepcopy.h"
+#include "vulkan/VkAndroidNativeBuffer.h"
+#include "vulkan/VkFormatUtils.h"
+#include "vulkan/emulated_textures/CompressedImageInfo.h"
+
+namespace gfxstream {
+namespace vk {
+template <class TDispatch>
+class ExternalFencePool {
+   public:
+    ExternalFencePool(TDispatch* dispatch, VkDevice device)
+        : m_vk(dispatch), mDevice(device), mMaxSize(5) {}
+
+    ~ExternalFencePool() {
+        if (!mPool.empty()) {
+            GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))
+                << "External fence pool for device " << static_cast<void*>(mDevice)
+                << " destroyed but " << mPool.size() << " fences still not destroyed.";
+        }
+    }
+
+    void add(VkFence fence) {
+        android::base::AutoLock lock(mLock);
+        mPool.push_back(fence);
+        if (mPool.size() > mMaxSize) {
+            INFO("External fence pool for %p has increased to size %d", mDevice, mPool.size());
+            mMaxSize = mPool.size();
+        }
+    }
+
+    VkFence pop(const VkFenceCreateInfo* pCreateInfo) {
+        VkFence fence = VK_NULL_HANDLE;
+        {
+            android::base::AutoLock lock(mLock);
+            auto it = std::find_if(mPool.begin(), mPool.end(), [this](const VkFence& fence) {
+                VkResult status = m_vk->vkGetFenceStatus(mDevice, fence);
+                if (status != VK_SUCCESS) {
+                    if (status != VK_NOT_READY) {
+                        VK_CHECK(status);
+                    }
+
+                    // Status is valid, but fence is not yet signaled
+                    return false;
+                }
+                return true;
+            });
+            if (it == mPool.end()) {
+                return VK_NULL_HANDLE;
+            }
+
+            fence = *it;
+            mPool.erase(it);
+        }
+
+        if (!(pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT)) {
+            VK_CHECK(m_vk->vkResetFences(mDevice, 1, &fence));
+        }
+
+        return fence;
+    }
+
+    std::vector<VkFence> popAll() {
+        android::base::AutoLock lock(mLock);
+        std::vector<VkFence> popped = mPool;
+        mPool.clear();
+        return popped;
+    }
+
+   private:
+    TDispatch* m_vk;
+    VkDevice mDevice;
+    android::base::Lock mLock;
+    std::vector<VkFence> mPool;
+    int mMaxSize;
+};
+
+// We always map the whole size on host.
+// This makes it much easier to implement
+// the memory map API.
+struct MemoryInfo {
+    // This indicates whether the VkDecoderGlobalState needs to clean up
+    // and unmap the mapped memory; only the owner of the mapped memory
+    // should call unmap.
+    bool needUnmap = false;
+    // When ptr is null, it means the VkDeviceMemory object
+    // was not allocated with the HOST_VISIBLE property.
+    void* ptr = nullptr;
+    VkDeviceSize size;
+    // GLDirectMem info
+    bool directMapped = false;
+    bool virtioGpuMapped = false;
+    uint32_t caching = 0;
+    uint64_t guestPhysAddr = 0;
+    void* pageAlignedHva = nullptr;
+    uint64_t sizeToPage = 0;
+    uint64_t hostmemId = 0;
+    VkDevice device = VK_NULL_HANDLE;
+    MTLTextureRef mtlTexture = nullptr;
+    uint32_t memoryIndex = 0;
+    // Set if the memory is backed by shared memory.
+    std::optional<android::base::SharedMemory> sharedMemory;
+
+    // virtio-gpu blobs
+    uint64_t blobId = 0;
+
+    // Buffer, provided via vkAllocateMemory().
+    std::optional<HandleType> boundBuffer;
+    // ColorBuffer, provided via vkAllocateMemory().
+    std::optional<HandleType> boundColorBuffer;
+};
+
+struct InstanceInfo {
+    std::vector<std::string> enabledExtensionNames;
+    uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0);
+    VkInstance boxed = nullptr;
+    bool isAngle = false;
+};
+
+struct PhysicalDeviceInfo {
+    VkPhysicalDeviceProperties props;
+    VkPhysicalDeviceMemoryProperties memoryProperties;
+    std::vector<VkQueueFamilyProperties> queueFamilyProperties;
+    VkPhysicalDevice boxed = nullptr;
+};
+
+struct DeviceInfo {
+    std::unordered_map<uint32_t, std::vector<VkQueue>> queues;
+    std::vector<std::string> enabledExtensionNames;
+    bool emulateTextureEtc2 = false;
+    bool emulateTextureAstc = false;
+    bool useAstcCpuDecompression = false;
+    VkPhysicalDevice physicalDevice;
+    VkDevice boxed = nullptr;
+    DebugUtilsHelper debugUtilsHelper = DebugUtilsHelper::withUtilsDisabled();
+    std::unique_ptr<ExternalFencePool<VulkanDispatch>> externalFencePool = nullptr;
+    std::set<VkFormat> imageFormats = {};  // image formats used on this device
+    std::unique_ptr<GpuDecompressionPipelineManager> decompPipelines = nullptr;
+
+    // True if this is a compressed image that needs to be decompressed on the GPU (with our
+    // compute shader)
+    bool needGpuDecompression(const CompressedImageInfo& cmpInfo) {
+        return ((cmpInfo.isEtc2() && emulateTextureEtc2) ||
+                (cmpInfo.isAstc() && emulateTextureAstc && !useAstcCpuDecompression));
+    }
+    bool needEmulatedDecompression(const CompressedImageInfo& cmpInfo) {
+        return ((cmpInfo.isEtc2() && emulateTextureEtc2) ||
+                (cmpInfo.isAstc() && emulateTextureAstc));
+    }
+    bool needEmulatedDecompression(VkFormat format) {
+        return (gfxstream::vk::isEtc2(format) && emulateTextureEtc2) ||
+               (gfxstream::vk::isAstc(format) && emulateTextureAstc);
+    }
+};
+
+struct QueueInfo {
+    android::base::Lock* lock = nullptr;
+    VkDevice device;
+    uint32_t queueFamilyIndex;
+    VkQueue boxed = nullptr;
+    uint32_t sequenceNumber = 0;
+};
+
+struct BufferInfo {
+    VkDevice device;
+    VkDeviceMemory memory = 0;
+    VkDeviceSize memoryOffset = 0;
+    VkDeviceSize size;
+};
+
+struct ImageInfo {
+    VkDevice device;
+    VkImageCreateInfo imageCreateInfoShallow;
+    std::shared_ptr<AndroidNativeBufferInfo> anbInfo;
+    CompressedImageInfo cmpInfo;
+    // ColorBuffer, provided via vkAllocateMemory().
+    std::optional<HandleType> boundColorBuffer;
+    // TODO: might need to use an array of layouts to represent each sub resource
+    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
+    VkDeviceMemory memory = VK_NULL_HANDLE;
+};
+
+struct ImageViewInfo {
+    VkDevice device;
+    bool needEmulatedAlpha = false;
+
+    // Color buffer, provided via vkAllocateMemory().
+    std::optional<HandleType> boundColorBuffer;
+};
+
+struct SamplerInfo {
+    VkDevice device;
+    bool needEmulatedAlpha = false;
+    VkSamplerCreateInfo createInfo = {};
+    VkSampler emulatedborderSampler = VK_NULL_HANDLE;
+    android::base::BumpPool pool = android::base::BumpPool(256);
+    SamplerInfo() = default;
+    SamplerInfo& operator=(const SamplerInfo& other) {
+        deepcopy_VkSamplerCreateInfo(&pool, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+                                     &other.createInfo, &createInfo);
+        device = other.device;
+        needEmulatedAlpha = other.needEmulatedAlpha;
+        emulatedborderSampler = other.emulatedborderSampler;
+        return *this;
+    }
+    SamplerInfo(const SamplerInfo& other) { *this = other; }
+    SamplerInfo(SamplerInfo&& other) = delete;
+    SamplerInfo& operator=(SamplerInfo&& other) = delete;
+};
+
+struct FenceInfo {
+    VkDevice device = VK_NULL_HANDLE;
+    VkFence boxed = VK_NULL_HANDLE;
+    VulkanDispatch* vk = nullptr;
+
+    android::base::StaticLock lock;
+    android::base::ConditionVariable cv;
+
+    enum class State {
+        kWaitable,
+        kNotWaitable,
+        kWaiting,
+    };
+    State state = State::kNotWaitable;
+
+    bool external = false;
+};
+
+struct SemaphoreInfo {
+    VkDevice device;
+    int externalHandleId = 0;
+    VK_EXT_MEMORY_HANDLE externalHandle = VK_EXT_MEMORY_HANDLE_INVALID;
+};
+
+struct DescriptorSetLayoutInfo {
+    VkDevice device = 0;
+    VkDescriptorSetLayout boxed = 0;
+    VkDescriptorSetLayoutCreateInfo createInfo;
+    std::vector<VkDescriptorSetLayoutBinding> bindings;
+};
+
+struct DescriptorPoolInfo {
+    VkDevice device = 0;
+    VkDescriptorPool boxed = 0;
+    struct PoolState {
+        VkDescriptorType type;
+        uint32_t descriptorCount;
+        uint32_t used;
+    };
+
+    VkDescriptorPoolCreateInfo createInfo;
+    uint32_t maxSets;
+    uint32_t usedSets;
+    std::vector<PoolState> pools;
+
+    std::unordered_map<VkDescriptorSet, VkDescriptorSet> allocedSetsToBoxed;
+    std::vector<uint64_t> poolIds;
+};
+
+struct DescriptorSetInfo {
+    VkDescriptorPool pool;
+    std::vector<VkDescriptorSetLayoutBinding> bindings;
+};
+
+struct ShaderModuleInfo {
+    VkDevice device;
+};
+
+struct PipelineCacheInfo {
+    VkDevice device;
+};
+
+struct PipelineInfo {
+    VkDevice device;
+};
+
+struct RenderPassInfo {
+    VkDevice device;
+};
+
+struct FramebufferInfo {
+    VkDevice device;
+};
+}  // namespace vk
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/host/vulkan/VkDecoderSnapshot.cpp b/host/vulkan/VkDecoderSnapshot.cpp
index dab9099..a51c873 100644
--- a/host/vulkan/VkDecoderSnapshot.cpp
+++ b/host/vulkan/VkDecoderSnapshot.cpp
@@ -57,7 +57,6 @@
                           android::base::BumpPool* pool, VkResult input_result,
                           const VkInstanceCreateInfo* pCreateInfo,
                           const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pInstance create
         mReconstruction.addHandles((const uint64_t*)pInstance, 1);
@@ -72,7 +71,6 @@
     void vkDestroyInstance(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkInstance instance,
                            const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // instance destroy
         mReconstruction.removeHandles((const uint64_t*)(&instance), 1);
@@ -81,7 +79,6 @@
                                     android::base::BumpPool* pool, VkResult input_result,
                                     VkInstance instance, uint32_t* pPhysicalDeviceCount,
                                     VkPhysicalDevice* pPhysicalDevices) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pPhysicalDevices create
         mReconstruction.addHandles((const uint64_t*)pPhysicalDevices, (*(pPhysicalDeviceCount)));
@@ -102,59 +99,43 @@
     }
     void vkGetPhysicalDeviceFeatures(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkPhysicalDevice physicalDevice,
-                                     VkPhysicalDeviceFeatures* pFeatures) {
-        // TODO: Implement
-    }
+                                     VkPhysicalDeviceFeatures* pFeatures) {}
     void vkGetPhysicalDeviceFormatProperties(const uint8_t* snapshotTraceBegin,
                                              size_t snapshotTraceBytes,
                                              android::base::BumpPool* pool,
                                              VkPhysicalDevice physicalDevice, VkFormat format,
-                                             VkFormatProperties* pFormatProperties) {
-        // TODO: Implement
-    }
+                                             VkFormatProperties* pFormatProperties) {}
     void vkGetPhysicalDeviceImageFormatProperties(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type,
         VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags,
-        VkImageFormatProperties* pImageFormatProperties) {
-        // TODO: Implement
-    }
+        VkImageFormatProperties* pImageFormatProperties) {}
     void vkGetPhysicalDeviceProperties(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool,
                                        VkPhysicalDevice physicalDevice,
-                                       VkPhysicalDeviceProperties* pProperties) {
-        // TODO: Implement
-    }
+                                       VkPhysicalDeviceProperties* pProperties) {}
     void vkGetPhysicalDeviceQueueFamilyProperties(const uint8_t* snapshotTraceBegin,
                                                   size_t snapshotTraceBytes,
                                                   android::base::BumpPool* pool,
                                                   VkPhysicalDevice physicalDevice,
                                                   uint32_t* pQueueFamilyPropertyCount,
                                                   VkQueueFamilyProperties* pQueueFamilyProperties) {
-        // TODO: Implement
     }
     void vkGetPhysicalDeviceMemoryProperties(const uint8_t* snapshotTraceBegin,
                                              size_t snapshotTraceBytes,
                                              android::base::BumpPool* pool,
                                              VkPhysicalDevice physicalDevice,
-                                             VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
-        // TODO: Implement
-    }
+                                             VkPhysicalDeviceMemoryProperties* pMemoryProperties) {}
     void vkGetInstanceProcAddr(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, PFN_vkVoidFunction input_result,
-                               VkInstance instance, const char* pName) {
-        // TODO: Implement
-    }
+                               VkInstance instance, const char* pName) {}
     void vkGetDeviceProcAddr(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, PFN_vkVoidFunction input_result,
-                             VkDevice device, const char* pName) {
-        // TODO: Implement
-    }
+                             VkDevice device, const char* pName) {}
     void vkCreateDevice(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkResult input_result,
                         VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo,
                         const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDevice create
         mReconstruction.addHandles((const uint64_t*)pDevice, 1);
@@ -171,7 +152,6 @@
     void vkDestroyDevice(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkDevice device,
                          const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // device destroy
         mReconstruction.removeHandles((const uint64_t*)(&device), 1);
@@ -181,35 +161,26 @@
                                                 android::base::BumpPool* pool,
                                                 VkResult input_result, const char* pLayerName,
                                                 uint32_t* pPropertyCount,
-                                                VkExtensionProperties* pProperties) {
-        // TODO: Implement
-    }
+                                                VkExtensionProperties* pProperties) {}
     void vkEnumerateDeviceExtensionProperties(const uint8_t* snapshotTraceBegin,
                                               size_t snapshotTraceBytes,
                                               android::base::BumpPool* pool, VkResult input_result,
                                               VkPhysicalDevice physicalDevice,
                                               const char* pLayerName, uint32_t* pPropertyCount,
-                                              VkExtensionProperties* pProperties) {
-        // TODO: Implement
-    }
+                                              VkExtensionProperties* pProperties) {}
     void vkEnumerateInstanceLayerProperties(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, VkResult input_result,
                                             uint32_t* pPropertyCount,
-                                            VkLayerProperties* pProperties) {
-        // TODO: Implement
-    }
+                                            VkLayerProperties* pProperties) {}
     void vkEnumerateDeviceLayerProperties(const uint8_t* snapshotTraceBegin,
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkResult input_result, VkPhysicalDevice physicalDevice,
                                           uint32_t* pPropertyCount,
-                                          VkLayerProperties* pProperties) {
-        // TODO: Implement
-    }
+                                          VkLayerProperties* pProperties) {}
     void vkGetDeviceQueue(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkDevice device, uint32_t queueFamilyIndex,
                           uint32_t queueIndex, VkQueue* pQueue) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pQueue create
         mReconstruction.addHandles((const uint64_t*)pQueue, 1);
@@ -225,22 +196,15 @@
     }
     void vkQueueSubmit(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkResult input_result, VkQueue queue,
-                       uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
-        // TODO: Implement
-    }
+                       uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {}
     void vkQueueWaitIdle(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                         android::base::BumpPool* pool, VkResult input_result, VkQueue queue) {
-        // TODO: Implement
-    }
+                         android::base::BumpPool* pool, VkResult input_result, VkQueue queue) {}
     void vkDeviceWaitIdle(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                          android::base::BumpPool* pool, VkResult input_result, VkDevice device) {
-        // TODO: Implement
-    }
+                          android::base::BumpPool* pool, VkResult input_result, VkDevice device) {}
     void vkAllocateMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                           const VkMemoryAllocateInfo* pAllocateInfo,
                           const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pMemory create
         mReconstruction.addHandles((const uint64_t*)pMemory, 1);
@@ -257,7 +221,6 @@
     void vkFreeMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                       android::base::BumpPool* pool, VkDevice device, VkDeviceMemory memory,
                       const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // memory destroy
         mReconstruction.removeHandles((const uint64_t*)(&memory), 1);
@@ -265,75 +228,63 @@
     void vkMapMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                      android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                      VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size,
-                     VkMemoryMapFlags flags, void** ppData) {
-        // TODO: Implement
-    }
+                     VkMemoryMapFlags flags, void** ppData) {}
     void vkUnmapMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                       android::base::BumpPool* pool, VkDevice device, VkDeviceMemory memory) {
-        // TODO: Implement
-    }
+                       android::base::BumpPool* pool, VkDevice device, VkDeviceMemory memory) {}
     void vkFlushMappedMemoryRanges(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkResult input_result,
                                    VkDevice device, uint32_t memoryRangeCount,
-                                   const VkMappedMemoryRange* pMemoryRanges) {
-        // TODO: Implement
-    }
+                                   const VkMappedMemoryRange* pMemoryRanges) {}
     void vkInvalidateMappedMemoryRanges(const uint8_t* snapshotTraceBegin,
                                         size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                         VkResult input_result, VkDevice device,
                                         uint32_t memoryRangeCount,
-                                        const VkMappedMemoryRange* pMemoryRanges) {
-        // TODO: Implement
-    }
+                                        const VkMappedMemoryRange* pMemoryRanges) {}
     void vkGetDeviceMemoryCommitment(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkDevice device,
                                      VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes) {
-        // TODO: Implement
     }
     void vkBindBufferMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                            VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
-        // TODO: Implement
-    }
+                            VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) {}
     void vkBindImageMemory(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                            VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
-        // TODO: Implement
+        android::base::AutoLock lock(mLock);
+        // image modify
+        auto apiHandle = mReconstruction.createApiInfo();
+        auto apiInfo = mReconstruction.getApiInfo(apiHandle);
+        mReconstruction.setApiTrace(apiInfo, OP_vkBindImageMemory, snapshotTraceBegin,
+                                    snapshotTraceBytes);
+        for (uint32_t i = 0; i < 1; ++i) {
+            VkImage boxed = unboxed_to_boxed_non_dispatchable_VkImage((&image)[i]);
+            mReconstruction.forEachHandleAddModifyApi((const uint64_t*)(&boxed), 1, apiHandle);
+        }
     }
     void vkGetBufferMemoryRequirements(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkDevice device,
                                        VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements) {
-        // TODO: Implement
     }
     void vkGetImageMemoryRequirements(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkDevice device, VkImage image,
-                                      VkMemoryRequirements* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                      VkMemoryRequirements* pMemoryRequirements) {}
     void vkGetImageSparseMemoryRequirements(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements* pSparseMemoryRequirements) {
-        // TODO: Implement
-    }
+        VkSparseImageMemoryRequirements* pSparseMemoryRequirements) {}
     void vkGetPhysicalDeviceSparseImageFormatProperties(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type,
         VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling,
-        uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties) {
-        // TODO: Implement
-    }
+        uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties) {}
     void vkQueueBindSparse(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkQueue queue,
                            uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo,
-                           VkFence fence) {
-        // TODO: Implement
-    }
+                           VkFence fence) {}
     void vkCreateFence(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                        const VkFenceCreateInfo* pCreateInfo,
                        const VkAllocationCallbacks* pAllocator, VkFence* pFence) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pFence create
         mReconstruction.addHandles((const uint64_t*)pFence, 1);
@@ -350,32 +301,24 @@
     void vkDestroyFence(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkDevice device, VkFence fence,
                         const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // fence destroy
         mReconstruction.removeHandles((const uint64_t*)(&fence), 1);
     }
     void vkResetFences(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                       uint32_t fenceCount, const VkFence* pFences) {
-        // TODO: Implement
-    }
+                       uint32_t fenceCount, const VkFence* pFences) {}
     void vkGetFenceStatus(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                          VkFence fence) {
-        // TODO: Implement
-    }
+                          VkFence fence) {}
     void vkWaitForFences(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                          uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll,
-                         uint64_t timeout) {
-        // TODO: Implement
-    }
+                         uint64_t timeout) {}
     void vkCreateSemaphore(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                            const VkSemaphoreCreateInfo* pCreateInfo,
                            const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pSemaphore create
         mReconstruction.addHandles((const uint64_t*)pSemaphore, 1);
@@ -392,7 +335,6 @@
     void vkDestroySemaphore(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkDevice device, VkSemaphore semaphore,
                             const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // semaphore destroy
         mReconstruction.removeHandles((const uint64_t*)(&semaphore), 1);
@@ -401,7 +343,6 @@
                        android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                        const VkEventCreateInfo* pCreateInfo,
                        const VkAllocationCallbacks* pAllocator, VkEvent* pEvent) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pEvent create
         mReconstruction.addHandles((const uint64_t*)pEvent, 1);
@@ -418,31 +359,23 @@
     void vkDestroyEvent(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkDevice device, VkEvent event,
                         const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // event destroy
         mReconstruction.removeHandles((const uint64_t*)(&event), 1);
     }
     void vkGetEventStatus(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                          VkEvent event) {
-        // TODO: Implement
-    }
+                          VkEvent event) {}
     void vkSetEvent(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                     android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                    VkEvent event) {
-        // TODO: Implement
-    }
+                    VkEvent event) {}
     void vkResetEvent(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                       android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                      VkEvent event) {
-        // TODO: Implement
-    }
+                      VkEvent event) {}
     void vkCreateQueryPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                            const VkQueryPoolCreateInfo* pCreateInfo,
                            const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pQueryPool create
         mReconstruction.addHandles((const uint64_t*)pQueryPool, 1);
@@ -459,7 +392,6 @@
     void vkDestroyQueryPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkDevice device, VkQueryPool queryPool,
                             const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // queryPool destroy
         mReconstruction.removeHandles((const uint64_t*)(&queryPool), 1);
@@ -468,14 +400,11 @@
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, VkQueryPool queryPool, uint32_t firstQuery,
                                uint32_t queryCount, size_t dataSize, void* pData,
-                               VkDeviceSize stride, VkQueryResultFlags flags) {
-        // TODO: Implement
-    }
+                               VkDeviceSize stride, VkQueryResultFlags flags) {}
     void vkCreateBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                         const VkBufferCreateInfo* pCreateInfo,
                         const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pBuffer create
         mReconstruction.addHandles((const uint64_t*)pBuffer, 1);
@@ -492,7 +421,6 @@
     void vkDestroyBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkDevice device, VkBuffer buffer,
                          const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // buffer destroy
         mReconstruction.removeHandles((const uint64_t*)(&buffer), 1);
@@ -501,7 +429,6 @@
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                             const VkBufferViewCreateInfo* pCreateInfo,
                             const VkAllocationCallbacks* pAllocator, VkBufferView* pView) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pView create
         mReconstruction.addHandles((const uint64_t*)pView, 1);
@@ -517,7 +444,6 @@
     void vkDestroyBufferView(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkDevice device,
                              VkBufferView bufferView, const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // bufferView destroy
         mReconstruction.removeHandles((const uint64_t*)(&bufferView), 1);
@@ -526,7 +452,6 @@
                        android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                        const VkImageCreateInfo* pCreateInfo,
                        const VkAllocationCallbacks* pAllocator, VkImage* pImage) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pImage create
         mReconstruction.addHandles((const uint64_t*)pImage, 1);
@@ -543,7 +468,6 @@
     void vkDestroyImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkDevice device, VkImage image,
                         const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // image destroy
         mReconstruction.removeHandles((const uint64_t*)(&image), 1);
@@ -551,14 +475,11 @@
     void vkGetImageSubresourceLayout(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkDevice device, VkImage image,
                                      const VkImageSubresource* pSubresource,
-                                     VkSubresourceLayout* pLayout) {
-        // TODO: Implement
-    }
+                                     VkSubresourceLayout* pLayout) {}
     void vkCreateImageView(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                            const VkImageViewCreateInfo* pCreateInfo,
                            const VkAllocationCallbacks* pAllocator, VkImageView* pView) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pView create
         mReconstruction.addHandles((const uint64_t*)pView, 1);
@@ -574,7 +495,6 @@
     void vkDestroyImageView(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkDevice device, VkImageView imageView,
                             const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // imageView destroy
         mReconstruction.removeHandles((const uint64_t*)(&imageView), 1);
@@ -584,7 +504,6 @@
                               const VkShaderModuleCreateInfo* pCreateInfo,
                               const VkAllocationCallbacks* pAllocator,
                               VkShaderModule* pShaderModule) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pShaderModule create
         mReconstruction.addHandles((const uint64_t*)pShaderModule, 1);
@@ -602,7 +521,6 @@
                                android::base::BumpPool* pool, VkDevice device,
                                VkShaderModule shaderModule,
                                const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // shaderModule destroy
         mReconstruction.removeHandles((const uint64_t*)(&shaderModule), 1);
@@ -612,7 +530,6 @@
                                VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo,
                                const VkAllocationCallbacks* pAllocator,
                                VkPipelineCache* pPipelineCache) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pPipelineCache create
         mReconstruction.addHandles((const uint64_t*)pPipelineCache, 1);
@@ -630,7 +547,6 @@
                                 android::base::BumpPool* pool, VkDevice device,
                                 VkPipelineCache pipelineCache,
                                 const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pipelineCache destroy
         mReconstruction.removeHandles((const uint64_t*)(&pipelineCache), 1);
@@ -638,15 +554,11 @@
     void vkGetPipelineCacheData(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize,
-                                void* pData) {
-        // TODO: Implement
-    }
+                                void* pData) {}
     void vkMergePipelineCaches(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount,
-                               const VkPipelineCache* pSrcCaches) {
-        // TODO: Implement
-    }
+                               const VkPipelineCache* pSrcCaches) {}
     void vkCreateGraphicsPipelines(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkResult input_result,
                                    VkDevice device, VkPipelineCache pipelineCache,
@@ -654,7 +566,6 @@
                                    const VkGraphicsPipelineCreateInfo* pCreateInfos,
                                    const VkAllocationCallbacks* pAllocator,
                                    VkPipeline* pPipelines) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pPipelines create
         mReconstruction.addHandles((const uint64_t*)pPipelines, ((createInfoCount)));
@@ -676,7 +587,6 @@
                                   uint32_t createInfoCount,
                                   const VkComputePipelineCreateInfo* pCreateInfos,
                                   const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pPipelines create
         mReconstruction.addHandles((const uint64_t*)pPipelines, ((createInfoCount)));
@@ -695,7 +605,6 @@
     void vkDestroyPipeline(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkDevice device, VkPipeline pipeline,
                            const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pipeline destroy
         mReconstruction.removeHandles((const uint64_t*)(&pipeline), 1);
@@ -705,7 +614,6 @@
                                 VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo,
                                 const VkAllocationCallbacks* pAllocator,
                                 VkPipelineLayout* pPipelineLayout) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pPipelineLayout create
         mReconstruction.addHandles((const uint64_t*)pPipelineLayout, 1);
@@ -723,7 +631,6 @@
                                  android::base::BumpPool* pool, VkDevice device,
                                  VkPipelineLayout pipelineLayout,
                                  const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pipelineLayout destroy
         mReconstruction.removeHandles((const uint64_t*)(&pipelineLayout), 1);
@@ -732,7 +639,6 @@
                          android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                          const VkSamplerCreateInfo* pCreateInfo,
                          const VkAllocationCallbacks* pAllocator, VkSampler* pSampler) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pSampler create
         mReconstruction.addHandles((const uint64_t*)pSampler, 1);
@@ -749,7 +655,6 @@
     void vkDestroySampler(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkDevice device, VkSampler sampler,
                           const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // sampler destroy
         mReconstruction.removeHandles((const uint64_t*)(&sampler), 1);
@@ -760,7 +665,6 @@
                                      const VkDescriptorSetLayoutCreateInfo* pCreateInfo,
                                      const VkAllocationCallbacks* pAllocator,
                                      VkDescriptorSetLayout* pSetLayout) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pSetLayout create
         mReconstruction.addHandles((const uint64_t*)pSetLayout, 1);
@@ -778,7 +682,6 @@
                                       android::base::BumpPool* pool, VkDevice device,
                                       VkDescriptorSetLayout descriptorSetLayout,
                                       const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // descriptorSetLayout destroy
         mReconstruction.removeHandles((const uint64_t*)(&descriptorSetLayout), 1);
@@ -788,7 +691,6 @@
                                 VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo,
                                 const VkAllocationCallbacks* pAllocator,
                                 VkDescriptorPool* pDescriptorPool) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDescriptorPool create
         mReconstruction.addHandles((const uint64_t*)pDescriptorPool, 1);
@@ -806,7 +708,6 @@
                                  android::base::BumpPool* pool, VkDevice device,
                                  VkDescriptorPool descriptorPool,
                                  const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // descriptorPool destroy
         mReconstruction.removeHandles((const uint64_t*)(&descriptorPool), 1);
@@ -814,14 +715,11 @@
     void vkResetDescriptorPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, VkDescriptorPool descriptorPool,
-                               VkDescriptorPoolResetFlags flags) {
-        // TODO: Implement
-    }
+                               VkDescriptorPoolResetFlags flags) {}
     void vkAllocateDescriptorSets(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkResult input_result,
                                   VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo,
                                   VkDescriptorSet* pDescriptorSets) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDescriptorSets create
         mReconstruction.addHandles((const uint64_t*)pDescriptorSets,
@@ -843,7 +741,6 @@
                               android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                               VkDescriptorPool descriptorPool, uint32_t descriptorSetCount,
                               const VkDescriptorSet* pDescriptorSets) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDescriptorSets destroy
         mReconstruction.removeHandles((const uint64_t*)pDescriptorSets, ((descriptorSetCount)));
@@ -853,14 +750,11 @@
                                 uint32_t descriptorWriteCount,
                                 const VkWriteDescriptorSet* pDescriptorWrites,
                                 uint32_t descriptorCopyCount,
-                                const VkCopyDescriptorSet* pDescriptorCopies) {
-        // TODO: Implement
-    }
+                                const VkCopyDescriptorSet* pDescriptorCopies) {}
     void vkCreateFramebuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                              const VkFramebufferCreateInfo* pCreateInfo,
                              const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pFramebuffer create
         mReconstruction.addHandles((const uint64_t*)pFramebuffer, 1);
@@ -877,7 +771,6 @@
     void vkDestroyFramebuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkDevice device,
                               VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // framebuffer destroy
         mReconstruction.removeHandles((const uint64_t*)(&framebuffer), 1);
@@ -886,7 +779,6 @@
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                             const VkRenderPassCreateInfo* pCreateInfo,
                             const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pRenderPass create
         mReconstruction.addHandles((const uint64_t*)pRenderPass, 1);
@@ -903,21 +795,17 @@
     void vkDestroyRenderPass(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkDevice device,
                              VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // renderPass destroy
         mReconstruction.removeHandles((const uint64_t*)(&renderPass), 1);
     }
     void vkGetRenderAreaGranularity(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkDevice device,
-                                    VkRenderPass renderPass, VkExtent2D* pGranularity) {
-        // TODO: Implement
-    }
+                                    VkRenderPass renderPass, VkExtent2D* pGranularity) {}
     void vkCreateCommandPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                              const VkCommandPoolCreateInfo* pCreateInfo,
                              const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pCommandPool create
         mReconstruction.addHandles((const uint64_t*)pCommandPool, 1);
@@ -934,21 +822,17 @@
     void vkDestroyCommandPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkDevice device,
                               VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // commandPool destroy
         mReconstruction.removeHandles((const uint64_t*)(&commandPool), 1);
     }
     void vkResetCommandPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                            VkCommandPool commandPool, VkCommandPoolResetFlags flags) {
-        // TODO: Implement
-    }
+                            VkCommandPool commandPool, VkCommandPoolResetFlags flags) {}
     void vkAllocateCommandBuffers(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkResult input_result,
                                   VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo,
                                   VkCommandBuffer* pCommandBuffers) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pCommandBuffers create
         mReconstruction.addHandles((const uint64_t*)pCommandBuffers,
@@ -971,7 +855,6 @@
                               android::base::BumpPool* pool, VkDevice device,
                               VkCommandPool commandPool, uint32_t commandBufferCount,
                               const VkCommandBuffer* pCommandBuffers) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pCommandBuffers destroy
         mReconstruction.removeHandles((const uint64_t*)pCommandBuffers, ((commandBufferCount)));
@@ -979,205 +862,135 @@
     void vkBeginCommandBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkResult input_result,
                               VkCommandBuffer commandBuffer,
-                              const VkCommandBufferBeginInfo* pBeginInfo) {
-        // TODO: Implement
-    }
+                              const VkCommandBufferBeginInfo* pBeginInfo) {}
     void vkEndCommandBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkResult input_result,
-                            VkCommandBuffer commandBuffer) {
-        // TODO: Implement
-    }
+                            VkCommandBuffer commandBuffer) {}
     void vkResetCommandBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkResult input_result,
-                              VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) {
-        // TODO: Implement
-    }
+                              VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) {}
     void vkCmdBindPipeline(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                           VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) {
-        // TODO: Implement
-    }
+                           VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) {}
     void vkCmdSetViewport(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                           uint32_t firstViewport, uint32_t viewportCount,
-                          const VkViewport* pViewports) {
-        // TODO: Implement
-    }
+                          const VkViewport* pViewports) {}
     void vkCmdSetScissor(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                         uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) {
-        // TODO: Implement
-    }
+                         uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) {}
     void vkCmdSetLineWidth(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                           float lineWidth) {
-        // TODO: Implement
-    }
+                           float lineWidth) {}
     void vkCmdSetDepthBias(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            float depthBiasConstantFactor, float depthBiasClamp,
-                           float depthBiasSlopeFactor) {
-        // TODO: Implement
-    }
+                           float depthBiasSlopeFactor) {}
     void vkCmdSetBlendConstants(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                const float blendConstants[4]) {
-        // TODO: Implement
-    }
+                                const float blendConstants[4]) {}
     void vkCmdSetDepthBounds(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             float minDepthBounds, float maxDepthBounds) {
-        // TODO: Implement
-    }
+                             float minDepthBounds, float maxDepthBounds) {}
     void vkCmdSetStencilCompareMask(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                    VkStencilFaceFlags faceMask, uint32_t compareMask) {
-        // TODO: Implement
-    }
+                                    VkStencilFaceFlags faceMask, uint32_t compareMask) {}
     void vkCmdSetStencilWriteMask(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                  VkStencilFaceFlags faceMask, uint32_t writeMask) {
-        // TODO: Implement
-    }
+                                  VkStencilFaceFlags faceMask, uint32_t writeMask) {}
     void vkCmdSetStencilReference(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                  VkStencilFaceFlags faceMask, uint32_t reference) {
-        // TODO: Implement
-    }
+                                  VkStencilFaceFlags faceMask, uint32_t reference) {}
     void vkCmdBindDescriptorSets(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                  VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout,
                                  uint32_t firstSet, uint32_t descriptorSetCount,
                                  const VkDescriptorSet* pDescriptorSets,
-                                 uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) {
-        // TODO: Implement
-    }
+                                 uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) {}
     void vkCmdBindIndexBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                              VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) {
-        // TODO: Implement
-    }
+                              VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) {}
     void vkCmdBindVertexBuffers(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                 uint32_t firstBinding, uint32_t bindingCount,
-                                const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) {
-        // TODO: Implement
-    }
+                                const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) {}
     void vkCmdDraw(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                    uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex,
-                   uint32_t firstInstance) {
-        // TODO: Implement
-    }
+                   uint32_t firstInstance) {}
     void vkCmdDrawIndexed(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                           uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex,
-                          int32_t vertexOffset, uint32_t firstInstance) {
-        // TODO: Implement
-    }
+                          int32_t vertexOffset, uint32_t firstInstance) {}
     void vkCmdDrawIndirect(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
-                           uint32_t stride) {
-        // TODO: Implement
-    }
+                           uint32_t stride) {}
     void vkCmdDrawIndexedIndirect(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                   VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
-                                  uint32_t stride) {
-        // TODO: Implement
-    }
+                                  uint32_t stride) {}
     void vkCmdDispatch(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                       uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
-        // TODO: Implement
-    }
+                       uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {}
     void vkCmdDispatchIndirect(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                               VkBuffer buffer, VkDeviceSize offset) {
-        // TODO: Implement
-    }
+                               VkBuffer buffer, VkDeviceSize offset) {}
     void vkCmdCopyBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                          VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount,
-                         const VkBufferCopy* pRegions) {
-        // TODO: Implement
-    }
+                         const VkBufferCopy* pRegions) {}
     void vkCmdCopyImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                         VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
                         VkImageLayout dstImageLayout, uint32_t regionCount,
-                        const VkImageCopy* pRegions) {
-        // TODO: Implement
-    }
+                        const VkImageCopy* pRegions) {}
     void vkCmdBlitImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                         VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
                         VkImageLayout dstImageLayout, uint32_t regionCount,
-                        const VkImageBlit* pRegions, VkFilter filter) {
-        // TODO: Implement
-    }
+                        const VkImageBlit* pRegions, VkFilter filter) {}
     void vkCmdCopyBufferToImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                 VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout,
-                                uint32_t regionCount, const VkBufferImageCopy* pRegions) {
-        // TODO: Implement
-    }
+                                uint32_t regionCount, const VkBufferImageCopy* pRegions) {}
     void vkCmdCopyImageToBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                 VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer,
-                                uint32_t regionCount, const VkBufferImageCopy* pRegions) {
-        // TODO: Implement
-    }
+                                uint32_t regionCount, const VkBufferImageCopy* pRegions) {}
     void vkCmdUpdateBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize,
-                           const void* pData) {
-        // TODO: Implement
-    }
+                           const void* pData) {}
     void vkCmdFillBuffer(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                          VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size,
-                         uint32_t data) {
-        // TODO: Implement
-    }
+                         uint32_t data) {}
     void vkCmdClearColorImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               VkImage image, VkImageLayout imageLayout,
                               const VkClearColorValue* pColor, uint32_t rangeCount,
-                              const VkImageSubresourceRange* pRanges) {
-        // TODO: Implement
-    }
+                              const VkImageSubresourceRange* pRanges) {}
     void vkCmdClearDepthStencilImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                      VkImage image, VkImageLayout imageLayout,
                                      const VkClearDepthStencilValue* pDepthStencil,
-                                     uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {
-        // TODO: Implement
-    }
+                                     uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {}
     void vkCmdClearAttachments(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                uint32_t attachmentCount, const VkClearAttachment* pAttachments,
-                               uint32_t rectCount, const VkClearRect* pRects) {
-        // TODO: Implement
-    }
+                               uint32_t rectCount, const VkClearRect* pRects) {}
     void vkCmdResolveImage(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
                            VkImageLayout dstImageLayout, uint32_t regionCount,
-                           const VkImageResolve* pRegions) {
-        // TODO: Implement
-    }
+                           const VkImageResolve* pRegions) {}
     void vkCmdSetEvent(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer, VkEvent event,
-                       VkPipelineStageFlags stageMask) {
-        // TODO: Implement
-    }
+                       VkPipelineStageFlags stageMask) {}
     void vkCmdResetEvent(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                         VkEvent event, VkPipelineStageFlags stageMask) {
-        // TODO: Implement
-    }
+                         VkEvent event, VkPipelineStageFlags stageMask) {}
     void vkCmdWaitEvents(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                          uint32_t eventCount, const VkEvent* pEvents,
@@ -1186,9 +999,7 @@
                          uint32_t bufferMemoryBarrierCount,
                          const VkBufferMemoryBarrier* pBufferMemoryBarriers,
                          uint32_t imageMemoryBarrierCount,
-                         const VkImageMemoryBarrier* pImageMemoryBarriers) {
-        // TODO: Implement
-    }
+                         const VkImageMemoryBarrier* pImageMemoryBarriers) {}
     void vkCmdPipelineBarrier(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
@@ -1197,177 +1008,119 @@
                               uint32_t bufferMemoryBarrierCount,
                               const VkBufferMemoryBarrier* pBufferMemoryBarriers,
                               uint32_t imageMemoryBarrierCount,
-                              const VkImageMemoryBarrier* pImageMemoryBarriers) {
-        // TODO: Implement
-    }
+                              const VkImageMemoryBarrier* pImageMemoryBarriers) {}
     void vkCmdBeginQuery(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                         VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) {
-        // TODO: Implement
-    }
+                         VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) {}
     void vkCmdEndQuery(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                       VkQueryPool queryPool, uint32_t query) {
-        // TODO: Implement
-    }
+                       VkQueryPool queryPool, uint32_t query) {}
     void vkCmdResetQueryPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
-        // TODO: Implement
-    }
+                             VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {}
     void vkCmdWriteTimestamp(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                              VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool,
-                             uint32_t query) {
-        // TODO: Implement
-    }
+                             uint32_t query) {}
     void vkCmdCopyQueryPoolResults(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                    VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount,
                                    VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride,
-                                   VkQueryResultFlags flags) {
-        // TODO: Implement
-    }
+                                   VkQueryResultFlags flags) {}
     void vkCmdPushConstants(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                             VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset,
-                            uint32_t size, const void* pValues) {
-        // TODO: Implement
-    }
+                            uint32_t size, const void* pValues) {}
     void vkCmdBeginRenderPass(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               const VkRenderPassBeginInfo* pRenderPassBegin,
-                              VkSubpassContents contents) {
-        // TODO: Implement
-    }
+                              VkSubpassContents contents) {}
     void vkCmdNextSubpass(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                          VkSubpassContents contents) {
-        // TODO: Implement
-    }
+                          VkSubpassContents contents) {}
     void vkCmdEndRenderPass(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {
-        // TODO: Implement
-    }
+                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {}
     void vkCmdExecuteCommands(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) {
-        // TODO: Implement
     }
 #endif
 #ifdef VK_VERSION_1_1
     void vkEnumerateInstanceVersion(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkResult input_result,
-                                    uint32_t* pApiVersion) {
-        // TODO: Implement
-    }
+                                    uint32_t* pApiVersion) {}
     void vkBindBufferMemory2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                             uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {
-        // TODO: Implement
-    }
+                             uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {}
     void vkBindImageMemory2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                            uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
-        // TODO: Implement
-    }
+                            uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {}
     void vkGetDeviceGroupPeerMemoryFeatures(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, VkDevice device,
                                             uint32_t heapIndex, uint32_t localDeviceIndex,
                                             uint32_t remoteDeviceIndex,
-                                            VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {
-        // TODO: Implement
-    }
+                                            VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {}
     void vkCmdSetDeviceMask(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                            uint32_t deviceMask) {
-        // TODO: Implement
-    }
+                            uint32_t deviceMask) {}
     void vkCmdDispatchBase(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ,
-                           uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
-        // TODO: Implement
-    }
+                           uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {}
     void vkEnumeratePhysicalDeviceGroups(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkInstance instance, uint32_t* pPhysicalDeviceGroupCount,
-        VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
-        // TODO: Implement
-    }
+        VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {}
     void vkGetImageMemoryRequirements2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkDevice device,
                                        const VkImageMemoryRequirementsInfo2* pInfo,
-                                       VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                       VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetBufferMemoryRequirements2(const uint8_t* snapshotTraceBegin,
                                         size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                         VkDevice device,
                                         const VkBufferMemoryRequirementsInfo2* pInfo,
-                                        VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                        VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetImageSparseMemoryRequirements2(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo,
         uint32_t* pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-        // TODO: Implement
-    }
+        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {}
     void vkGetPhysicalDeviceFeatures2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool,
                                       VkPhysicalDevice physicalDevice,
-                                      VkPhysicalDeviceFeatures2* pFeatures) {
-        // TODO: Implement
-    }
+                                      VkPhysicalDeviceFeatures2* pFeatures) {}
     void vkGetPhysicalDeviceProperties2(const uint8_t* snapshotTraceBegin,
                                         size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                         VkPhysicalDevice physicalDevice,
-                                        VkPhysicalDeviceProperties2* pProperties) {
-        // TODO: Implement
-    }
+                                        VkPhysicalDeviceProperties2* pProperties) {}
     void vkGetPhysicalDeviceFormatProperties2(const uint8_t* snapshotTraceBegin,
                                               size_t snapshotTraceBytes,
                                               android::base::BumpPool* pool,
                                               VkPhysicalDevice physicalDevice, VkFormat format,
-                                              VkFormatProperties2* pFormatProperties) {
-        // TODO: Implement
-    }
+                                              VkFormatProperties2* pFormatProperties) {}
     void vkGetPhysicalDeviceImageFormatProperties2(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
-        VkImageFormatProperties2* pImageFormatProperties) {
-        // TODO: Implement
-    }
+        VkImageFormatProperties2* pImageFormatProperties) {}
     void vkGetPhysicalDeviceQueueFamilyProperties2(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount,
-        VkQueueFamilyProperties2* pQueueFamilyProperties) {
-        // TODO: Implement
-    }
+        VkQueueFamilyProperties2* pQueueFamilyProperties) {}
     void vkGetPhysicalDeviceMemoryProperties2(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
-        VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
-        // TODO: Implement
-    }
+        VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {}
     void vkGetPhysicalDeviceSparseImageFormatProperties2(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
-        uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
-        // TODO: Implement
-    }
+        uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {}
     void vkTrimCommandPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkDevice device,
-                           VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
-        // TODO: Implement
-    }
+                           VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {}
     void vkGetDeviceQueue2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkDevice device,
                            const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pQueue create
         mReconstruction.addHandles((const uint64_t*)pQueue, 1);
@@ -1387,7 +1140,6 @@
                                         const VkSamplerYcbcrConversionCreateInfo* pCreateInfo,
                                         const VkAllocationCallbacks* pAllocator,
                                         VkSamplerYcbcrConversion* pYcbcrConversion) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pYcbcrConversion create
         mReconstruction.addHandles((const uint64_t*)pYcbcrConversion, 1);
@@ -1405,7 +1157,6 @@
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkDevice device, VkSamplerYcbcrConversion ycbcrConversion,
                                          const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // ycbcrConversion destroy
         mReconstruction.removeHandles((const uint64_t*)(&ycbcrConversion), 1);
@@ -1416,7 +1167,6 @@
                                           const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo,
                                           const VkAllocationCallbacks* pAllocator,
                                           VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDescriptorUpdateTemplate create
         mReconstruction.addHandles((const uint64_t*)pDescriptorUpdateTemplate, 1);
@@ -1437,7 +1187,6 @@
                                            VkDevice device,
                                            VkDescriptorUpdateTemplate descriptorUpdateTemplate,
                                            const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // descriptorUpdateTemplate destroy
         mReconstruction.removeHandles((const uint64_t*)(&descriptorUpdateTemplate), 1);
@@ -1446,58 +1195,43 @@
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkDevice device, VkDescriptorSet descriptorSet,
                                            VkDescriptorUpdateTemplate descriptorUpdateTemplate,
-                                           const void* pData) {
-        // TODO: Implement
-    }
+                                           const void* pData) {}
     void vkGetPhysicalDeviceExternalBufferProperties(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
-        VkExternalBufferProperties* pExternalBufferProperties) {
-        // TODO: Implement
-    }
+        VkExternalBufferProperties* pExternalBufferProperties) {}
     void vkGetPhysicalDeviceExternalFenceProperties(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
-        VkExternalFenceProperties* pExternalFenceProperties) {
-        // TODO: Implement
-    }
+        VkExternalFenceProperties* pExternalFenceProperties) {}
     void vkGetPhysicalDeviceExternalSemaphoreProperties(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
-        VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
-        // TODO: Implement
-    }
+        VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {}
     void vkGetDescriptorSetLayoutSupport(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkDevice device,
                                          const VkDescriptorSetLayoutCreateInfo* pCreateInfo,
-                                         VkDescriptorSetLayoutSupport* pSupport) {
-        // TODO: Implement
-    }
+                                         VkDescriptorSetLayoutSupport* pSupport) {}
 #endif
 #ifdef VK_VERSION_1_2
     void vkCmdDrawIndirectCount(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                 VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer,
                                 VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
-                                uint32_t stride) {
-        // TODO: Implement
-    }
+                                uint32_t stride) {}
     void vkCmdDrawIndexedIndirectCount(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                        VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer,
                                        VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
-                                       uint32_t stride) {
-        // TODO: Implement
-    }
+                                       uint32_t stride) {}
     void vkCreateRenderPass2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                              const VkRenderPassCreateInfo2* pCreateInfo,
                              const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pRenderPass create
         mReconstruction.addHandles((const uint64_t*)pRenderPass, 1);
@@ -1514,262 +1248,168 @@
     void vkCmdBeginRenderPass2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                const VkRenderPassBeginInfo* pRenderPassBegin,
-                               const VkSubpassBeginInfo* pSubpassBeginInfo) {
-        // TODO: Implement
-    }
+                               const VkSubpassBeginInfo* pSubpassBeginInfo) {}
     void vkCmdNextSubpass2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            const VkSubpassBeginInfo* pSubpassBeginInfo,
-                           const VkSubpassEndInfo* pSubpassEndInfo) {
-        // TODO: Implement
-    }
+                           const VkSubpassEndInfo* pSubpassEndInfo) {}
     void vkCmdEndRenderPass2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             const VkSubpassEndInfo* pSubpassEndInfo) {
-        // TODO: Implement
-    }
+                             const VkSubpassEndInfo* pSubpassEndInfo) {}
     void vkResetQueryPool(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkDevice device, VkQueryPool queryPool,
-                          uint32_t firstQuery, uint32_t queryCount) {
-        // TODO: Implement
-    }
+                          uint32_t firstQuery, uint32_t queryCount) {}
     void vkGetSemaphoreCounterValue(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkResult input_result,
-                                    VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
-        // TODO: Implement
-    }
+                                    VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {}
     void vkWaitSemaphores(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                          const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
-        // TODO: Implement
-    }
+                          const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {}
     void vkSignalSemaphore(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                           const VkSemaphoreSignalInfo* pSignalInfo) {
-        // TODO: Implement
-    }
+                           const VkSemaphoreSignalInfo* pSignalInfo) {}
     void vkGetBufferDeviceAddress(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkDeviceAddress input_result,
-                                  VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+                                  VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {}
     void vkGetBufferOpaqueCaptureAddress(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          uint64_t input_result, VkDevice device,
-                                         const VkBufferDeviceAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+                                         const VkBufferDeviceAddressInfo* pInfo) {}
     void vkGetDeviceMemoryOpaqueCaptureAddress(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         uint64_t input_result, VkDevice device,
-        const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+        const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {}
 #endif
 #ifdef VK_VERSION_1_3
     void vkGetPhysicalDeviceToolProperties(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkResult input_result, VkPhysicalDevice physicalDevice,
                                            uint32_t* pToolCount,
-                                           VkPhysicalDeviceToolProperties* pToolProperties) {
-        // TODO: Implement
-    }
+                                           VkPhysicalDeviceToolProperties* pToolProperties) {}
     void vkCreatePrivateDataSlot(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkResult input_result,
                                  VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo,
                                  const VkAllocationCallbacks* pAllocator,
-                                 VkPrivateDataSlot* pPrivateDataSlot) {
-        // TODO: Implement
-    }
+                                 VkPrivateDataSlot* pPrivateDataSlot) {}
     void vkDestroyPrivateDataSlot(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkDevice device,
                                   VkPrivateDataSlot privateDataSlot,
-                                  const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
-    }
+                                  const VkAllocationCallbacks* pAllocator) {}
     void vkSetPrivateData(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                           VkObjectType objectType, uint64_t objectHandle,
-                          VkPrivateDataSlot privateDataSlot, uint64_t data) {
-        // TODO: Implement
-    }
+                          VkPrivateDataSlot privateDataSlot, uint64_t data) {}
     void vkGetPrivateData(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkDevice device, VkObjectType objectType,
                           uint64_t objectHandle, VkPrivateDataSlot privateDataSlot,
-                          uint64_t* pData) {
-        // TODO: Implement
-    }
+                          uint64_t* pData) {}
     void vkCmdSetEvent2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkCommandBuffer commandBuffer, VkEvent event,
-                        const VkDependencyInfo* pDependencyInfo) {
-        // TODO: Implement
-    }
+                        const VkDependencyInfo* pDependencyInfo) {}
     void vkCmdResetEvent2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                          VkEvent event, VkPipelineStageFlags2 stageMask) {
-        // TODO: Implement
-    }
+                          VkEvent event, VkPipelineStageFlags2 stageMask) {}
     void vkCmdWaitEvents2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                           uint32_t eventCount, const VkEvent* pEvents,
-                          const VkDependencyInfo* pDependencyInfos) {
-        // TODO: Implement
-    }
+                          const VkDependencyInfo* pDependencyInfos) {}
     void vkCmdPipelineBarrier2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                               const VkDependencyInfo* pDependencyInfo) {
-        // TODO: Implement
-    }
+                               const VkDependencyInfo* pDependencyInfo) {}
     void vkCmdWriteTimestamp2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                              VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) {
-        // TODO: Implement
-    }
+                              VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) {}
     void vkQueueSubmit2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                         android::base::BumpPool* pool, VkResult input_result, VkQueue queue,
-                        uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {
-        // TODO: Implement
-    }
+                        uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {}
     void vkCmdCopyBuffer2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                          const VkCopyBufferInfo2* pCopyBufferInfo) {
-        // TODO: Implement
-    }
+                          const VkCopyBufferInfo2* pCopyBufferInfo) {}
     void vkCmdCopyImage2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                         const VkCopyImageInfo2* pCopyImageInfo) {
-        // TODO: Implement
-    }
+                         const VkCopyImageInfo2* pCopyImageInfo) {}
     void vkCmdCopyBufferToImage2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                 const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {
-        // TODO: Implement
-    }
+                                 const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {}
     void vkCmdCopyImageToBuffer2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                 const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
-        // TODO: Implement
-    }
+                                 const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {}
     void vkCmdBlitImage2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                         const VkBlitImageInfo2* pBlitImageInfo) {
-        // TODO: Implement
-    }
+                         const VkBlitImageInfo2* pBlitImageInfo) {}
     void vkCmdResolveImage2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                            const VkResolveImageInfo2* pResolveImageInfo) {
-        // TODO: Implement
-    }
+                            const VkResolveImageInfo2* pResolveImageInfo) {}
     void vkCmdBeginRendering(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             const VkRenderingInfo* pRenderingInfo) {
-        // TODO: Implement
-    }
+                             const VkRenderingInfo* pRenderingInfo) {}
     void vkCmdEndRendering(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {
-        // TODO: Implement
-    }
+                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {}
     void vkCmdSetCullMode(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                           android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                          VkCullModeFlags cullMode) {
-        // TODO: Implement
-    }
+                          VkCullModeFlags cullMode) {}
     void vkCmdSetFrontFace(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                           VkFrontFace frontFace) {
-        // TODO: Implement
-    }
+                           VkFrontFace frontFace) {}
     void vkCmdSetPrimitiveTopology(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                   VkPrimitiveTopology primitiveTopology) {
-        // TODO: Implement
-    }
+                                   VkPrimitiveTopology primitiveTopology) {}
     void vkCmdSetViewportWithCount(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                   uint32_t viewportCount, const VkViewport* pViewports) {
-        // TODO: Implement
-    }
+                                   uint32_t viewportCount, const VkViewport* pViewports) {}
     void vkCmdSetScissorWithCount(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                  uint32_t scissorCount, const VkRect2D* pScissors) {
-        // TODO: Implement
-    }
+                                  uint32_t scissorCount, const VkRect2D* pScissors) {}
     void vkCmdBindVertexBuffers2(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                  uint32_t firstBinding, uint32_t bindingCount,
                                  const VkBuffer* pBuffers, const VkDeviceSize* pOffsets,
-                                 const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
-        // TODO: Implement
-    }
+                                 const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {}
     void vkCmdSetDepthTestEnable(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                 VkBool32 depthTestEnable) {
-        // TODO: Implement
-    }
+                                 VkBool32 depthTestEnable) {}
     void vkCmdSetDepthWriteEnable(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                  VkBool32 depthWriteEnable) {
-        // TODO: Implement
-    }
+                                  VkBool32 depthWriteEnable) {}
     void vkCmdSetDepthCompareOp(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                VkCompareOp depthCompareOp) {
-        // TODO: Implement
-    }
+                                VkCompareOp depthCompareOp) {}
     void vkCmdSetDepthBoundsTestEnable(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                       VkBool32 depthBoundsTestEnable) {
-        // TODO: Implement
-    }
+                                       VkBool32 depthBoundsTestEnable) {}
     void vkCmdSetStencilTestEnable(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                   VkBool32 stencilTestEnable) {
-        // TODO: Implement
-    }
+                                   VkBool32 stencilTestEnable) {}
     void vkCmdSetStencilOp(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                            VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp,
-                           VkStencilOp depthFailOp, VkCompareOp compareOp) {
-        // TODO: Implement
-    }
+                           VkStencilOp depthFailOp, VkCompareOp compareOp) {}
     void vkCmdSetRasterizerDiscardEnable(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkCommandBuffer commandBuffer,
-                                         VkBool32 rasterizerDiscardEnable) {
-        // TODO: Implement
-    }
+                                         VkBool32 rasterizerDiscardEnable) {}
     void vkCmdSetDepthBiasEnable(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                 VkBool32 depthBiasEnable) {
-        // TODO: Implement
-    }
+                                 VkBool32 depthBiasEnable) {}
     void vkCmdSetPrimitiveRestartEnable(const uint8_t* snapshotTraceBegin,
                                         size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                         VkCommandBuffer commandBuffer,
-                                        VkBool32 primitiveRestartEnable) {
-        // TODO: Implement
-    }
+                                        VkBool32 primitiveRestartEnable) {}
     void vkGetDeviceBufferMemoryRequirements(const uint8_t* snapshotTraceBegin,
                                              size_t snapshotTraceBytes,
                                              android::base::BumpPool* pool, VkDevice device,
                                              const VkDeviceBufferMemoryRequirements* pInfo,
-                                             VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                             VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetDeviceImageMemoryRequirements(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, VkDevice device,
                                             const VkDeviceImageMemoryRequirements* pInfo,
-                                            VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                            VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetDeviceImageSparseMemoryRequirements(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkDevice device, const VkDeviceImageMemoryRequirements* pInfo,
         uint32_t* pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-        // TODO: Implement
-    }
+        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {}
 #endif
 #ifdef VK_KHR_surface
 #endif
@@ -1778,7 +1418,6 @@
                               android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                               const VkSwapchainCreateInfoKHR* pCreateInfo,
                               const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pSwapchain create
         mReconstruction.addHandles((const uint64_t*)pSwapchain, 1);
@@ -1795,7 +1434,6 @@
     void vkDestroySwapchainKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkDevice device,
                                VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // swapchain destroy
         mReconstruction.removeHandles((const uint64_t*)(&swapchain), 1);
@@ -1803,46 +1441,32 @@
     void vkGetSwapchainImagesKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkResult input_result,
                                  VkDevice device, VkSwapchainKHR swapchain,
-                                 uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
-        // TODO: Implement
-    }
+                                 uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {}
     void vkAcquireNextImageKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout,
-                               VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
-        // TODO: Implement
-    }
+                               VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {}
     void vkQueuePresentKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkQueue queue,
-                           const VkPresentInfoKHR* pPresentInfo) {
-        // TODO: Implement
-    }
+                           const VkPresentInfoKHR* pPresentInfo) {}
     void vkGetDeviceGroupPresentCapabilitiesKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkDevice device,
-        VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
-        // TODO: Implement
-    }
+        VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {}
     void vkGetDeviceGroupSurfacePresentModesKHR(const uint8_t* snapshotTraceBegin,
                                                 size_t snapshotTraceBytes,
                                                 android::base::BumpPool* pool,
                                                 VkResult input_result, VkDevice device,
                                                 VkSurfaceKHR surface,
-                                                VkDeviceGroupPresentModeFlagsKHR* pModes) {
-        // TODO: Implement
-    }
+                                                VkDeviceGroupPresentModeFlagsKHR* pModes) {}
     void vkGetPhysicalDevicePresentRectanglesKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
-        uint32_t* pRectCount, VkRect2D* pRects) {
-        // TODO: Implement
-    }
+        uint32_t* pRectCount, VkRect2D* pRects) {}
     void vkAcquireNextImage2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo,
-                                uint32_t* pImageIndex) {
-        // TODO: Implement
-    }
+                                uint32_t* pImageIndex) {}
 #endif
 #ifdef VK_KHR_xcb_surface
 #endif
@@ -1853,74 +1477,52 @@
 #ifdef VK_KHR_dynamic_rendering
     void vkCmdBeginRenderingKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                const VkRenderingInfo* pRenderingInfo) {
-        // TODO: Implement
-    }
+                                const VkRenderingInfo* pRenderingInfo) {}
     void vkCmdEndRenderingKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {
-        // TODO: Implement
-    }
+                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer) {}
 #endif
 #ifdef VK_KHR_get_physical_device_properties2
     void vkGetPhysicalDeviceFeatures2KHR(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkPhysicalDevice physicalDevice,
-                                         VkPhysicalDeviceFeatures2* pFeatures) {
-        // TODO: Implement
-    }
+                                         VkPhysicalDeviceFeatures2* pFeatures) {}
     void vkGetPhysicalDeviceProperties2KHR(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkPhysicalDevice physicalDevice,
-                                           VkPhysicalDeviceProperties2* pProperties) {
-        // TODO: Implement
-    }
+                                           VkPhysicalDeviceProperties2* pProperties) {}
     void vkGetPhysicalDeviceFormatProperties2KHR(const uint8_t* snapshotTraceBegin,
                                                  size_t snapshotTraceBytes,
                                                  android::base::BumpPool* pool,
                                                  VkPhysicalDevice physicalDevice, VkFormat format,
-                                                 VkFormatProperties2* pFormatProperties) {
-        // TODO: Implement
-    }
+                                                 VkFormatProperties2* pFormatProperties) {}
     void vkGetPhysicalDeviceImageFormatProperties2KHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
-        VkImageFormatProperties2* pImageFormatProperties) {
-        // TODO: Implement
-    }
+        VkImageFormatProperties2* pImageFormatProperties) {}
     void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount,
-        VkQueueFamilyProperties2* pQueueFamilyProperties) {
-        // TODO: Implement
-    }
+        VkQueueFamilyProperties2* pQueueFamilyProperties) {}
     void vkGetPhysicalDeviceMemoryProperties2KHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
-        VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
-        // TODO: Implement
-    }
+        VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {}
     void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
-        uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
-        // TODO: Implement
-    }
+        uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {}
 #endif
 #ifdef VK_KHR_maintenance1
     void vkTrimCommandPoolKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkDevice device,
-                              VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
-        // TODO: Implement
-    }
+                              VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {}
 #endif
 #ifdef VK_KHR_external_memory_capabilities
     void vkGetPhysicalDeviceExternalBufferPropertiesKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
-        VkExternalBufferProperties* pExternalBufferProperties) {
-        // TODO: Implement
-    }
+        VkExternalBufferProperties* pExternalBufferProperties) {}
 #endif
 #ifdef VK_KHR_external_memory
 #endif
@@ -1933,9 +1535,7 @@
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
-        VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
-        // TODO: Implement
-    }
+        VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {}
 #endif
 #ifdef VK_KHR_external_semaphore
 #endif
@@ -1945,14 +1545,10 @@
     void vkImportSemaphoreFdKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device,
-                                const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) {
-        // TODO: Implement
-    }
+                                const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) {}
     void vkGetSemaphoreFdKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                             const VkSemaphoreGetFdInfoKHR* pGetFdInfo, int* pFd) {
-        // TODO: Implement
-    }
+                             const VkSemaphoreGetFdInfoKHR* pGetFdInfo, int* pFd) {}
 #endif
 #ifdef VK_KHR_shader_float16_int8
 #endif
@@ -1965,7 +1561,6 @@
         const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo,
         const VkAllocationCallbacks* pAllocator,
         VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pDescriptorUpdateTemplate create
         mReconstruction.addHandles((const uint64_t*)pDescriptorUpdateTemplate, 1);
@@ -1986,7 +1581,6 @@
                                               android::base::BumpPool* pool, VkDevice device,
                                               VkDescriptorUpdateTemplate descriptorUpdateTemplate,
                                               const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // descriptorUpdateTemplate destroy
         mReconstruction.removeHandles((const uint64_t*)(&descriptorUpdateTemplate), 1);
@@ -1996,9 +1590,7 @@
                                               android::base::BumpPool* pool, VkDevice device,
                                               VkDescriptorSet descriptorSet,
                                               VkDescriptorUpdateTemplate descriptorUpdateTemplate,
-                                              const void* pData) {
-        // TODO: Implement
-    }
+                                              const void* pData) {}
 #endif
 #ifdef VK_KHR_imageless_framebuffer
 #endif
@@ -2008,7 +1600,6 @@
                                 VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo,
                                 const VkAllocationCallbacks* pAllocator,
                                 VkRenderPass* pRenderPass) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pRenderPass create
         mReconstruction.addHandles((const uint64_t*)pRenderPass, 1);
@@ -2025,43 +1616,31 @@
     void vkCmdBeginRenderPass2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                   const VkRenderPassBeginInfo* pRenderPassBegin,
-                                  const VkSubpassBeginInfo* pSubpassBeginInfo) {
-        // TODO: Implement
-    }
+                                  const VkSubpassBeginInfo* pSubpassBeginInfo) {}
     void vkCmdNextSubpass2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               const VkSubpassBeginInfo* pSubpassBeginInfo,
-                              const VkSubpassEndInfo* pSubpassEndInfo) {
-        // TODO: Implement
-    }
+                              const VkSubpassEndInfo* pSubpassEndInfo) {}
     void vkCmdEndRenderPass2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                const VkSubpassEndInfo* pSubpassEndInfo) {
-        // TODO: Implement
-    }
+                                const VkSubpassEndInfo* pSubpassEndInfo) {}
 #endif
 #ifdef VK_KHR_external_fence_capabilities
     void vkGetPhysicalDeviceExternalFencePropertiesKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkPhysicalDevice physicalDevice,
         const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
-        VkExternalFenceProperties* pExternalFenceProperties) {
-        // TODO: Implement
-    }
+        VkExternalFenceProperties* pExternalFenceProperties) {}
 #endif
 #ifdef VK_KHR_external_fence
 #endif
 #ifdef VK_KHR_external_fence_fd
     void vkImportFenceFdKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                            const VkImportFenceFdInfoKHR* pImportFenceFdInfo) {
-        // TODO: Implement
-    }
+                            const VkImportFenceFdInfoKHR* pImportFenceFdInfo) {}
     void vkGetFenceFdKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkResult input_result, VkDevice device,
-                         const VkFenceGetFdInfoKHR* pGetFdInfo, int* pFd) {
-        // TODO: Implement
-    }
+                         const VkFenceGetFdInfoKHR* pGetFdInfo, int* pFd) {}
 #endif
 #ifdef VK_KHR_maintenance2
 #endif
@@ -2074,23 +1653,17 @@
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkDevice device,
                                           const VkImageMemoryRequirementsInfo2* pInfo,
-                                          VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                          VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetBufferMemoryRequirements2KHR(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkDevice device,
                                            const VkBufferMemoryRequirementsInfo2* pInfo,
-                                           VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                           VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetImageSparseMemoryRequirements2KHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo,
         uint32_t* pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-        // TODO: Implement
-    }
+        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {}
 #endif
 #ifdef VK_KHR_image_format_list
 #endif
@@ -2101,7 +1674,6 @@
                                            const VkSamplerYcbcrConversionCreateInfo* pCreateInfo,
                                            const VkAllocationCallbacks* pAllocator,
                                            VkSamplerYcbcrConversion* pYcbcrConversion) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pYcbcrConversion create
         mReconstruction.addHandles((const uint64_t*)pYcbcrConversion, 1);
@@ -2120,7 +1692,6 @@
                                             android::base::BumpPool* pool, VkDevice device,
                                             VkSamplerYcbcrConversion ycbcrConversion,
                                             const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // ycbcrConversion destroy
         mReconstruction.removeHandles((const uint64_t*)(&ycbcrConversion), 1);
@@ -2130,24 +1701,18 @@
     void vkBindBufferMemory2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device, uint32_t bindInfoCount,
-                                const VkBindBufferMemoryInfo* pBindInfos) {
-        // TODO: Implement
-    }
+                                const VkBindBufferMemoryInfo* pBindInfos) {}
     void vkBindImageMemory2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, uint32_t bindInfoCount,
-                               const VkBindImageMemoryInfo* pBindInfos) {
-        // TODO: Implement
-    }
+                               const VkBindImageMemoryInfo* pBindInfos) {}
 #endif
 #ifdef VK_KHR_maintenance3
     void vkGetDescriptorSetLayoutSupportKHR(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, VkDevice device,
                                             const VkDescriptorSetLayoutCreateInfo* pCreateInfo,
-                                            VkDescriptorSetLayoutSupport* pSupport) {
-        // TODO: Implement
-    }
+                                            VkDescriptorSetLayoutSupport* pSupport) {}
 #endif
 #ifdef VK_KHR_shader_subgroup_extended_types
 #endif
@@ -2158,43 +1723,31 @@
 #ifdef VK_KHR_buffer_device_address
     void vkGetBufferDeviceAddressKHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkDeviceAddress input_result,
-                                     VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+                                     VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {}
     void vkGetBufferOpaqueCaptureAddressKHR(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, uint64_t input_result,
                                             VkDevice device,
-                                            const VkBufferDeviceAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+                                            const VkBufferDeviceAddressInfo* pInfo) {}
     void vkGetDeviceMemoryOpaqueCaptureAddressKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         uint64_t input_result, VkDevice device,
-        const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
-        // TODO: Implement
-    }
+        const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {}
 #endif
 #ifdef VK_KHR_pipeline_executable_properties
     void vkGetPipelineExecutablePropertiesKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkDevice device, const VkPipelineInfoKHR* pPipelineInfo,
-        uint32_t* pExecutableCount, VkPipelineExecutablePropertiesKHR* pProperties) {
-        // TODO: Implement
-    }
+        uint32_t* pExecutableCount, VkPipelineExecutablePropertiesKHR* pProperties) {}
     void vkGetPipelineExecutableStatisticsKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo,
-        uint32_t* pStatisticCount, VkPipelineExecutableStatisticKHR* pStatistics) {
-        // TODO: Implement
-    }
+        uint32_t* pStatisticCount, VkPipelineExecutableStatisticKHR* pStatistics) {}
     void vkGetPipelineExecutableInternalRepresentationsKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo,
         uint32_t* pInternalRepresentationCount,
-        VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations) {
-        // TODO: Implement
-    }
+        VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations) {}
 #endif
 #ifdef VK_KHR_shader_integer_dot_product
 #endif
@@ -2203,82 +1756,54 @@
 #ifdef VK_KHR_synchronization2
     void vkCmdSetEvent2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                           VkEvent event, const VkDependencyInfo* pDependencyInfo) {
-        // TODO: Implement
-    }
+                           VkEvent event, const VkDependencyInfo* pDependencyInfo) {}
     void vkCmdResetEvent2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             VkEvent event, VkPipelineStageFlags2 stageMask) {
-        // TODO: Implement
-    }
+                             VkEvent event, VkPipelineStageFlags2 stageMask) {}
     void vkCmdWaitEvents2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                              uint32_t eventCount, const VkEvent* pEvents,
-                             const VkDependencyInfo* pDependencyInfos) {
-        // TODO: Implement
-    }
+                             const VkDependencyInfo* pDependencyInfos) {}
     void vkCmdPipelineBarrier2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                  const VkDependencyInfo* pDependencyInfo) {
-        // TODO: Implement
-    }
+                                  const VkDependencyInfo* pDependencyInfo) {}
     void vkCmdWriteTimestamp2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                  VkPipelineStageFlags2 stage, VkQueryPool queryPool,
-                                 uint32_t query) {
-        // TODO: Implement
-    }
+                                 uint32_t query) {}
     void vkQueueSubmit2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                            android::base::BumpPool* pool, VkResult input_result, VkQueue queue,
-                           uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {
-        // TODO: Implement
-    }
+                           uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {}
     void vkCmdWriteBufferMarker2AMD(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                     VkPipelineStageFlags2 stage, VkBuffer dstBuffer,
-                                    VkDeviceSize dstOffset, uint32_t marker) {
-        // TODO: Implement
-    }
+                                    VkDeviceSize dstOffset, uint32_t marker) {}
     void vkGetQueueCheckpointData2NV(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkQueue queue,
                                      uint32_t* pCheckpointDataCount,
-                                     VkCheckpointData2NV* pCheckpointData) {
-        // TODO: Implement
-    }
+                                     VkCheckpointData2NV* pCheckpointData) {}
 #endif
 #ifdef VK_KHR_zero_initialize_workgroup_memory
 #endif
 #ifdef VK_KHR_copy_commands2
     void vkCmdCopyBuffer2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             const VkCopyBufferInfo2* pCopyBufferInfo) {
-        // TODO: Implement
-    }
+                             const VkCopyBufferInfo2* pCopyBufferInfo) {}
     void vkCmdCopyImage2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                            const VkCopyImageInfo2* pCopyImageInfo) {
-        // TODO: Implement
-    }
+                            const VkCopyImageInfo2* pCopyImageInfo) {}
     void vkCmdCopyBufferToImage2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                    const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {
-        // TODO: Implement
-    }
+                                    const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {}
     void vkCmdCopyImageToBuffer2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                    const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
-        // TODO: Implement
-    }
+                                    const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {}
     void vkCmdBlitImage2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                            const VkBlitImageInfo2* pBlitImageInfo) {
-        // TODO: Implement
-    }
+                            const VkBlitImageInfo2* pBlitImageInfo) {}
     void vkCmdResolveImage2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                               const VkResolveImageInfo2* pResolveImageInfo) {
-        // TODO: Implement
-    }
+                               const VkResolveImageInfo2* pResolveImageInfo) {}
 #endif
 #ifdef VK_KHR_format_feature_flags2
 #endif
@@ -2287,122 +1812,88 @@
                                                 size_t snapshotTraceBytes,
                                                 android::base::BumpPool* pool, VkDevice device,
                                                 const VkDeviceBufferMemoryRequirements* pInfo,
-                                                VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                                VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetDeviceImageMemoryRequirementsKHR(const uint8_t* snapshotTraceBegin,
                                                size_t snapshotTraceBytes,
                                                android::base::BumpPool* pool, VkDevice device,
                                                const VkDeviceImageMemoryRequirements* pInfo,
-                                               VkMemoryRequirements2* pMemoryRequirements) {
-        // TODO: Implement
-    }
+                                               VkMemoryRequirements2* pMemoryRequirements) {}
     void vkGetDeviceImageSparseMemoryRequirementsKHR(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkDevice device, const VkDeviceImageMemoryRequirements* pInfo,
         uint32_t* pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-        // TODO: Implement
-    }
+        VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {}
 #endif
 #ifdef VK_KHR_maintenance5
     void vkCmdBindIndexBuffer2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                   VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size,
-                                  VkIndexType indexType) {
-        // TODO: Implement
-    }
+                                  VkIndexType indexType) {}
     void vkGetRenderingAreaGranularityKHR(const uint8_t* snapshotTraceBegin,
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkDevice device,
                                           const VkRenderingAreaInfoKHR* pRenderingAreaInfo,
-                                          VkExtent2D* pGranularity) {
-        // TODO: Implement
-    }
+                                          VkExtent2D* pGranularity) {}
     void vkGetDeviceImageSubresourceLayoutKHR(const uint8_t* snapshotTraceBegin,
                                               size_t snapshotTraceBytes,
                                               android::base::BumpPool* pool, VkDevice device,
                                               const VkDeviceImageSubresourceInfoKHR* pInfo,
-                                              VkSubresourceLayout2KHR* pLayout) {
-        // TODO: Implement
-    }
+                                              VkSubresourceLayout2KHR* pLayout) {}
     void vkGetImageSubresourceLayout2KHR(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkDevice device, VkImage image,
                                          const VkImageSubresource2KHR* pSubresource,
-                                         VkSubresourceLayout2KHR* pLayout) {
-        // TODO: Implement
-    }
+                                         VkSubresourceLayout2KHR* pLayout) {}
 #endif
 #ifdef VK_ANDROID_native_buffer
     void vkGetSwapchainGrallocUsageANDROID(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkResult input_result, VkDevice device, VkFormat format,
-                                           VkImageUsageFlags imageUsage, int* grallocUsage) {
-        // TODO: Implement
-    }
+                                           VkImageUsageFlags imageUsage, int* grallocUsage) {}
     void vkAcquireImageANDROID(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, VkImage image, int nativeFenceFd,
-                               VkSemaphore semaphore, VkFence fence) {
-        // TODO: Implement
-    }
+                               VkSemaphore semaphore, VkFence fence) {}
     void vkQueueSignalReleaseImageANDROID(const uint8_t* snapshotTraceBegin,
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkResult input_result, VkQueue queue,
                                           uint32_t waitSemaphoreCount,
                                           const VkSemaphore* pWaitSemaphores, VkImage image,
-                                          int* pNativeFenceFd) {
-        // TODO: Implement
-    }
+                                          int* pNativeFenceFd) {}
     void vkGetSwapchainGrallocUsage2ANDROID(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkResult input_result, VkDevice device, VkFormat format, VkImageUsageFlags imageUsage,
         VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage,
-        uint64_t* grallocProducerUsage) {
-        // TODO: Implement
-    }
+        uint64_t* grallocProducerUsage) {}
 #endif
 #ifdef VK_EXT_transform_feedback
     void vkCmdBindTransformFeedbackBuffersEXT(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount,
-        const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes) {
-        // TODO: Implement
-    }
+        const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes) {}
     void vkCmdBeginTransformFeedbackEXT(const uint8_t* snapshotTraceBegin,
                                         size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                         VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer,
                                         uint32_t counterBufferCount,
                                         const VkBuffer* pCounterBuffers,
-                                        const VkDeviceSize* pCounterBufferOffsets) {
-        // TODO: Implement
-    }
+                                        const VkDeviceSize* pCounterBufferOffsets) {}
     void vkCmdEndTransformFeedbackEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                       uint32_t firstCounterBuffer, uint32_t counterBufferCount,
                                       const VkBuffer* pCounterBuffers,
-                                      const VkDeviceSize* pCounterBufferOffsets) {
-        // TODO: Implement
-    }
+                                      const VkDeviceSize* pCounterBufferOffsets) {}
     void vkCmdBeginQueryIndexedEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                    VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags,
-                                   uint32_t index) {
-        // TODO: Implement
-    }
+                                   uint32_t index) {}
     void vkCmdEndQueryIndexedEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                  android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                 VkQueryPool queryPool, uint32_t query, uint32_t index) {
-        // TODO: Implement
-    }
+                                 VkQueryPool queryPool, uint32_t query, uint32_t index) {}
     void vkCmdDrawIndirectByteCountEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                        uint32_t instanceCount, uint32_t firstInstance,
                                        VkBuffer counterBuffer, VkDeviceSize counterBufferOffset,
-                                       uint32_t counterOffset, uint32_t vertexStride) {
-        // TODO: Implement
-    }
+                                       uint32_t counterOffset, uint32_t vertexStride) {}
 #endif
 #ifdef VK_AMD_gpu_shader_half_float
 #endif
@@ -2441,127 +1932,87 @@
                                               size_t snapshotTraceBytes,
                                               android::base::BumpPool* pool, VkResult input_result,
                                               VkPhysicalDevice physicalDevice, uint32_t* pToolCount,
-                                              VkPhysicalDeviceToolProperties* pToolProperties) {
-        // TODO: Implement
-    }
+                                              VkPhysicalDeviceToolProperties* pToolProperties) {}
 #endif
 #ifdef VK_EXT_provoking_vertex
 #endif
 #ifdef VK_EXT_line_rasterization
     void vkCmdSetLineStippleEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
-        // TODO: Implement
-    }
+                                uint32_t lineStippleFactor, uint16_t lineStipplePattern) {}
 #endif
 #ifdef VK_EXT_index_type_uint8
 #endif
 #ifdef VK_EXT_extended_dynamic_state
     void vkCmdSetCullModeEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                             VkCullModeFlags cullMode) {
-        // TODO: Implement
-    }
+                             VkCullModeFlags cullMode) {}
     void vkCmdSetFrontFaceEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                              VkFrontFace frontFace) {
-        // TODO: Implement
-    }
+                              VkFrontFace frontFace) {}
     void vkCmdSetPrimitiveTopologyEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                      VkPrimitiveTopology primitiveTopology) {
-        // TODO: Implement
-    }
+                                      VkPrimitiveTopology primitiveTopology) {}
     void vkCmdSetViewportWithCountEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                      uint32_t viewportCount, const VkViewport* pViewports) {
-        // TODO: Implement
-    }
+                                      uint32_t viewportCount, const VkViewport* pViewports) {}
     void vkCmdSetScissorWithCountEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                     uint32_t scissorCount, const VkRect2D* pScissors) {
-        // TODO: Implement
-    }
+                                     uint32_t scissorCount, const VkRect2D* pScissors) {}
     void vkCmdBindVertexBuffers2EXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                                     uint32_t firstBinding, uint32_t bindingCount,
                                     const VkBuffer* pBuffers, const VkDeviceSize* pOffsets,
-                                    const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
-        // TODO: Implement
-    }
+                                    const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {}
     void vkCmdSetDepthTestEnableEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                    VkBool32 depthTestEnable) {
-        // TODO: Implement
-    }
+                                    VkBool32 depthTestEnable) {}
     void vkCmdSetDepthWriteEnableEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                     VkBool32 depthWriteEnable) {
-        // TODO: Implement
-    }
+                                     VkBool32 depthWriteEnable) {}
     void vkCmdSetDepthCompareOpEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                   VkCompareOp depthCompareOp) {
-        // TODO: Implement
-    }
+                                   VkCompareOp depthCompareOp) {}
     void vkCmdSetDepthBoundsTestEnableEXT(const uint8_t* snapshotTraceBegin,
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkCommandBuffer commandBuffer,
-                                          VkBool32 depthBoundsTestEnable) {
-        // TODO: Implement
-    }
+                                          VkBool32 depthBoundsTestEnable) {}
     void vkCmdSetStencilTestEnableEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                      VkBool32 stencilTestEnable) {
-        // TODO: Implement
-    }
+                                      VkBool32 stencilTestEnable) {}
     void vkCmdSetStencilOpEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                               android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
                               VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp,
-                              VkStencilOp depthFailOp, VkCompareOp compareOp) {
-        // TODO: Implement
-    }
+                              VkStencilOp depthFailOp, VkCompareOp compareOp) {}
 #endif
 #ifdef VK_EXT_host_image_copy
     void vkCopyMemoryToImageEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device,
-                                const VkCopyMemoryToImageInfoEXT* pCopyMemoryToImageInfo) {
-        // TODO: Implement
-    }
+                                const VkCopyMemoryToImageInfoEXT* pCopyMemoryToImageInfo) {}
     void vkCopyImageToMemoryEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device,
-                                const VkCopyImageToMemoryInfoEXT* pCopyImageToMemoryInfo) {
-        // TODO: Implement
-    }
+                                const VkCopyImageToMemoryInfoEXT* pCopyImageToMemoryInfo) {}
     void vkCopyImageToImageEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device,
-                               const VkCopyImageToImageInfoEXT* pCopyImageToImageInfo) {
-        // TODO: Implement
-    }
+                               const VkCopyImageToImageInfoEXT* pCopyImageToImageInfo) {}
     void vkTransitionImageLayoutEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkResult input_result,
                                     VkDevice device, uint32_t transitionCount,
-                                    const VkHostImageLayoutTransitionInfoEXT* pTransitions) {
-        // TODO: Implement
-    }
+                                    const VkHostImageLayoutTransitionInfoEXT* pTransitions) {}
     void vkGetImageSubresourceLayout2EXT(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkDevice device, VkImage image,
                                          const VkImageSubresource2KHR* pSubresource,
-                                         VkSubresourceLayout2KHR* pLayout) {
-        // TODO: Implement
-    }
+                                         VkSubresourceLayout2KHR* pLayout) {}
 #endif
 #ifdef VK_EXT_swapchain_maintenance1
     void vkReleaseSwapchainImagesEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkResult input_result,
                                      VkDevice device,
-                                     const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) {
-        // TODO: Implement
-    }
+                                     const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) {}
 #endif
 #ifdef VK_EXT_shader_demote_to_helper_invocation
 #endif
@@ -2576,27 +2027,19 @@
                                     android::base::BumpPool* pool, VkResult input_result,
                                     VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo,
                                     const VkAllocationCallbacks* pAllocator,
-                                    VkPrivateDataSlot* pPrivateDataSlot) {
-        // TODO: Implement
-    }
+                                    VkPrivateDataSlot* pPrivateDataSlot) {}
     void vkDestroyPrivateDataSlotEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                      android::base::BumpPool* pool, VkDevice device,
                                      VkPrivateDataSlot privateDataSlot,
-                                     const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
-    }
+                                     const VkAllocationCallbacks* pAllocator) {}
     void vkSetPrivateDataEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                              VkObjectType objectType, uint64_t objectHandle,
-                             VkPrivateDataSlot privateDataSlot, uint64_t data) {
-        // TODO: Implement
-    }
+                             VkPrivateDataSlot privateDataSlot, uint64_t data) {}
     void vkGetPrivateDataEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                              android::base::BumpPool* pool, VkDevice device,
                              VkObjectType objectType, uint64_t objectHandle,
-                             VkPrivateDataSlot privateDataSlot, uint64_t* pData) {
-        // TODO: Implement
-    }
+                             VkPrivateDataSlot privateDataSlot, uint64_t* pData) {}
 #endif
 #ifdef VK_EXT_pipeline_creation_cache_control
 #endif
@@ -2615,39 +2058,28 @@
 #ifdef VK_EXT_extended_dynamic_state2
     void vkCmdSetPatchControlPointsEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                       uint32_t patchControlPoints) {
-        // TODO: Implement
-    }
+                                       uint32_t patchControlPoints) {}
     void vkCmdSetRasterizerDiscardEnableEXT(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool,
                                             VkCommandBuffer commandBuffer,
-                                            VkBool32 rasterizerDiscardEnable) {
-        // TODO: Implement
-    }
+                                            VkBool32 rasterizerDiscardEnable) {}
     void vkCmdSetDepthBiasEnableEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                    VkBool32 depthBiasEnable) {
-        // TODO: Implement
-    }
+                                    VkBool32 depthBiasEnable) {}
     void vkCmdSetLogicOpEXT(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                             android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                            VkLogicOp logicOp) {
-        // TODO: Implement
-    }
+                            VkLogicOp logicOp) {}
     void vkCmdSetPrimitiveRestartEnableEXT(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkCommandBuffer commandBuffer,
-                                           VkBool32 primitiveRestartEnable) {
-        // TODO: Implement
-    }
+                                           VkBool32 primitiveRestartEnable) {}
 #endif
 #ifdef VK_GOOGLE_gfxstream
     void vkMapMemoryIntoAddressSpaceGOOGLE(const uint8_t* snapshotTraceBegin,
                                            size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                            VkResult input_result, VkDevice device,
                                            VkDeviceMemory memory, uint64_t* pAddress) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // memory modify
         auto apiHandle = mReconstruction.createApiInfo();
@@ -2666,31 +2098,21 @@
         uint32_t bufferInfoCount, uint32_t bufferViewCount, const uint32_t* pImageInfoEntryIndices,
         const uint32_t* pBufferInfoEntryIndices, const uint32_t* pBufferViewEntryIndices,
         const VkDescriptorImageInfo* pImageInfos, const VkDescriptorBufferInfo* pBufferInfos,
-        const VkBufferView* pBufferViews) {
-        // TODO: Implement
-    }
+        const VkBufferView* pBufferViews) {}
     void vkBeginCommandBufferAsyncGOOGLE(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkCommandBuffer commandBuffer,
-                                         const VkCommandBufferBeginInfo* pBeginInfo) {
-        // TODO: Implement
-    }
+                                         const VkCommandBufferBeginInfo* pBeginInfo) {}
     void vkEndCommandBufferAsyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool,
-                                       VkCommandBuffer commandBuffer) {
-        // TODO: Implement
-    }
+                                       VkCommandBuffer commandBuffer) {}
     void vkResetCommandBufferAsyncGOOGLE(const uint8_t* snapshotTraceBegin,
                                          size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                          VkCommandBuffer commandBuffer,
-                                         VkCommandBufferResetFlags flags) {
-        // TODO: Implement
-    }
+                                         VkCommandBufferResetFlags flags) {}
     void vkCommandBufferHostSyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkCommandBuffer commandBuffer,
-                                       uint32_t needHostSync, uint32_t sequenceNumber) {
-        // TODO: Implement
-    }
+                                       uint32_t needHostSync, uint32_t sequenceNumber) {}
     void vkCreateImageWithRequirementsGOOGLE(const uint8_t* snapshotTraceBegin,
                                              size_t snapshotTraceBytes,
                                              android::base::BumpPool* pool, VkResult input_result,
@@ -2698,7 +2120,6 @@
                                              const VkAllocationCallbacks* pAllocator,
                                              VkImage* pImage,
                                              VkMemoryRequirements* pMemoryRequirements) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pImage create
         mReconstruction.addHandles((const uint64_t*)pImage, 1);
@@ -2717,7 +2138,6 @@
         VkResult input_result, VkDevice device, const VkBufferCreateInfo* pCreateInfo,
         const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer,
         VkMemoryRequirements* pMemoryRequirements) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // pBuffer create
         mReconstruction.addHandles((const uint64_t*)pBuffer, 1);
@@ -2735,57 +2155,40 @@
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkResult input_result, VkDevice device,
                                           VkDeviceMemory memory, uint64_t* pAddress,
-                                          uint64_t* pSize, uint64_t* pHostmemId) {
-        // TODO: Implement
-    }
+                                          uint64_t* pSize, uint64_t* pHostmemId) {}
     void vkFreeMemorySyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                 android::base::BumpPool* pool, VkResult input_result,
                                 VkDevice device, VkDeviceMemory memory,
                                 const VkAllocationCallbacks* pAllocator) {
-        // TODO: Implement
         android::base::AutoLock lock(mLock);
         // memory destroy
         mReconstruction.removeHandles((const uint64_t*)(&memory), 1);
     }
     void vkQueueHostSyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkQueue queue, uint32_t needHostSync,
-                               uint32_t sequenceNumber) {
-        // TODO: Implement
-    }
+                               uint32_t sequenceNumber) {}
     void vkQueueSubmitAsyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                   android::base::BumpPool* pool, VkQueue queue,
                                   uint32_t submitCount, const VkSubmitInfo* pSubmits,
-                                  VkFence fence) {
-        // TODO: Implement
-    }
+                                  VkFence fence) {}
     void vkQueueWaitIdleAsyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
-                                    android::base::BumpPool* pool, VkQueue queue) {
-        // TODO: Implement
-    }
+                                    android::base::BumpPool* pool, VkQueue queue) {}
     void vkQueueBindSparseAsyncGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkQueue queue,
                                       uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo,
-                                      VkFence fence) {
-        // TODO: Implement
-    }
+                                      VkFence fence) {}
     void vkGetLinearImageLayoutGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                       android::base::BumpPool* pool, VkDevice device,
                                       VkFormat format, VkDeviceSize* pOffset,
-                                      VkDeviceSize* pRowPitchAlignment) {
-        // TODO: Implement
-    }
+                                      VkDeviceSize* pRowPitchAlignment) {}
     void vkGetLinearImageLayout2GOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                        android::base::BumpPool* pool, VkDevice device,
                                        const VkImageCreateInfo* pCreateInfo, VkDeviceSize* pOffset,
-                                       VkDeviceSize* pRowPitchAlignment) {
-        // TODO: Implement
-    }
+                                       VkDeviceSize* pRowPitchAlignment) {}
     void vkQueueFlushCommandsGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                     android::base::BumpPool* pool, VkQueue queue,
                                     VkCommandBuffer commandBuffer, VkDeviceSize dataSize,
-                                    const void* pData) {
-        // TODO: Implement
-    }
+                                    const void* pData) {}
     void vkQueueCommitDescriptorSetUpdatesGOOGLE(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
         VkQueue queue, uint32_t descriptorPoolCount, const VkDescriptorPool* pDescriptorPools,
@@ -2793,35 +2196,36 @@
         const uint64_t* pDescriptorSetPoolIds, const uint32_t* pDescriptorSetWhichPool,
         const uint32_t* pDescriptorSetPendingAllocation,
         const uint32_t* pDescriptorWriteStartingIndices, uint32_t pendingDescriptorWriteCount,
-        const VkWriteDescriptorSet* pPendingDescriptorWrites) {
-        // TODO: Implement
-    }
+        const VkWriteDescriptorSet* pPendingDescriptorWrites) {}
     void vkCollectDescriptorPoolIdsGOOGLE(const uint8_t* snapshotTraceBegin,
                                           size_t snapshotTraceBytes, android::base::BumpPool* pool,
                                           VkDevice device, VkDescriptorPool descriptorPool,
-                                          uint32_t* pPoolIdCount, uint64_t* pPoolIds) {
-        // TODO: Implement
-    }
+                                          uint32_t* pPoolIdCount, uint64_t* pPoolIds) {}
     void vkQueueSignalReleaseImageANDROIDAsyncGOOGLE(const uint8_t* snapshotTraceBegin,
                                                      size_t snapshotTraceBytes,
                                                      android::base::BumpPool* pool, VkQueue queue,
                                                      uint32_t waitSemaphoreCount,
                                                      const VkSemaphore* pWaitSemaphores,
-                                                     VkImage image) {
-        // TODO: Implement
-    }
+                                                     VkImage image) {}
     void vkQueueFlushCommandsFromAuxMemoryGOOGLE(const uint8_t* snapshotTraceBegin,
                                                  size_t snapshotTraceBytes,
                                                  android::base::BumpPool* pool, VkQueue queue,
                                                  VkCommandBuffer commandBuffer,
                                                  VkDeviceMemory deviceMemory,
-                                                 VkDeviceSize dataOffset, VkDeviceSize dataSize) {
-        // TODO: Implement
-    }
+                                                 VkDeviceSize dataOffset, VkDeviceSize dataSize) {}
     void vkGetBlobGOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                          android::base::BumpPool* pool, VkResult input_result, VkDevice device,
                          VkDeviceMemory memory) {
-        // TODO: Implement
+        android::base::AutoLock lock(mLock);
+        // memory modify
+        auto apiHandle = mReconstruction.createApiInfo();
+        auto apiInfo = mReconstruction.getApiInfo(apiHandle);
+        mReconstruction.setApiTrace(apiInfo, OP_vkGetBlobGOOGLE, snapshotTraceBegin,
+                                    snapshotTraceBytes);
+        for (uint32_t i = 0; i < 1; ++i) {
+            VkDeviceMemory boxed = unboxed_to_boxed_non_dispatchable_VkDeviceMemory((&memory)[i]);
+            mReconstruction.forEachHandleAddModifyApi((const uint64_t*)(&boxed), 1, apiHandle);
+        }
     }
     void vkUpdateDescriptorSetWithTemplateSized2GOOGLE(
         const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes, android::base::BumpPool* pool,
@@ -2831,15 +2235,11 @@
         const uint32_t* pImageInfoEntryIndices, const uint32_t* pBufferInfoEntryIndices,
         const uint32_t* pBufferViewEntryIndices, const VkDescriptorImageInfo* pImageInfos,
         const VkDescriptorBufferInfo* pBufferInfos, const VkBufferView* pBufferViews,
-        const uint8_t* pInlineUniformBlockData) {
-        // TODO: Implement
-    }
+        const uint8_t* pInlineUniformBlockData) {}
     void vkQueueSubmitAsync2GOOGLE(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                    android::base::BumpPool* pool, VkQueue queue,
                                    uint32_t submitCount, const VkSubmitInfo2* pSubmits,
-                                   VkFence fence) {
-        // TODO: Implement
-    }
+                                   VkFence fence) {}
 #endif
 #ifdef VK_EXT_load_store_op_none
 #endif
diff --git a/host/vulkan/VkDecoderSnapshotUtils.cpp b/host/vulkan/VkDecoderSnapshotUtils.cpp
new file mode 100644
index 0000000..7867ff4
--- /dev/null
+++ b/host/vulkan/VkDecoderSnapshotUtils.cpp
@@ -0,0 +1,404 @@
+// Copyright 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "vulkan/VkDecoderSnapshotUtils.h"
+
+#include "VkCommonOperations.h"
+
+namespace gfxstream {
+namespace vk {
+
+namespace {
+
+uint32_t GetMemoryType(const PhysicalDeviceInfo& physicalDevice,
+                       const VkMemoryRequirements& memoryRequirements,
+                       VkMemoryPropertyFlags memoryProperties) {
+    const auto& props = physicalDevice.memoryProperties;
+    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;
+    }
+    GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))
+        << "Cannot find memory type for snapshot save " << __func__ << " (" << __FILE__ << ":"
+        << __LINE__ << ")";
+}
+
+uint32_t bytes_per_pixel(VkFormat format) {
+    switch (format) {
+        case VK_FORMAT_R8_UNORM:
+        case VK_FORMAT_R8_SNORM:
+        case VK_FORMAT_R8_USCALED:
+        case VK_FORMAT_R8_SSCALED:
+        case VK_FORMAT_R8_UINT:
+        case VK_FORMAT_R8_SINT:
+        case VK_FORMAT_R8_SRGB:
+            return 1;
+        case VK_FORMAT_R8G8_UNORM:
+        case VK_FORMAT_R8G8_SNORM:
+        case VK_FORMAT_R8G8_USCALED:
+        case VK_FORMAT_R8G8_SSCALED:
+        case VK_FORMAT_R8G8_UINT:
+        case VK_FORMAT_R8G8_SINT:
+        case VK_FORMAT_R8G8_SRGB:
+            return 2;
+        case VK_FORMAT_R8G8B8_UNORM:
+        case VK_FORMAT_R8G8B8_SNORM:
+        case VK_FORMAT_R8G8B8_USCALED:
+        case VK_FORMAT_R8G8B8_SSCALED:
+        case VK_FORMAT_R8G8B8_UINT:
+        case VK_FORMAT_R8G8B8_SINT:
+        case VK_FORMAT_R8G8B8_SRGB:
+        case VK_FORMAT_B8G8R8_UNORM:
+        case VK_FORMAT_B8G8R8_SNORM:
+        case VK_FORMAT_B8G8R8_USCALED:
+        case VK_FORMAT_B8G8R8_SSCALED:
+        case VK_FORMAT_B8G8R8_UINT:
+        case VK_FORMAT_B8G8R8_SINT:
+        case VK_FORMAT_B8G8R8_SRGB:
+            return 3;
+        case VK_FORMAT_R8G8B8A8_UNORM:
+        case VK_FORMAT_R8G8B8A8_SNORM:
+        case VK_FORMAT_R8G8B8A8_USCALED:
+        case VK_FORMAT_R8G8B8A8_SSCALED:
+        case VK_FORMAT_R8G8B8A8_UINT:
+        case VK_FORMAT_R8G8B8A8_SINT:
+        case VK_FORMAT_R8G8B8A8_SRGB:
+        case VK_FORMAT_B8G8R8A8_UNORM:
+        case VK_FORMAT_B8G8R8A8_SNORM:
+        case VK_FORMAT_B8G8R8A8_USCALED:
+        case VK_FORMAT_B8G8R8A8_SSCALED:
+        case VK_FORMAT_B8G8R8A8_UINT:
+        case VK_FORMAT_B8G8R8A8_SINT:
+        case VK_FORMAT_B8G8R8A8_SRGB:
+        case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
+        case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
+        case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
+        case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
+        case VK_FORMAT_A8B8G8R8_UINT_PACK32:
+        case VK_FORMAT_A8B8G8R8_SINT_PACK32:
+        case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+            return 4;
+        default:
+            GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))
+                << "Unsupported VkFormat on snapshot save " << format << " " << __func__ << " ("
+                << __FILE__ << ":" << __LINE__ << ")";
+    }
+}
+
+VkExtent3D getMipmapExtent(VkExtent3D baseExtent, uint32_t mipLevel) {
+    return VkExtent3D{
+        .width = (baseExtent.width + (1 << mipLevel) - 1) >> mipLevel,
+        .height = (baseExtent.height + (1 << mipLevel) - 1) >> mipLevel,
+        .depth = baseExtent.depth,
+    };
+}
+
+}  // namespace
+
+#define _RUN_AND_CHECK(command)                                                             \
+    {                                                                                       \
+        if (command)                                                                        \
+            GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))                   \
+                << "Vulkan snapshot save failed at " << __func__ << " (" << __FILE__ << ":" \
+                << __LINE__ << ")";                                                         \
+    }
+
+void saveImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image,
+                      const ImageInfo* imageInfo) {
+    VkEmulation* vkEmulation = getGlobalVkEmulation();
+    VulkanDispatch* dispatch = vkEmulation->dvk;
+    const VkImageCreateInfo& imageCreateInfo = imageInfo->imageCreateInfoShallow;
+    VkCommandBufferAllocateInfo allocInfo{
+        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+        .commandPool = vkEmulation->commandPool,
+        .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+        .commandBufferCount = 1,
+    };
+    VkCommandBuffer commandBuffer;
+    _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo,
+                                                      &commandBuffer) != VK_SUCCESS);
+    VkFenceCreateInfo fenceCreateInfo{
+        .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+    };
+    VkFence fence;
+    _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence));
+    VkBufferCreateInfo bufferCreateInfo = {
+        .size = static_cast<VkDeviceSize>(
+            imageCreateInfo.extent.width * imageCreateInfo.extent.height *
+            imageCreateInfo.extent.depth * bytes_per_pixel(imageCreateInfo.format)),
+        .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+    };
+    VkBuffer readbackBuffer;
+    _RUN_AND_CHECK(
+        dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &readbackBuffer));
+
+    VkMemoryRequirements readbackBufferMemoryRequirements{};
+    dispatch->vkGetBufferMemoryRequirements(stateBlock->device, readbackBuffer,
+                                            &readbackBufferMemoryRequirements);
+
+    const auto readbackBufferMemoryType =
+        GetMemoryType(*stateBlock->physicalDeviceInfo, readbackBufferMemoryRequirements,
+                      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    // Staging memory
+    // TODO(b/323064243): reuse staging memory
+    VkMemoryAllocateInfo readbackBufferMemoryAllocateInfo = {
+        .allocationSize = readbackBufferMemoryRequirements.size,
+        .memoryTypeIndex = readbackBufferMemoryType,
+    };
+    VkDeviceMemory readbackMemory;
+    _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &readbackBufferMemoryAllocateInfo,
+                                              nullptr, &readbackMemory));
+    _RUN_AND_CHECK(
+        dispatch->vkBindBufferMemory(stateBlock->device, readbackBuffer, readbackMemory, 0));
+
+    void* mapped = nullptr;
+    _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, readbackMemory, 0, VK_WHOLE_SIZE,
+                                         VkMemoryMapFlags{}, &mapped));
+
+    for (uint32_t mipLevel = 0; mipLevel < imageInfo->imageCreateInfoShallow.mipLevels;
+         mipLevel++) {
+        for (uint32_t arrayLayer = 0; arrayLayer < imageInfo->imageCreateInfoShallow.arrayLayers;
+             arrayLayer++) {
+            // TODO(b/323064243): reuse command buffers
+            VkCommandBufferBeginInfo beginInfo{
+                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+            };
+            if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
+                GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))
+                    << "Failed to start command buffer on snapshot save";
+            }
+
+            // TODO(b/323059453): get the right aspect
+            VkExtent3D mipmapExtent = getMipmapExtent(imageCreateInfo.extent, mipLevel);
+            VkImageAspectFlags aspects = VK_IMAGE_ASPECT_COLOR_BIT;
+            VkImageLayout layoutBeforeSave = imageInfo->layout;
+            VkImageMemoryBarrier imgMemoryBarrier = {
+                .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+                .pNext = nullptr,
+                .srcAccessMask = static_cast<VkAccessFlags>(~VK_ACCESS_NONE_KHR),
+                .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+                .oldLayout = layoutBeforeSave,
+                .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .image = image,
+                .subresourceRange = VkImageSubresourceRange{.aspectMask = aspects,
+                                                            .baseMipLevel = mipLevel,
+                                                            .levelCount = 1,
+                                                            .baseArrayLayer = arrayLayer,
+                                                            .layerCount = 1}};
+
+            dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+                                           VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0,
+                                           nullptr, 1, &imgMemoryBarrier);
+
+            VkBufferImageCopy region{
+                .bufferOffset = 0,
+                .bufferRowLength = 0,
+                .bufferImageHeight = 0,
+                .imageSubresource = VkImageSubresourceLayers{.aspectMask = aspects,
+                                                             .mipLevel = mipLevel,
+                                                             .baseArrayLayer = arrayLayer,
+                                                             .layerCount = 1},
+                .imageOffset =
+                    VkOffset3D{
+                        .x = 0,
+                        .y = 0,
+                        .z = 0,
+                    },
+                .imageExtent = mipmapExtent,
+            };
+            dispatch->vkCmdCopyImageToBuffer(commandBuffer, image,
+                                             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuffer,
+                                             1, &region);
+
+            imgMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+            imgMemoryBarrier.newLayout = layoutBeforeSave;
+            imgMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+            imgMemoryBarrier.dstAccessMask = ~VK_ACCESS_NONE_KHR;
+
+            dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+                                           VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0,
+                                           nullptr, 1, &imgMemoryBarrier);
+            _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer));
+
+            // Execute the command to copy image
+            VkSubmitInfo submitInfo = {
+                .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+                .commandBufferCount = 1,
+                .pCommandBuffers = &commandBuffer,
+            };
+            _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence));
+            _RUN_AND_CHECK(
+                dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L));
+            _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence));
+            size_t bytes = mipmapExtent.width * mipmapExtent.height * mipmapExtent.depth *
+                           bytes_per_pixel(imageCreateInfo.format);
+            stream->putBe64(bytes);
+            stream->write(mapped, bytes);
+        }
+    }
+    dispatch->vkDestroyFence(stateBlock->device, fence, nullptr);
+    dispatch->vkUnmapMemory(stateBlock->device, readbackMemory);
+    dispatch->vkFreeMemory(stateBlock->device, readbackMemory, nullptr);
+}
+
+void loadImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image,
+                      const ImageInfo* imageInfo) {
+    VkEmulation* vkEmulation = getGlobalVkEmulation();
+    VulkanDispatch* dispatch = vkEmulation->dvk;
+    const VkImageCreateInfo& imageCreateInfo = imageInfo->imageCreateInfoShallow;
+    VkCommandBufferAllocateInfo allocInfo{
+        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+        .commandPool = vkEmulation->commandPool,
+        .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+        .commandBufferCount = 1,
+    };
+    VkCommandBuffer commandBuffer;
+    _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo,
+                                                      &commandBuffer) != VK_SUCCESS);
+    VkFenceCreateInfo fenceCreateInfo{
+        .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+    };
+    VkFence fence;
+    _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence));
+    VkBufferCreateInfo bufferCreateInfo = {
+        .size = static_cast<VkDeviceSize>(
+            imageCreateInfo.extent.width * imageCreateInfo.extent.height *
+            imageCreateInfo.extent.depth * bytes_per_pixel(imageCreateInfo.format)),
+        .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+    };
+    VkBuffer stagingBuffer;
+    _RUN_AND_CHECK(
+        dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &stagingBuffer));
+
+    VkMemoryRequirements stagingBufferMemoryRequirements{};
+    dispatch->vkGetBufferMemoryRequirements(stateBlock->device, stagingBuffer,
+                                            &stagingBufferMemoryRequirements);
+
+    const auto stagingBufferMemoryType =
+        GetMemoryType(*stateBlock->physicalDeviceInfo, stagingBufferMemoryRequirements,
+                      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    // Staging memory
+    // TODO(b/323064243): reuse staging memory
+    VkMemoryAllocateInfo stagingBufferMemoryAllocateInfo = {
+        .allocationSize = stagingBufferMemoryRequirements.size,
+        .memoryTypeIndex = stagingBufferMemoryType,
+    };
+    VkDeviceMemory stagingMemory;
+    _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &stagingBufferMemoryAllocateInfo,
+                                              nullptr, &stagingMemory));
+    _RUN_AND_CHECK(
+        dispatch->vkBindBufferMemory(stateBlock->device, stagingBuffer, stagingMemory, 0));
+
+    void* mapped = nullptr;
+    _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, stagingMemory, 0, VK_WHOLE_SIZE,
+                                         VkMemoryMapFlags{}, &mapped));
+
+    for (uint32_t mipLevel = 0; mipLevel < imageInfo->imageCreateInfoShallow.mipLevels;
+         mipLevel++) {
+        for (uint32_t arrayLayer = 0; arrayLayer < imageInfo->imageCreateInfoShallow.arrayLayers;
+             arrayLayer++) {
+            // TODO(b/323064243): reuse command buffers
+            VkCommandBufferBeginInfo beginInfo{
+                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+            };
+            if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
+                GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER))
+                    << "Failed to start command buffer on snapshot save";
+            }
+
+            VkExtent3D mipmapExtent = getMipmapExtent(imageCreateInfo.extent, mipLevel);
+            size_t bytes = stream->getBe64();
+            stream->read(mapped, bytes);
+
+            // TODO(b/323059453): get the right aspect
+            VkImageAspectFlags aspects = VK_IMAGE_ASPECT_COLOR_BIT;
+            VkImageMemoryBarrier imgMemoryBarrier = {
+                .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+                .pNext = nullptr,
+                .srcAccessMask = static_cast<VkAccessFlags>(~VK_ACCESS_NONE_KHR),
+                .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+                .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+                .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .image = image,
+                .subresourceRange = VkImageSubresourceRange{.aspectMask = aspects,
+                                                            .baseMipLevel = mipLevel,
+                                                            .levelCount = 1,
+                                                            .baseArrayLayer = arrayLayer,
+                                                            .layerCount = 1}};
+
+            dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+                                           VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0,
+                                           nullptr, 1, &imgMemoryBarrier);
+
+            VkBufferImageCopy region{
+                .bufferOffset = 0,
+                .bufferRowLength = 0,
+                .bufferImageHeight = 0,
+                .imageSubresource = VkImageSubresourceLayers{.aspectMask = aspects,
+                                                             .mipLevel = mipLevel,
+                                                             .baseArrayLayer = arrayLayer,
+                                                             .layerCount = 1},
+                .imageOffset =
+                    VkOffset3D{
+                        .x = 0,
+                        .y = 0,
+                        .z = 0,
+                    },
+                .imageExtent = mipmapExtent,
+            };
+            dispatch->vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, image,
+                                             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 1, &region);
+
+            imgMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+            imgMemoryBarrier.newLayout = imageInfo->layout;
+            imgMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+            imgMemoryBarrier.dstAccessMask = ~VK_ACCESS_NONE_KHR;
+
+            dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+                                           VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0,
+                                           nullptr, 1, &imgMemoryBarrier);
+            _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer));
+
+            // Execute the command to copy image
+            VkSubmitInfo submitInfo = {
+                .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+                .commandBufferCount = 1,
+                .pCommandBuffers = &commandBuffer,
+            };
+            _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence));
+            _RUN_AND_CHECK(
+                dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L));
+            _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence));
+        }
+    }
+    dispatch->vkDestroyFence(stateBlock->device, fence, nullptr);
+    dispatch->vkUnmapMemory(stateBlock->device, stagingMemory);
+    dispatch->vkFreeMemory(stateBlock->device, stagingMemory, nullptr);
+}
+
+}  // namespace vk
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/host/vulkan/VkDecoderSnapshotUtils.h b/host/vulkan/VkDecoderSnapshotUtils.h
new file mode 100644
index 0000000..7a626ec
--- /dev/null
+++ b/host/vulkan/VkDecoderSnapshotUtils.h
@@ -0,0 +1,33 @@
+// Copyright 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "vulkan/VkDecoderInternalStructs.h"
+
+namespace gfxstream {
+namespace vk {
+struct StateBlock {
+    VkPhysicalDevice physicalDevice;
+    const PhysicalDeviceInfo* physicalDeviceInfo;
+    VkDevice device;
+    VkQueue queue;
+    VkCommandPool commandPool;
+};
+void saveImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image,
+                      const ImageInfo* imageInfo);
+void loadImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image,
+                      const ImageInfo* imageInfo);
+}  // namespace vk
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/host/vulkan/meson.build b/host/vulkan/meson.build
index 81597b9..500826d 100644
--- a/host/vulkan/meson.build
+++ b/host/vulkan/meson.build
@@ -24,6 +24,7 @@
   'VkDecoder.cpp',
   'VkDecoderGlobalState.cpp',
   'VkDecoderSnapshot.cpp',
+  'VkDecoderSnapshotUtils.cpp',
   'VkFormatUtils.cpp',
   'VkReconstruction.cpp',
   'VulkanDispatch.cpp',
diff --git a/meson.build b/meson.build
index e6762c0..b499d59 100644
--- a/meson.build
+++ b/meson.build
@@ -4,7 +4,8 @@
 project('gfxstream', 'cpp', 'c',
          version : '0.1.2',
          license : 'MIT OR Apache-2.0',
-         default_options : ['cpp_std=gnu++17'])
+         default_options : ['cpp_std=gnu++17',
+                            'b_ndebug=if-release'])
 
 cc = meson.get_compiler('cpp')
 prog_python = import('python').find_installation('python3')
@@ -17,6 +18,7 @@
 
 with_guest = build.contains('guest') or build.contains('both')
 with_host = build.contains('host') or build.contains('both')
+with_guest_test = build.contains('guest-test')
 
 #========================#
 # Logging + error report #
@@ -34,10 +36,10 @@
 use_magma = decoders.contains('magma') and with_host
 use_composer = decoders.contains('composer') and with_host
 
-if with_guest
+if with_guest or with_guest_test
   subdir('guest')
 endif
-if with_host
+if with_host and not with_guest_test
   subdir('common')
   subdir('host')
 endif
diff --git a/meson_options.txt b/meson_options.txt
index b5032fe..05a5856 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -731,6 +731,7 @@
     'both',
     'guest',
     'host',
+    'guest-test',
   ],
   description : 'Which portions of gfxstream to build'
 )
diff --git a/subprojects/libdrm b/subprojects/libdrm
new file mode 120000
index 0000000..8e1635f
--- /dev/null
+++ b/subprojects/libdrm
@@ -0,0 +1 @@
+../../../../external/libdrm
\ No newline at end of file
diff --git a/third-party/fuchsia/README b/third-party/fuchsia/README
index e315413..e012b39 100644
--- a/third-party/fuchsia/README
+++ b/third-party/fuchsia/README
@@ -1 +1 @@
-These files come from fuchsia.googlesource.com. Once Magma is in the Fuchsia SDK (https://fxbug.dev/116007, https://fxbug.dev/111471), these files should be removed and the Fuchsia SDK added as a proper dependency (https://fxbug.dev/122678).
+These files come from fuchsia.googlesource.com. Once Magma is in the Fuchsia SDK (https://fxbug.dev/42067270, https://fxbug.dev/42062806), these files should be removed and the Fuchsia SDK added as a proper dependency (https://fxbug.dev/42073654).