Implement metal_objects for MoltenVK

Using metal_objects extension for MoltenVK
to avoid using the deprecated and unsafe old API
and fix AHB and external memory related issues.

Bug: 333460957
Test: build and run with -use-host-vulkan, with VVL disabled
Change-Id: If1269359a7b4a7f8a4746c8ca698eb8838aebb9a
diff --git a/host/vulkan/VkAndroidNativeBuffer.cpp b/host/vulkan/VkAndroidNativeBuffer.cpp
index 0b74d00..a3601c1 100644
--- a/host/vulkan/VkAndroidNativeBuffer.cpp
+++ b/host/vulkan/VkAndroidNativeBuffer.cpp
@@ -183,6 +183,26 @@
             VK_EXT_MEMORY_HANDLE_TYPE_BIT,
         };
 
+#if defined(__APPLE__)
+        VkImportMetalTextureInfoEXT metalImageImport = {
+            VK_STRUCTURE_TYPE_IMPORT_METAL_TEXTURE_INFO_EXT};
+
+        if (emu->instanceSupportsMoltenVK) {
+            // Change handle type requested to mtltexture
+            extImageCi.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+
+            if (out->colorBufferHandle) {
+                // TODO(b/333460957): External memory is not properly supported on MoltenVK
+                // and although this works fine, it's not valid and causing validation layer issues
+                metalImageImport.plane = VK_IMAGE_ASPECT_PLANE_0_BIT;
+                metalImageImport.mtlTexture = getColorBufferMTLTexture(out->colorBufferHandle);
+
+                // Insert metalImageImport to the chain
+                vk_insert_struct(createImageCi, metalImageImport);
+            }
+        }
+#endif
+
         vk_insert_struct(createImageCi, extImageCi);
 
         VkResult createResult = vk->vkCreateImage(device, &createImageCi, pAllocator, &out->image);
diff --git a/host/vulkan/VkCommonOperations.cpp b/host/vulkan/VkCommonOperations.cpp
index aad921f..f8c48cf 100644
--- a/host/vulkan/VkCommonOperations.cpp
+++ b/host/vulkan/VkCommonOperations.cpp
@@ -312,6 +312,12 @@
         0,
         VK_EXT_MEMORY_HANDLE_TYPE_BIT,
     };
+#if defined(__APPLE__)
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        // Using a different handle type when in MoltenVK mode
+        extInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+    }
+#endif
 
     VkPhysicalDeviceImageFormatInfo2 formatInfo2 = {
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
@@ -347,6 +353,12 @@
 
     if (res != VK_SUCCESS) {
         if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
+            VK_COMMON_VERBOSE("Not Supported: %s %s %s %s",
+                                string_VkFormat(info->format),
+                                string_VkImageType(info->type),
+                                string_VkImageTiling(info->tiling),
+                                string_VkImageUsageFlagBits((VkImageUsageFlagBits)info->usageFlags));
+
             info->supported = false;
             return true;
         } else {
@@ -372,7 +384,15 @@
     VkExternalMemoryHandleTypeFlags compatibleHandleTypes =
         outExternalProps.externalMemoryProperties.compatibleHandleTypes;
 
-    info->supportsExternalMemory = (VK_EXT_MEMORY_HANDLE_TYPE_BIT & compatibleHandleTypes) &&
+    VkExternalMemoryHandleTypeFlagBits handleTypeNeeded = VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+#if defined(__APPLE__)
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        // Using a different handle type when in MoltenVK mode
+        handleTypeNeeded = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+    }
+#endif
+
+    info->supportsExternalMemory = (handleTypeNeeded & compatibleHandleTypes) &&
                                    (VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT & featureFlags) &&
                                    (VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT & featureFlags);
 
@@ -551,6 +571,9 @@
 #elif defined(__QNX__)
         VK_QNX_EXTERNAL_MEMORY_SCREEN_BUFFER_EXTENSION_NAME,
         VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
+#elif defined(__APPLE__)
+        // VK_EXT_metal_objects will be added if host MoltenVK is enabled,
+        // otherwise VK_KHR_external_memory_fd will be used
 #else
         VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
 #endif
@@ -585,17 +608,11 @@
         extensionsSupported(instanceExts, externalMemoryInstanceExtNames);
     bool externalSemaphoreCapabilitiesSupported =
         extensionsSupported(instanceExts, externalSemaphoreInstanceExtNames);
-    bool surfaceSupported =
-        extensionsSupported(instanceExts, surfaceInstanceExtNames);
+    bool surfaceSupported = extensionsSupported(instanceExts, surfaceInstanceExtNames);
 #if defined(__APPLE__) && defined(VK_MVK_moltenvk)
     bool moltenVKSupported = (vk->vkGetMTLTextureMVK != nullptr) &&
                              (vk->vkSetMTLTextureMVK != nullptr) &&
                              extensionsSupported(instanceExts, moltenVkInstanceExtNames);
-    if (moltenVKSupported) {
-        // We don't need both moltenVK and external memory. Disable
-        // external memory if moltenVK is supported.
-        externalMemoryCapabilitiesSupported = false;
-    }
 #endif
 
     VkInstanceCreateInfo instCi = {
@@ -635,6 +652,7 @@
 
 #if defined(__APPLE__) && defined(VK_MVK_moltenvk)
     if (moltenVKSupported) {
+        VK_COMMON_LOG("MoltenVK is supported, enabling Vulkan portability.");
         instCi.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
         for (auto extension : moltenVkInstanceExtNames) {
             selectedInstanceExtensionNames.emplace(extension);
@@ -642,7 +660,8 @@
     }
 #endif
 
-    std::vector<const char*> selectedInstanceExtensionNames_(selectedInstanceExtensionNames.begin(), selectedInstanceExtensionNames.end());
+    std::vector<const char*> selectedInstanceExtensionNames_(selectedInstanceExtensionNames.begin(),
+                                                             selectedInstanceExtensionNames.end());
     instCi.enabledExtensionCount = static_cast<uint32_t>(selectedInstanceExtensionNames_.size());
     instCi.ppEnabledExtensionNames = selectedInstanceExtensionNames_.data();
 
@@ -755,7 +774,13 @@
             VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(ABORT_REASON_OTHER,
                                                  "Cannot find vkGetMTLTextureMVK.");
         }
-        VK_COMMON_LOG("Instance supports VK_MVK_moltenvk.");
+
+        // Using metal_objects extension on MacOS when moltenVK is used.
+        externalMemoryDeviceExtNames.push_back(VK_EXT_METAL_OBJECTS_EXTENSION_NAME);
+    }
+    else {
+        // When MoltenVK is not used(e.g. SwiftShader), use memory fd extension for external memory.
+        externalMemoryDeviceExtNames.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
     }
 #endif
 
@@ -775,7 +800,8 @@
     for (int i = 0; i < physdevCount; ++i) {
         ivk->vkGetPhysicalDeviceProperties(physdevs[i], &deviceInfos[i].physdevProps);
 
-        VK_COMMON_VERBOSE("Considering Vulkan physical device %d : %s", i, deviceInfos[i].physdevProps.deviceName);
+        VK_COMMON_VERBOSE("Considering Vulkan physical device %d : %s", i,
+                          deviceInfos[i].physdevProps.deviceName);
 
         // It's easier to figure out the staging buffer along with
         // external memories if we have the memory properties on hand.
@@ -793,7 +819,7 @@
         deviceInfos[i].supportsExternalMemoryExport = false;
         deviceInfos[i].glInteropSupported = 0;  // set later
 
-#if defined(__APPLE__) && defined(VK_MVK_moltenvk)
+#if defined(__APPLE__)
         if (moltenVKSupported && !extensionsSupported(deviceExts, moltenVkDeviceExtNames)) {
             VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(
                 ABORT_REASON_OTHER,
@@ -1070,7 +1096,7 @@
         selectedDeviceExtensionNames_.emplace(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
     }
 
-#if defined(__APPLE__) && defined(VK_MVK_moltenvk)
+#if defined(__APPLE__)
     if (moltenVKSupported) {
         for (auto extension : moltenVkDeviceExtNames) {
             selectedDeviceExtensionNames_.emplace(extension);
@@ -1163,17 +1189,32 @@
     }
     if (sVkEmulation->deviceInfo.supportsExternalMemoryExport) {
 #ifdef _WIN32
+        // Use vkGetMemoryWin32HandleKHR
         sVkEmulation->deviceInfo.getMemoryHandleFunc =
             reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>(
                 dvk->vkGetDeviceProcAddr(sVkEmulation->device, "vkGetMemoryWin32HandleKHR"));
-#else
-        sVkEmulation->deviceInfo.getMemoryHandleFunc = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
-            dvk->vkGetDeviceProcAddr(sVkEmulation->device, "vkGetMemoryFdKHR"));
-#endif
         if (!sVkEmulation->deviceInfo.getMemoryHandleFunc) {
             VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(ABORT_REASON_OTHER,
-                                                 "Cannot find vkGetMemory(Fd|Win32Handle)KHR");
+                                                 "Cannot find vkGetMemoryWin32HandleKHR");
         }
+#else
+        if (sVkEmulation->instanceSupportsMoltenVK) {
+            // vkExportMetalObjectsEXT will be used directly
+            sVkEmulation->deviceInfo.getMemoryHandleFunc = nullptr;
+            if (!dvk->vkGetDeviceProcAddr(sVkEmulation->device, "vkExportMetalObjectsEXT")) {
+                VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(ABORT_REASON_OTHER,
+                                                     "Cannot find vkExportMetalObjectsEXT");
+            }
+        } else {
+            // Use vkGetMemoryFdKHR
+            sVkEmulation->deviceInfo.getMemoryHandleFunc = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
+                dvk->vkGetDeviceProcAddr(sVkEmulation->device, "vkGetMemoryFdKHR"));
+            if (!sVkEmulation->deviceInfo.getMemoryHandleFunc) {
+                VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(ABORT_REASON_OTHER,
+                                                     "Cannot find vkGetMemoryFdKHR");
+            }
+        }
+#endif
     }
 
     VK_COMMON_VERBOSE("Vulkan logical device created and extension functions obtained.");
@@ -1440,6 +1481,34 @@
     return std::make_unique<gfxstream::DisplaySurface>(width, height, std::move(surfaceVk));
 }
 
+#ifdef __APPLE__
+static MTLBufferRef getMtlBufferFromVkDeviceMemory(VulkanDispatch* vk, VkDeviceMemory memory) {
+    VkExportMetalBufferInfoEXT exportMetalBufferInfo = {
+        VK_STRUCTURE_TYPE_EXPORT_METAL_BUFFER_INFO_EXT, nullptr, memory, VK_NULL_HANDLE};
+    VkExportMetalObjectsInfoEXT metalObjectsInfo = {VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT,
+                                                    &exportMetalBufferInfo};
+    vk->vkExportMetalObjectsEXT(sVkEmulation->device, &metalObjectsInfo);
+
+    return exportMetalBufferInfo.mtlBuffer;
+}
+
+static MTLTextureRef getMtlTextureFromVkImage(VulkanDispatch* vk, VkImage image) {
+    VkExportMetalTextureInfoEXT exportMetalTextureInfo = {
+        VK_STRUCTURE_TYPE_EXPORT_METAL_TEXTURE_INFO_EXT,
+        nullptr,
+        image,
+        VK_NULL_HANDLE,
+        VK_NULL_HANDLE,
+        VK_IMAGE_ASPECT_PLANE_0_BIT,
+        VK_NULL_HANDLE};
+    VkExportMetalObjectsInfoEXT metalObjectsInfo = {VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT,
+                                                    &exportMetalTextureInfo};
+    vk->vkExportMetalObjectsEXT(sVkEmulation->device, &metalObjectsInfo);
+
+    return exportMetalTextureInfo.mtlTexture;
+}
+#endif
+
 // Precondition: sVkEmulation has valid device support info
 bool allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalMemoryInfo* info,
                          bool actuallyExternal, Optional<uint64_t> deviceAlignment,
@@ -1467,7 +1536,26 @@
 
     auto allocInfoChain = vk_make_chain_iterator(&allocInfo);
 
+#ifdef __APPLE__
+    // On MoltenVK, use metal objects to export metal handles
+    VkExportMetalObjectCreateInfoEXT metalBufferExport = {
+        VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT,
+        nullptr,
+        VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT
+    };
+#endif
+
     if (sVkEmulation->deviceInfo.supportsExternalMemoryExport && actuallyExternal) {
+#ifdef __APPLE__
+        if (sVkEmulation->instanceSupportsMoltenVK) {
+            // Change handle type to metal buffers
+            exportAi.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
+
+            // Append metal buffer export for getting metal handles for the allocation
+            vk_append_struct(&allocInfoChain, &metalBufferExport);
+        }
+#endif
+
         vk_append_struct(&allocInfoChain, &exportAi);
     }
 
@@ -1548,6 +1636,7 @@
     }
 
     VkResult exportRes = VK_SUCCESS;
+    bool validHandle = false;
 #ifdef _WIN32
     VkMemoryGetWin32HandleInfoKHR getWin32HandleInfo = {
         VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
@@ -1557,19 +1646,35 @@
     };
     exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc(
         sVkEmulation->device, &getWin32HandleInfo, &info->externalHandle);
+    validHandle = (VK_EXT_MEMORY_HANDLE_INVALID != info->externalHandle);
 #elif !defined(__QNX__)
-    VkMemoryGetFdInfoKHR getFdInfo = {
-        VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
-        0,
-        info->memory,
-        VK_EXT_MEMORY_HANDLE_TYPE_BIT,
-    };
-    exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc(sVkEmulation->device, &getFdInfo,
-                                                             &info->externalHandle);
+#ifdef __APPLE__
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        info->externalMetalHandle = getMtlBufferFromVkDeviceMemory(vk, info->memory);
+        validHandle = (nullptr != info->externalMetalHandle);
+        if (validHandle) {
+            CFRetain(info->externalMetalHandle);
+            exportRes = VK_SUCCESS;
+        } else {
+            exportRes = VK_ERROR_INVALID_EXTERNAL_HANDLE;
+        }
+    } else
+#endif
+    {
+        VkMemoryGetFdInfoKHR getFdInfo = {
+            VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+            0,
+            info->memory,
+            VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+        };
+        exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc(sVkEmulation->device, &getFdInfo,
+                                                                &info->externalHandle);
+        validHandle = (VK_EXT_MEMORY_HANDLE_INVALID != info->externalHandle);
+    }
 #endif
 
-    if (exportRes != VK_SUCCESS || VK_EXT_MEMORY_HANDLE_INVALID == info->externalHandle) {
-        VK_COMMON_VERBOSE("allocExternalMemory: Failed to get external memory, native handle: %s", string_VkResult(exportRes));
+    if (exportRes != VK_SUCCESS || !validHandle) {
+        VK_COMMON_VERBOSE("allocExternalMemory: Failed to get external memory, result: %s", string_VkResult(exportRes));
         return false;
     }
 
@@ -1606,10 +1711,17 @@
 #endif
         info->externalHandle = VK_EXT_MEMORY_HANDLE_INVALID;
     }
+
+#if defined(__APPLE__)
+    if (info->externalMetalHandle) {
+        CFRelease(info->externalMetalHandle);
+    }
+#endif
 }
 
 bool importExternalMemory(VulkanDispatch* vk, VkDevice targetDevice,
                           const VkEmulation::ExternalMemoryInfo* info, VkDeviceMemory* out) {
+    const void* importInfoPtr = nullptr;
 #ifdef _WIN32
     VkImportMemoryWin32HandleInfoKHR importInfo = {
         VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
@@ -1618,23 +1730,41 @@
         info->externalHandle,
         0,
     };
+    importInfoPtr = &importInfo;
 #elif defined(__QNX__)
     VkImportScreenBufferInfoQNX importInfo = {
         VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX,
         NULL,
         info->externalHandle,
     };
+    importInfoPtr = &importInfo;
 #else
-    VkImportMemoryFdInfoKHR importInfo = {
+    VkImportMemoryFdInfoKHR importInfoFd = {
         VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
         0,
         VK_EXT_MEMORY_HANDLE_TYPE_BIT,
-        dupExternalMemory(info->externalHandle),
+        VK_EXT_MEMORY_HANDLE_INVALID,
     };
+
+#ifdef __APPLE__
+    VkImportMetalBufferInfoEXT importInfoMetalBuffer = {
+        VK_STRUCTURE_TYPE_IMPORT_METAL_BUFFER_INFO_EXT,
+        0,
+        nullptr,
+    };
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        importInfoMetalBuffer.mtlBuffer = info->externalMetalHandle;
+        importInfoPtr = &importInfoMetalBuffer;
+    } else
+#endif
+    {
+        importInfoFd.fd = dupExternalMemory(info->externalHandle);
+        importInfoPtr = &importInfoFd;
+    }
 #endif
     VkMemoryAllocateInfo allocInfo = {
         VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-        &importInfo,
+        importInfoPtr,
         info->size,
         info->typeIndex,
     };
@@ -1659,6 +1789,7 @@
         VK_NULL_HANDLE,
     };
 
+    const void *importInfoPtr = nullptr;
 #ifdef _WIN32
     VkImportMemoryWin32HandleInfoKHR importInfo = {
         VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
@@ -1667,23 +1798,41 @@
         info->externalHandle,
         0,
     };
+    importInfoPtr = &importInfo;
 #elif defined(__QNX__)
     VkImportScreenBufferInfoQNX importInfo = {
         VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX,
         &dedicatedInfo,
         info->externalHandle,
     };
+    importInfoPtr = &importInfo;
 #else
-    VkImportMemoryFdInfoKHR importInfo = {
+    VkImportMemoryFdInfoKHR importInfoFd = {
         VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
         &dedicatedInfo,
         VK_EXT_MEMORY_HANDLE_TYPE_BIT,
-        dupExternalMemory(info->externalHandle),
+        -1,
     };
+
+#ifdef __APPLE__
+    VkImportMetalBufferInfoEXT importInfoMetalBuffer = {
+        VK_STRUCTURE_TYPE_IMPORT_METAL_BUFFER_INFO_EXT,
+        &dedicatedInfo,
+        nullptr,
+    };
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        importInfoMetalBuffer.mtlBuffer = info->externalMetalHandle;
+        importInfoPtr = &importInfoMetalBuffer;
+    } else
+#endif
+    {
+        importInfoFd.fd = dupExternalMemory(info->externalHandle);
+        importInfoPtr = &importInfoFd;
+    }
 #endif
     VkMemoryAllocateInfo allocInfo = {
         VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-        &importInfo,
+        importInfoPtr,
         info->size,
         info->typeIndex,
     };
@@ -2055,6 +2204,18 @@
         0,
         VK_EXT_MEMORY_HANDLE_TYPE_BIT,
     };
+#if defined(__APPLE__)
+    VkExportMetalObjectCreateInfoEXT metalImageExportCI = {
+        VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT, nullptr,
+        VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT};
+
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        // Using a different handle type when in MoltenVK mode
+        extImageCi.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+
+        extImageCi.pNext = &metalImageExportCI;
+    }
+#endif
 
     VkExternalMemoryImageCreateInfo* extImageCiPtr = nullptr;
 
@@ -2194,13 +2355,10 @@
         return false;
     }
 
-#if defined(VK_MVK_moltenvk) && defined(__APPLE__)
+#if defined(__APPLE__)
     if (sVkEmulation->instanceSupportsMoltenVK) {
-        sVkEmulation->getMTLTextureFunc(infoPtr->image, &infoPtr->mtlTexture);
-        if (!infoPtr->mtlTexture) {
-            VK_COMMON_ERROR("Failed to get MTLTexture for Vulkan image %p.", infoPtr->image);
-        }
-
+        // Retrieve metal texture for this image
+        infoPtr->mtlTexture = getMtlTextureFromVkImage(vk, infoPtr->image);
         CFRetain(infoPtr->mtlTexture);
     }
 #endif
@@ -2880,8 +3038,57 @@
     return infoPtr->memory.externalHandle;
 }
 
+#ifdef __APPLE__
+MTLBufferRef getColorBufferMetalMemoryHandle(uint32_t colorBuffer) {
+    if (!sVkEmulation || !sVkEmulation->live) return nullptr;
+
+    AutoLock lock(sVkEmulationLock);
+
+    auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBuffer);
+
+    if (!infoPtr) {
+        // Color buffer not found; this is usually OK.
+        return nullptr;
+    }
+
+    return infoPtr->memory.externalMetalHandle;
+}
+
+MTLTextureRef getColorBufferMTLTexture(uint32_t colorBufferHandle) {
+    if (!sVkEmulation || !sVkEmulation->live) return nullptr;
+
+    AutoLock lock(sVkEmulationLock);
+
+    auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+    if (!infoPtr) {
+        // Color buffer not found; this is usually OK.
+        return nullptr;
+    }
+
+    CFRetain(infoPtr->mtlTexture);
+    return infoPtr->mtlTexture;
+}
+
+// TODO(b/333460957): Temporary function for MoltenVK
+VkImage getColorBufferVkImage(uint32_t colorBufferHandle) {
+    if (!sVkEmulation || !sVkEmulation->live) return nullptr;
+
+    AutoLock lock(sVkEmulationLock);
+
+    auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+    if (!infoPtr) {
+        // Color buffer not found; this is usually OK.
+        return nullptr;
+    }
+
+    return infoPtr->image;
+}
+#endif  // __APPLE__
+
 bool setColorBufferVulkanMode(uint32_t colorBuffer, uint32_t vulkanMode) {
-    if (!sVkEmulation || !sVkEmulation->live) return VK_EXT_MEMORY_HANDLE_INVALID;
+    if (!sVkEmulation || !sVkEmulation->live) return false;
 
     AutoLock lock(sVkEmulationLock);
 
@@ -2896,24 +3103,6 @@
     return true;
 }
 
-MTLTextureRef getColorBufferMTLTexture(uint32_t colorBuffer) {
-    if (!sVkEmulation || !sVkEmulation->live) return nullptr;
-
-    AutoLock lock(sVkEmulationLock);
-
-    auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBuffer);
-
-    if (!infoPtr) {
-        // Color buffer not found; this is usually OK.
-        return nullptr;
-    }
-
-#ifdef __APPLE__
-    CFRetain(infoPtr->mtlTexture);
-#endif
-    return infoPtr->mtlTexture;
-}
-
 int32_t mapGpaToBufferHandle(uint32_t bufferHandle, uint64_t gpa, uint64_t size) {
     if (!sVkEmulation || !sVkEmulation->live) return VK_ERROR_DEVICE_LOST;
 
@@ -3031,8 +3220,7 @@
         0,
         VK_EXT_MEMORY_HANDLE_TYPE_BIT,
     };
-
-    VkExternalMemoryBufferCreateInfo* extBufferCiPtr = nullptr;
+    void* extBufferCiPtr = nullptr;
     if (sVkEmulation->deviceInfo.supportsExternalMemoryImport ||
         sVkEmulation->deviceInfo.supportsExternalMemoryExport) {
         extBufferCiPtr = &extBufferCi;
@@ -3169,6 +3357,22 @@
     return infoPtr->memory.externalHandle;
 }
 
+#ifdef __APPLE__
+MTLBufferRef getBufferMetalMemoryHandle(uint32_t bufferHandle) {
+    if (!sVkEmulation || !sVkEmulation->live) return nullptr;
+
+    AutoLock lock(sVkEmulationLock);
+
+    auto infoPtr = android::base::find(sVkEmulation->buffers, bufferHandle);
+    if (!infoPtr) {
+        // Color buffer not found; this is usually OK.
+        return nullptr;
+    }
+
+    return infoPtr->memory.externalMetalHandle;
+}
+#endif
+
 bool readBufferToBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* outBytes) {
     if (!sVkEmulation || !sVkEmulation->live) {
         VK_COMMON_ERROR("VkEmulation not available.");
@@ -3367,7 +3571,15 @@
 
     if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) {
         res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
-        res |= VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+
+        VkExternalMemoryHandleTypeFlagBits handleTypeNeeded = VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+#if defined(__APPLE__)
+        if (sVkEmulation->instanceSupportsMoltenVK) {
+            // Using a different handle type when in MoltenVK mode
+            handleTypeNeeded = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+        }
+#endif
+        res |= handleTypeNeeded;
     }
 
     if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA) {
@@ -3391,8 +3603,15 @@
     VkExternalMemoryHandleTypeFlags wantedGuestHandleType) {
     VkExternalMemoryHandleTypeFlags res = hostBits;
 
-    if (res & VK_EXT_MEMORY_HANDLE_TYPE_BIT) {
-        res &= ~VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+    VkExternalMemoryHandleTypeFlagBits handleTypeUsed = VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+#if defined(__APPLE__)
+    if (sVkEmulation->instanceSupportsMoltenVK) {
+        // Using a different handle type when in MoltenVK mode
+        handleTypeUsed = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+    }
+#endif
+    if ((res & handleTypeUsed) == handleTypeUsed) {
+        res &= ~handleTypeUsed;
         res |= wantedGuestHandleType;
     }
 
diff --git a/host/vulkan/VkCommonOperations.h b/host/vulkan/VkCommonOperations.h
index 25e228c..2038e33 100644
--- a/host/vulkan/VkCommonOperations.h
+++ b/host/vulkan/VkCommonOperations.h
@@ -84,7 +84,6 @@
                                const VkPhysicalDeviceMemoryProperties* memProps,
                                uint32_t* typeIndex);
 
-
 VK_EXT_MEMORY_HANDLE dupExternalMemory(VK_EXT_MEMORY_HANDLE);
 
 enum class AstcEmulationMode {
@@ -247,12 +246,16 @@
         uint32_t pageOffset = 0u;
         // the offset set in |vkBindImageMemory| or |vkBindBufferMemory|.
         uint32_t bindOffset = 0u;
-        // the size of all the pages the mmeory uses.
+        // the size of all the pages the memory uses.
         size_t sizeToPage = 0u;
         // guest physical address.
         uintptr_t gpa = 0u;
 
         VK_EXT_MEMORY_HANDLE externalHandle = VK_EXT_MEMORY_HANDLE_INVALID;
+#ifdef __APPLE__
+        // This is used as an external handle when MoltenVK is enabled
+        MTLBufferRef externalMetalHandle = nullptr;
+#endif
 
         bool dedicatedAllocation = false;
     };
@@ -334,7 +337,9 @@
 
         VulkanMode vulkanMode = VulkanMode::Default;
 
+#if defined(__APPLE__)
         MTLTextureRef mtlTexture = nullptr;
+#endif
 
         std::optional<DeviceOpWaitable> latestUse;
         DeviceOpTrackerPtr latestUseTracker = nullptr;
@@ -354,7 +359,6 @@
 
         bool glExported = false;
         VulkanMode vulkanMode = VulkanMode::Default;
-        MTLBufferRef mtlBuffer = nullptr;
     };
 
     // Track what is supported on whatever device was selected.
@@ -490,6 +494,11 @@
 
 VkEmulation::ColorBufferInfo getColorBufferInfo(uint32_t colorBufferHandle);
 VK_EXT_MEMORY_HANDLE getColorBufferExtMemoryHandle(uint32_t colorBufferHandle);
+#ifdef __APPLE__
+MTLBufferRef getColorBufferMetalMemoryHandle(uint32_t colorBufferHandle);
+MTLTextureRef getColorBufferMTLTexture(uint32_t colorBufferHandle);
+VkImage getColorBufferVkImage(uint32_t colorBufferHandle);
+#endif
 
 struct VkColorBufferMemoryExport {
     android::base::ManagedDescriptor descriptor;
@@ -499,7 +508,6 @@
 };
 std::optional<VkColorBufferMemoryExport> exportColorBufferMemory(uint32_t colorBufferHandle);
 
-MTLTextureRef getColorBufferMTLTexture(uint32_t colorBufferHandle);
 bool setColorBufferVulkanMode(uint32_t colorBufferHandle, uint32_t vulkanMode);
 int32_t mapGpaToBufferHandle(uint32_t bufferHandle, uint64_t gpa, uint64_t size = 0);
 
@@ -523,6 +531,9 @@
                    uint32_t memoryProperty = 0);
 bool teardownVkBuffer(uint32_t bufferHandle);
 VK_EXT_MEMORY_HANDLE getBufferExtMemoryHandle(uint32_t bufferHandle);
+#ifdef __APPLE__
+MTLBufferRef getBufferMetalMemoryHandle(uint32_t bufferHandle);
+#endif
 
 bool readBufferToBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* outBytes);
 bool updateBufferFromBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size,
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index 33692a6..732c051 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -2132,6 +2132,24 @@
         const VkNativeBufferANDROID* nativeBufferANDROID =
             vk_find_struct<VkNativeBufferANDROID>(pCreateInfo);
 
+#if defined(__APPLE__)
+        VkExportMetalObjectCreateInfoEXT metalImageExportCI = {
+            VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT, nullptr,
+            VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT};
+
+        // Add VkExportMetalObjectCreateInfoEXT on MoltenVK
+        if (m_emu->instanceSupportsMoltenVK) {
+            const VkExternalMemoryImageCreateInfo* externalMemCI =
+                vk_find_struct<VkExternalMemoryImageCreateInfo>(pCreateInfo);
+            if (externalMemCI) {
+                // Insert metalImageExportCI to the chain
+                metalImageExportCI.pNext = externalMemCI->pNext;
+                const_cast<VkExternalMemoryImageCreateInfo*>(externalMemCI)->pNext =
+                    &metalImageExportCI;
+            }
+        }
+#endif
+
         VkResult createRes = VK_SUCCESS;
 
         if (nativeBufferANDROID) {
@@ -2282,16 +2300,6 @@
         auto* memoryInfo = android::base::find(mMemoryInfo, memory);
         if (!memoryInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-#if defined(__APPLE__) && defined(VK_MVK_moltenvk)
-        if (memoryInfo->mtlTexture) {
-            result = m_vk->vkSetMTLTextureMVK(image, memoryInfo->mtlTexture);
-            if (result != VK_SUCCESS) {
-                fprintf(stderr, "vkSetMTLTexture failed\n");
-                return VK_ERROR_OUT_OF_HOST_MEMORY;
-            }
-        }
-#endif
-
         auto* imageInfo = android::base::find(mImageInfo, image);
         if (!imageInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
         imageInfo->boundColorBuffer = memoryInfo->boundColorBuffer;
@@ -4354,6 +4362,14 @@
         };
 #endif
 
+#if defined(__APPLE__)
+        VkImportMetalBufferInfoEXT importInfoMetalBuffer = {
+            VK_STRUCTURE_TYPE_IMPORT_METAL_BUFFER_INFO_EXT,
+            0,
+            nullptr,
+        };
+#endif
+
         void* mappedPtr = nullptr;
         ManagedDescriptor externalMemoryHandle;
         if (importCbInfoPtr) {
@@ -4378,7 +4394,37 @@
                 }
             }
 
-            if (m_emu->instanceSupportsExternalMemoryCapabilities) {
+#if defined(__APPLE__)
+            // Use metal object extension on MoltenVK mode for color buffer import,
+            // non-moltenVK path on MacOS will use FD handles
+            if (m_emu->instanceSupportsMoltenVK) {
+                // TODO(b/333460957): This is a temporary fix to get MoltenVK image memory binding
+                // checks working as expected  based on dedicated memory checks. It's not a valid usage
+                // of Vulkan as the device of the image is different than what's being used here
+                localDedicatedAllocInfo = {
+                    .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
+                    .pNext = nullptr,
+                    .image = getColorBufferVkImage(importCbInfoPtr->colorBuffer),
+                    .buffer = VK_NULL_HANDLE,
+                };
+                shouldUseDedicatedAllocInfo = true;
+
+                MTLBufferRef cbExtMemoryHandle =
+                    getColorBufferMetalMemoryHandle(importCbInfoPtr->colorBuffer);
+
+                if (cbExtMemoryHandle == nullptr) {
+                    fprintf(stderr,
+                            "%s: VK_ERROR_OUT_OF_DEVICE_MEMORY: "
+                            "colorBuffer 0x%x does not have Vulkan external memory backing\n",
+                            __func__, importCbInfoPtr->colorBuffer);
+                    return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+                }
+
+                importInfoMetalBuffer.mtlBuffer = cbExtMemoryHandle;
+                vk_append_struct(&structChainIter, &importInfoMetalBuffer);
+            } else
+#endif
+            if (m_emu->deviceInfo.supportsExternalMemoryImport) {
                 VK_EXT_MEMORY_HANDLE cbExtMemoryHandle =
                     getColorBufferExtMemoryHandle(importCbInfoPtr->colorBuffer);
 
@@ -4416,7 +4462,25 @@
 
             shouldUseDedicatedAllocInfo &= bufferMemoryUsesDedicatedAlloc;
 
-            if (m_emu->instanceSupportsExternalMemoryCapabilities) {
+#ifdef __APPLE__
+            if (m_emu->instanceSupportsMoltenVK) {
+                MTLBufferRef bufferMetalMemoryHandle =
+                    getBufferMetalMemoryHandle(importBufferInfoPtr->buffer);
+
+                if (bufferMetalMemoryHandle == nullptr) {
+                    fprintf(stderr,
+                            "%s: VK_ERROR_OUT_OF_DEVICE_MEMORY: "
+                            "buffer 0x%x does not have Vulkan external memory "
+                            "backing\n",
+                            __func__, importBufferInfoPtr->buffer);
+                    return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+                }
+
+                importInfoMetalBuffer.mtlBuffer = bufferMetalMemoryHandle;
+                vk_append_struct(&structChainIter, &importInfoMetalBuffer);
+            } else
+#endif
+            if (m_emu->deviceInfo.supportsExternalMemoryImport) {
                 VK_EXT_MEMORY_HANDLE bufferExtMemoryHandle =
                     getBufferExtMemoryHandle(importBufferInfoPtr->buffer);
 
@@ -4497,6 +4561,13 @@
         exportAllocate.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
 #endif
 
+#if defined(__APPLE__)
+        if (m_emu->instanceSupportsMoltenVK) {
+            // Using a different handle type when in MoltenVK mode
+            exportAllocate.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
+        }
+#endif
+
         bool hostVisible = memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
         if (hostVisible && m_emu->features.ExternalBlob.enabled) {
             vk_append_struct(&structChainIter, &exportAllocate);
@@ -4617,11 +4688,6 @@
         memoryInfo.size = localAllocInfo.allocationSize;
         memoryInfo.device = device;
         memoryInfo.memoryIndex = localAllocInfo.memoryTypeIndex;
-#if defined(__APPLE__) && defined(VK_MVK_moltenvk)
-        if (importCbInfoPtr && m_emu->instanceSupportsMoltenVK) {
-            memoryInfo.mtlTexture = getColorBufferMTLTexture(importCbInfoPtr->colorBuffer);
-        }
-#endif
 
         if (importCbInfoPtr) {
             memoryInfo.boundColorBuffer = importCbInfoPtr->colorBuffer;
@@ -4640,8 +4706,6 @@
             memoryInfo.caching = MAP_CACHE_WC;
         }
 
-        VkInstance* instance = deviceToInstanceLocked(device);
-        InstanceInfo* instanceInfo = android::base::find(mInstanceInfo, *instance);
         auto* deviceInfo = android::base::find(mDeviceInfo, device);
         if (!deviceInfo) return VK_ERROR_OUT_OF_HOST_MEMORY;
 
@@ -4691,13 +4755,6 @@
         auto* info = android::base::find(mMemoryInfo, memory);
         if (!info) return;  // Invalid usage.
 
-#ifdef __APPLE__
-        if (info->mtlTexture) {
-            CFRelease(info->mtlTexture);
-            info->mtlTexture = nullptr;
-        }
-#endif
-
         if (info->directMapped) {
             // if direct mapped, we leave it up to the guest address space driver
             // to control the unmapping of kvm slot on the host side
@@ -5073,6 +5130,13 @@
             }
 #endif
 
+#ifdef __APPLE__
+            if (m_emu->instanceSupportsMoltenVK) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "ExternalBlob feature is not supported with MoltenVK";
+            }
+#endif
+
             ManagedDescriptor managedHandle(handle);
             BlobManager::get()->addDescriptorInfo(ctx_id, hostBlobId, std::move(managedHandle),
                                                   handleType, info->caching,
diff --git a/host/vulkan/VkDecoderInternalStructs.h b/host/vulkan/VkDecoderInternalStructs.h
index 1b51a5a..fc0d514 100644
--- a/host/vulkan/VkDecoderInternalStructs.h
+++ b/host/vulkan/VkDecoderInternalStructs.h
@@ -156,7 +156,6 @@
     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;