Merge changes I8b7245aa,If228f644 into main

* changes:
  Fix meson build
  Revert^2 "Add snapshot support for vkBindImageMemory2"
diff --git a/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py b/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
index d40b95e..2a5ef47 100644
--- a/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
+++ b/codegen/vulkan/vulkan-docs-next/scripts/cereal/decodersnapshot.py
@@ -187,6 +187,38 @@
     "vkBindBufferMemory": VkObjectState("buffer", "VkReconstruction::BOUND_MEMORY"),
 }
 
+def api_special_implementation_vkBindImageMemory2(api, cgen):
+    childType = "VkImage"
+    parentType = "VkDeviceMemory"
+    childObj = "boxed_%s" % childType
+    parentObj = "boxed_%s" % parentType
+    cgen.stmt("android::base::AutoLock lock(mLock)")
+    cgen.beginFor("uint32_t i = 0", "i < bindInfoCount", "++i")
+    cgen.stmt("%s boxed_%s = unboxed_to_boxed_non_dispatchable_%s(pBindInfos[i].image)"
+              % (childType, childType, childType))
+    cgen.stmt("%s boxed_%s = unboxed_to_boxed_non_dispatchable_%s(pBindInfos[i].memory)"
+              % (parentType, parentType, parentType))
+    cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)&%s, %s, (uint64_t)(uintptr_t)%s, VkReconstruction::BOUND_MEMORY)" % \
+              (childObj, "1", parentObj))
+    cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)&%s, %s, (uint64_t)(uintptr_t)%s, VkReconstruction::BOUND_MEMORY)" % \
+              (childObj, "1", childObj))
+    cgen.endFor()
+
+    cgen.stmt("auto apiHandle = mReconstruction.createApiInfo()")
+    cgen.stmt("auto apiInfo = mReconstruction.getApiInfo(apiHandle)")
+    cgen.stmt("mReconstruction.setApiTrace(apiInfo, OP_%s, snapshotTraceBegin, snapshotTraceBytes)" % api.name)
+    cgen.line("// Note: the implementation does not work with bindInfoCount > 1");
+    cgen.beginFor("uint32_t i = 0", "i < bindInfoCount", "++i")
+    cgen.stmt("%s boxed_%s = unboxed_to_boxed_non_dispatchable_%s(pBindInfos[i].image)"
+              % (childType, childType, childType))
+    cgen.stmt(f"mReconstruction.forEachHandleAddApi((const uint64_t*)&{childObj}, {1}, apiHandle, VkReconstruction::BOUND_MEMORY)")
+    cgen.endFor()
+
+apiSpecialImplementation = {
+    "vkBindImageMemory2": api_special_implementation_vkBindImageMemory2,
+    "vkBindImageMemory2KHR": api_special_implementation_vkBindImageMemory2,
+}
+
 apiModifies = {
     "vkMapMemoryIntoAddressSpaceGOOGLE" : ["memory"],
     "vkGetBlobGOOGLE" : ["memory"],
@@ -240,6 +272,8 @@
 
 
 def emit_impl(typeInfo, api, cgen):
+    if api.name in apiSpecialImplementation:
+        apiSpecialImplementation[api.name](api, cgen)
     for p in api.parameters:
         if not (p.isHandleType):
             continue
diff --git a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
index eb6483d..370b48c 100644
--- a/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
+++ b/common/end2end/GfxstreamEnd2EndVkSnapshotImageTests.cpp
@@ -234,6 +234,89 @@
     SnapshotSaveAndLoad();
 }
 
+// Use vkBindImageMemory2 instead of vkBindImageMemory
+TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageViewDependency2) {
+    auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
+        VK_ASSERT(SetUpTypicalVkTestEnvironment());
+
+    const uint32_t width = 32;
+    const uint32_t height = 32;
+
+    const vkhpp::ImageCreateInfo imageCreateInfo = {
+        .pNext = nullptr,
+        .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;
+    ASSERT_THAT(image, IsValidHandle());
+
+    vkhpp::MemoryRequirements imageMemoryRequirements{};
+    device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
+
+    const uint32_t imageMemoryIndex = utils::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());
+
+    const vkhpp::BindImageMemoryInfo imageBindMemoryInfo = {
+        .pNext = nullptr,
+        .image = *image,
+        .memory = *imageMemory,
+        .memoryOffset = 0,
+    };
+
+    ASSERT_THAT(device->bindImageMemory2({imageBindMemoryInfo}), IsVkSuccess());
+
+    // b/331677615
+    // Create and delete a buffer handle right before creating image view.
+    // Gfxstream recycle handles. We trick the VkImageView handle to collide with
+    // a destroyed buffer handle and verify there is no bug snapshotting recycled
+    // handles.
+    const vkhpp::BufferCreateInfo bufferCreateInfo = {
+        .size = 1024,
+        .usage = vkhpp::BufferUsageFlagBits::eTransferSrc,
+    };
+    auto buffer = device->createBufferUnique(bufferCreateInfo).value;
+    ASSERT_THAT(buffer, IsValidHandle());
+    buffer.reset();
+
+    const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
+        .image = *image,
+        .viewType = vkhpp::ImageViewType::e2D,
+        .format = vkhpp::Format::eR8G8B8A8Unorm,
+        .subresourceRange =
+            {
+                .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
+                .baseMipLevel = 0,
+                .levelCount = 1,
+                .baseArrayLayer = 0,
+                .layerCount = 1,
+            },
+    };
+    auto imageView = device->createImageViewUnique(imageViewCreateInfo).value;
+    ASSERT_THAT(imageView, IsValidHandle());
+    // Make sure it doesn't crash on load
+    SnapshotSaveAndLoad();
+}
+
 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, MultiSampleImage) {
     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
         VK_ASSERT(SetUpTypicalVkTestEnvironment());
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index 9b0454b..014eda1 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -2347,6 +2347,17 @@
     VkResult on_vkBindImageMemory2(android::base::BumpPool* pool, VkDevice boxed_device,
                                    uint32_t bindInfoCount,
                                    const VkBindImageMemoryInfo* pBindInfos) {
+#ifdef GFXSTREAM_ENABLE_HOST_VK_SNAPSHOT
+        if (bindInfoCount > 1 && snapshotsEnabled()) {
+            if (mVerbosePrints) {
+                fprintf(stderr,
+                    "vkBindImageMemory2 with more than 1 bindInfoCount not supporting snapshot");
+            }
+            get_emugl_vm_operations().setSkipSnapshotSave(true);
+            get_emugl_vm_operations().setSkipSnapshotSaveReason(SNAPSHOT_SKIP_UNSUPPORTED_VK_API);
+        }
+#endif
+
         auto device = unbox_VkDevice(boxed_device);
         auto vk = dispatch_VkDevice(boxed_device);
         bool needEmulation = false;
diff --git a/host/vulkan/VkDecoderSnapshot.cpp b/host/vulkan/VkDecoderSnapshot.cpp
index bc0ea04..370c9a9 100644
--- a/host/vulkan/VkDecoderSnapshot.cpp
+++ b/host/vulkan/VkDecoderSnapshot.cpp
@@ -1628,7 +1628,30 @@
                              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) {}
+                            uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
+        android::base::AutoLock lock(mLock);
+        for (uint32_t i = 0; i < bindInfoCount; ++i) {
+            VkImage boxed_VkImage = unboxed_to_boxed_non_dispatchable_VkImage(pBindInfos[i].image);
+            VkDeviceMemory boxed_VkDeviceMemory =
+                unboxed_to_boxed_non_dispatchable_VkDeviceMemory(pBindInfos[i].memory);
+            mReconstruction.addHandleDependency((const uint64_t*)&boxed_VkImage, 1,
+                                                (uint64_t)(uintptr_t)boxed_VkDeviceMemory,
+                                                VkReconstruction::BOUND_MEMORY);
+            mReconstruction.addHandleDependency((const uint64_t*)&boxed_VkImage, 1,
+                                                (uint64_t)(uintptr_t)boxed_VkImage,
+                                                VkReconstruction::BOUND_MEMORY);
+        }
+        auto apiHandle = mReconstruction.createApiInfo();
+        auto apiInfo = mReconstruction.getApiInfo(apiHandle);
+        mReconstruction.setApiTrace(apiInfo, OP_vkBindImageMemory2, snapshotTraceBegin,
+                                    snapshotTraceBytes);
+        // Note: the implementation does not work with bindInfoCount > 1
+        for (uint32_t i = 0; i < bindInfoCount; ++i) {
+            VkImage boxed_VkImage = unboxed_to_boxed_non_dispatchable_VkImage(pBindInfos[i].image);
+            mReconstruction.forEachHandleAddApi((const uint64_t*)&boxed_VkImage, 1, apiHandle,
+                                                VkReconstruction::BOUND_MEMORY);
+        }
+    }
     void vkGetDeviceGroupPeerMemoryFeatures(const uint8_t* snapshotTraceBegin,
                                             size_t snapshotTraceBytes,
                                             android::base::BumpPool* pool, VkDevice device,
@@ -2711,7 +2734,30 @@
     void vkBindImageMemory2KHR(const uint8_t* snapshotTraceBegin, size_t snapshotTraceBytes,
                                android::base::BumpPool* pool, VkResult input_result,
                                VkDevice device, uint32_t bindInfoCount,
-                               const VkBindImageMemoryInfo* pBindInfos) {}
+                               const VkBindImageMemoryInfo* pBindInfos) {
+        android::base::AutoLock lock(mLock);
+        for (uint32_t i = 0; i < bindInfoCount; ++i) {
+            VkImage boxed_VkImage = unboxed_to_boxed_non_dispatchable_VkImage(pBindInfos[i].image);
+            VkDeviceMemory boxed_VkDeviceMemory =
+                unboxed_to_boxed_non_dispatchable_VkDeviceMemory(pBindInfos[i].memory);
+            mReconstruction.addHandleDependency((const uint64_t*)&boxed_VkImage, 1,
+                                                (uint64_t)(uintptr_t)boxed_VkDeviceMemory,
+                                                VkReconstruction::BOUND_MEMORY);
+            mReconstruction.addHandleDependency((const uint64_t*)&boxed_VkImage, 1,
+                                                (uint64_t)(uintptr_t)boxed_VkImage,
+                                                VkReconstruction::BOUND_MEMORY);
+        }
+        auto apiHandle = mReconstruction.createApiInfo();
+        auto apiInfo = mReconstruction.getApiInfo(apiHandle);
+        mReconstruction.setApiTrace(apiInfo, OP_vkBindImageMemory2KHR, snapshotTraceBegin,
+                                    snapshotTraceBytes);
+        // Note: the implementation does not work with bindInfoCount > 1
+        for (uint32_t i = 0; i < bindInfoCount; ++i) {
+            VkImage boxed_VkImage = unboxed_to_boxed_non_dispatchable_VkImage(pBindInfos[i].image);
+            mReconstruction.forEachHandleAddApi((const uint64_t*)&boxed_VkImage, 1, apiHandle,
+                                                VkReconstruction::BOUND_MEMORY);
+        }
+    }
 #endif
 #ifdef VK_KHR_maintenance3
     void vkGetDescriptorSetLayoutSupportKHR(const uint8_t* snapshotTraceBegin,