[vulkan] experimental fuchsia platform support

bug: 111137294

- Add goldfish address space on Fuchsia
- Support Fuchsia external memory export via color buffer import
- In Fuchsia, create Android native buffers + colorBuffer on host
for images with color attachment or scanout usage.
- Allow the presence of preSignalSemaphores in vkQueueSubmit.

Change-Id: I0af09953be6095886013c15dbd2e266bb8621abf
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index fb99ce3..523ca99 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -22,6 +22,21 @@
 typedef uint32_t zx_handle_t;
 #define ZX_HANDLE_INVALID         ((zx_handle_t)0)
 void zx_handle_close(zx_handle_t) { }
+void zx_event_create(int, zx_handle_t*) { }
+
+typedef struct VkImportMemoryFuchsiaHandleInfoKHR {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagBits    handleType;
+    uint32_t                              handle;
+} VkImportMemoryFuchsiaHandleInfoKHR;
+
+#define VK_STRUCTURE_TYPE_IMPORT_MEMORY_FUCHSIA_HANDLE_INFO_KHR \
+    ((VkStructureType)1001000000)
+#define VK_EXTERNAL_MEMORY_HANDLE_TYPE_FUCHSIA_VMO_BIT_KHR \
+    ((VkStructureType)0x00000800)
+#define VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FUCHSIA_FENCE_BIT_KHR \
+    ((VkStructureType)0x00000020)
 
 #include "AndroidHardwareBuffer.h"
 
@@ -29,15 +44,52 @@
 
 #ifdef VK_USE_PLATFORM_FUCHSIA
 
-typedef uint32_t AHardwareBuffer;
-void AHardwareBuffer_release(AHardwareBuffer*) { }
-
+#include <cutils/native_handle.h>
 #include <fuchsia/hardware/goldfish/c/fidl.h>
 #include <lib/fzl/fdio.h>
 #include <zircon/process.h>
 #include <zircon/syscalls.h>
 #include <zircon/syscalls/object.h>
 
+struct AHardwareBuffer;
+
+typedef struct VkImportAndroidHardwareBufferInfoANDROID {
+    VkStructureType            sType;
+    const void*                pNext;
+    struct AHardwareBuffer*    buffer;
+} VkImportAndroidHardwareBufferInfoANDROID;
+
+typedef struct VkAndroidHardwareBufferPropertiesANDROID {
+    VkStructureType    sType;
+    void*              pNext;
+    VkDeviceSize       allocationSize;
+    uint32_t           memoryTypeBits;
+} VkAndroidHardwareBufferPropertiesANDROID;
+
+void AHardwareBuffer_release(AHardwareBuffer*) { }
+
+native_handle_t *AHardwareBuffer_getNativeHandle(AHardwareBuffer*) { return NULL; }
+
+VkResult importAndroidHardwareBuffer(
+    const VkImportAndroidHardwareBufferInfoANDROID* info,
+    struct AHardwareBuffer **importOut) {
+  return VK_SUCCESS;
+}
+
+VkResult createAndroidHardwareBuffer(
+    bool hasDedicatedImage,
+    bool hasDedicatedBuffer,
+    const VkExtent3D& imageExtent,
+    uint32_t imageLayers,
+    VkFormat imageFormat,
+    VkImageUsageFlags imageUsage,
+    VkImageCreateFlags imageCreateFlags,
+    VkDeviceSize bufferSize,
+    VkDeviceSize allocationInfoAllocSize,
+    struct AHardwareBuffer **out) {
+  return VK_SUCCESS;
+}
+
 #endif // VK_USE_PLATFORM_FUCHSIA
 
 #include "HostVisibleMemoryVirtualization.h"
@@ -79,36 +131,6 @@
 
 namespace goldfish_vk {
 
-namespace {
-
-template<typename T>
-bool GetImportHandle(const void* pNext, VkStructureType import_type,
-                     uint32_t bit, uint32_t* pHandle) {
-    while (pNext) {
-        auto info = static_cast<const T*>(pNext);
-        if (info->sType == import_type && info->handleType & bit) {
-            *pHandle = info->handle;
-            return true;
-        }
-        pNext = info->pNext;
-    }
-    return false;
-}
-
-template<typename T>
-bool HasExportBit(const void* pNext, VkStructureType export_type, uint32_t bit) {
-    while (pNext) {
-        auto info = static_cast<const T*>(pNext);
-        if (info->sType == export_type && info->handleTypes & bit) {
-            return true;
-        }
-        pNext = info->pNext;
-    }
-    return false;
-}
-
-} // namespace
-
 #define MAKE_HANDLE_MAPPING_FOREACH(type_name, map_impl, map_to_u64_impl, map_from_u64_impl) \
     void mapHandles_##type_name(type_name* handles, size_t count) override { \
         for (size_t i = 0; i < count; ++i) { \
@@ -207,6 +229,7 @@
         VkDeviceMemory currentBacking = VK_NULL_HANDLE;
         VkDeviceSize currentBackingOffset = 0;
         VkDeviceSize currentBackingSize = 0;
+        uint32_t cbHandle = 0;
     };
 
     struct VkBuffer_Info {
@@ -217,6 +240,12 @@
         VkDeviceSize currentBackingSize = 0;
     };
 
+    struct VkSemaphore_Info {
+        VkDevice device;
+        zx_handle_t eventHandle = ZX_HANDLE_INVALID;
+    };
+
+
 #define HANDLE_REGISTER_IMPL_IMPL(type) \
     std::unordered_map<type, type##_Info> info_##type; \
     void register_##type(type obj) { \
@@ -265,7 +294,7 @@
             AHardwareBuffer_release(memInfo.ahw);
         }
 
-        if (memInfo.vmoHandle) {
+        if (memInfo.vmoHandle != ZX_HANDLE_INVALID) {
             zx_handle_close(memInfo.vmoHandle);
         }
 
@@ -290,6 +319,11 @@
         auto it = info_VkImage.find(img);
         if (it == info_VkImage.end()) return;
 
+        auto& imageInfo = it->second;
+        if (imageInfo.cbHandle) {
+            (*mCloseColorBuffer)(imageInfo.cbHandle);
+        }
+
         info_VkImage.erase(img);
     }
 
@@ -302,6 +336,21 @@
         info_VkBuffer.erase(buf);
     }
 
+    void unregister_VkSemaphore(VkSemaphore sem) {
+        AutoLock lock(mLock);
+
+        auto it = info_VkSemaphore.find(sem);
+        if (it == info_VkSemaphore.end()) return;
+
+        auto& semInfo = it->second;
+
+        if (semInfo.eventHandle != ZX_HANDLE_INVALID) {
+            zx_handle_close(semInfo.eventHandle);
+        }
+
+        info_VkSemaphore.erase(sem);
+    }
+
     // TODO: Upgrade to 1.1
     static constexpr uint32_t kMaxApiVersion = VK_MAKE_VERSION(1, 0, 65);
     static constexpr uint32_t kMinApiVersion = VK_MAKE_VERSION(1, 0, 0);
@@ -364,6 +413,16 @@
         info.vmoHandle = vmoHandle;
     }
 
+    void setImageInfo(VkImage image,
+                      VkDevice device,
+                      const VkImageCreateInfo *pCreateInfo) {
+        AutoLock lock(mLock);
+        auto& info = info_VkImage[image];
+
+        info.device = device;
+        info.createInfo = *pCreateInfo;
+    }
+
     bool isMemoryTypeHostVisible(VkDevice device, uint32_t typeIndex) const {
         AutoLock lock(mLock);
         const auto it = info_VkDevice.find(device);
@@ -467,6 +526,12 @@
         return -1;
     }
 
+    void setColorBufferFunctions(PFN_CreateColorBuffer create,
+                                 PFN_CloseColorBuffer close) {
+        mCreateColorBuffer = create;
+        mCloseColorBuffer = close;
+    }
+
     void deviceMemoryTransform_tohost(
         VkDeviceMemory* memory, uint32_t memoryCount,
         VkDeviceSize* offset, uint32_t offsetCount,
@@ -628,11 +693,19 @@
             }
         }
 
-        VkExtensionProperties anbExtProp = {
-            "VK_ANDROID_native_buffer", 7,
+        VkExtensionProperties anbExtProps[] = {
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+            { "VK_ANDROID_native_buffer", 7 },
+#endif
+#ifdef VK_USE_PLATFORM_FUCHSIA
+            { "VK_KHR_external_memory_capabilities", 1},
+            { "VK_KHR_external_semaphore_capabilities", 1},
+#endif
         };
 
-        filteredExts.push_back(anbExtProp);
+        for (auto& anbExtProp: anbExtProps) {
+            filteredExts.push_back(anbExtProp);
+        }
 
         if (pPropertyCount) {
             *pPropertyCount = filteredExts.size();
@@ -692,11 +765,21 @@
             }
         }
 
-        VkExtensionProperties anbExtProp = {
-            "VK_ANDROID_native_buffer", 7,
+        VkExtensionProperties anbExtProps[] = {
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+            { "VK_ANDROID_native_buffer", 7 },
+#endif
+#ifdef VK_USE_PLATFORM_FUCHSIA
+            { "VK_KHR_external_memory", 1 },
+            { "VK_KHR_external_memory_fuchsia", 1 },
+            { "VK_KHR_external_semaphore", 1 },
+            { "VK_KHR_external_semaphore_fuchsia", 1 },
+#endif
         };
 
-        filteredExts.push_back(anbExtProp);
+        for (auto& anbExtProp: anbExtProps) {
+            filteredExts.push_back(anbExtProp);
+        }
 
         if (pPropertyCount) {
             *pPropertyCount = filteredExts.size();
@@ -863,6 +946,7 @@
         }
     }
 
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
     VkResult on_vkGetAndroidHardwareBufferPropertiesANDROID(
         VkDevice device,
         const AHardwareBuffer* buffer,
@@ -905,6 +989,116 @@
 
         return queryRes;
     }
+#endif
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+    VkResult on_vkGetMemoryFuchsiaHandleKHR(
+        VkDevice device,
+        const VkMemoryGetFuchsiaHandleInfoKHR* pInfo,
+        uint32_t* pHandle) {
+
+        if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED;
+        if (!pInfo->memory) return VK_ERROR_INITIALIZATION_FAILED;
+
+        AutoLock lock(mLock);
+
+        auto deviceIt = info_VkDevice.find(device);
+
+        if (deviceIt == info_VkDevice.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto memoryIt = info_VkDeviceMemory.find(pInfo->memory);
+
+        if (memoryIt == info_VkDeviceMemory.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto& info = memoryIt->second;
+
+        if (info.vmoHandle == ZX_HANDLE_INVALID) {
+            ALOGE("%s: memory cannot be exported: %d", __func__);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        *pHandle = ZX_HANDLE_INVALID;
+        zx_handle_duplicate(info.vmoHandle, ZX_RIGHT_SAME_RIGHTS, pHandle);
+        return VK_SUCCESS;
+    }
+
+    VkResult on_vkGetMemoryFuchsiaHandlePropertiesKHR(
+        VkDevice device,
+        VkExternalMemoryHandleTypeFlagBitsKHR handleType,
+        uint32_t handle,
+        VkMemoryFuchsiaHandlePropertiesKHR* pProperties) {
+        ALOGW("%s", __FUNCTION__);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    VkResult on_vkImportSemaphoreFuchsiaHandleKHR(
+        VkDevice device,
+        const VkImportSemaphoreFuchsiaHandleInfoKHR* pInfo) {
+
+        if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED;
+        if (!pInfo->semaphore) return VK_ERROR_INITIALIZATION_FAILED;
+
+        AutoLock lock(mLock);
+
+        auto deviceIt = info_VkDevice.find(device);
+
+        if (deviceIt == info_VkDevice.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto semaphoreIt = info_VkSemaphore.find(pInfo->semaphore);
+
+        if (semaphoreIt == info_VkSemaphore.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto& info = semaphoreIt->second;
+
+        if (info.eventHandle != ZX_HANDLE_INVALID) {
+            zx_handle_close(info.eventHandle);
+        }
+        info.eventHandle = pInfo->handle;
+
+        return VK_SUCCESS;
+    }
+
+    VkResult on_vkGetSemaphoreFuchsiaHandleKHR(
+        VkDevice device,
+        const VkSemaphoreGetFuchsiaHandleInfoKHR* pInfo,
+        uint32_t* pHandle) {
+
+        if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED;
+        if (!pInfo->semaphore) return VK_ERROR_INITIALIZATION_FAILED;
+
+        AutoLock lock(mLock);
+
+        auto deviceIt = info_VkDevice.find(device);
+
+        if (deviceIt == info_VkDevice.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto semaphoreIt = info_VkSemaphore.find(pInfo->semaphore);
+
+        if (semaphoreIt == info_VkSemaphore.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        auto& info = semaphoreIt->second;
+
+        if (info.eventHandle == ZX_HANDLE_INVALID) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        *pHandle = ZX_HANDLE_INVALID;
+        zx_handle_duplicate(info.eventHandle, ZX_RIGHT_SAME_RIGHTS, pHandle);
+        return VK_SUCCESS;
+    }
+#endif
 
     VkResult on_vkAllocateMemory(
         void* context,
@@ -940,8 +1134,9 @@
             (VkImportAndroidHardwareBufferInfoANDROID*)vk_find_struct((vk_struct_common*)pAllocateInfo,
                 VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID);
 
-        // TODO: Fuchsia image works in a similar way but over vmo id (phys addr)?
-        VkImportPhysicalAddressGOOGLE* importPhysAddrInfoPtr = nullptr;
+        VkImportMemoryFuchsiaHandleInfoKHR* importPhysAddrInfoPtr =
+            (VkImportMemoryFuchsiaHandleInfoKHR*)vk_find_struct((vk_struct_common*)pAllocateInfo,
+                VK_STRUCTURE_TYPE_IMPORT_MEMORY_FUCHSIA_HANDLE_INFO_KHR);
 
         VkMemoryDedicatedAllocateInfo* dedicatedAllocInfoPtr =
             (VkMemoryDedicatedAllocateInfo*)vk_find_struct((vk_struct_common*)pAllocateInfo,
@@ -953,7 +1148,7 @@
                 pAllocateInfo->memoryTypeIndex);
 
         if (!exportAllocateInfoPtr &&
-            importAhbInfoPtr && // TODO: Fuchsia image
+            (importAhbInfoPtr || importPhysAddrInfoPtr) &&
             dedicatedAllocInfoPtr &&
             isHostVisibleMemoryTypeIndexForGuest(
                 &mHostVisibleMemoryVirtInfo,
@@ -997,12 +1192,12 @@
             exportAhb =
                 exportAllocateInfoPtr->handleTypes &
                 VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
+            exportPhysAddr =
+                exportAllocateInfoPtr->handleTypes &
+                VK_EXTERNAL_MEMORY_HANDLE_TYPE_FUCHSIA_VMO_BIT_KHR;
         } else if (importAhbInfoPtr) {
             importAhb = true;
-        }
-
-        if (importPhysAddrInfoPtr) {
-            importPhysAddrInfo = *importPhysAddrInfoPtr;
+        } else if (importPhysAddrInfoPtr) {
             importPhysAddr = true;
         }
 
@@ -1083,6 +1278,21 @@
                 vk_append_struct(structChain, (vk_struct_common*)(&importCbInfo));
         }
 
+        if (importPhysAddr) {
+            vmo_handle = importPhysAddrInfoPtr->handle;
+            uint64_t cb = 0;
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+            zx_object_get_cookie(vmo_handle, vmo_handle, &cb);
+#endif
+
+            if (cb) {
+                importCbInfo.colorBuffer = cb;
+                structChain =
+                    vk_append_struct(structChain, (vk_struct_common*)(&importCbInfo));
+            }
+        }
+
         // TODO if (exportPhysAddr) { }
 
         if (!isHostVisibleMemoryTypeIndexForGuest(
@@ -1117,6 +1327,13 @@
             abort();
         }
 
+        if (vmo_handle != ZX_HANDLE_INVALID) {
+            ALOGE("%s: Host visible export/import allocation "
+                  "of VMO is not supported yet.",
+                  __func__);
+            abort();
+        }
+
         // Host visible memory, non external
         bool directMappingSupported = usingDirectMapping();
         if (!directMappingSupported) {
@@ -1319,6 +1536,37 @@
         VkImage *pImage) {
         VkEncoder* enc = (VkEncoder*)context;
 
+        uint32_t cbHandle = 0;
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+        VkNativeBufferANDROID native_info = {
+            .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
+            .pNext = NULL,
+        };
+        cb_handle_t native_handle(
+            0, 0, 0, 0, 0, 0, 0, 0, 0, FRAMEWORK_FORMAT_GL_COMPATIBLE);
+        VkImageCreateInfo localCreateInfo = *pCreateInfo;
+        pCreateInfo = &localCreateInfo;
+
+        if (pCreateInfo->usage &
+            (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+             VK_IMAGE_USAGE_SCANOUT_BIT_GOOGLE)) {
+            if (localCreateInfo.pNext) {
+                abort();
+            }
+            // Create color buffer.
+            cbHandle = (*mCreateColorBuffer)(localCreateInfo.extent.width,
+                                             localCreateInfo.extent.height,
+                                             0x1908 /*GL_RGBA*/);
+            native_handle.hostHandle = cbHandle;
+            native_info.handle = (uint32_t*)&native_handle;
+            native_info.stride = 0;
+            native_info.format = 1; // RGBA
+            native_info.usage = GRALLOC_USAGE_HW_FB;
+            localCreateInfo.pNext = &native_info;
+        }
+#endif
+
         VkResult res = enc->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
 
         if (res != VK_SUCCESS) return res;
@@ -1330,8 +1578,10 @@
 
         auto& info = it->second;
 
+        info.device = device;
         info.createInfo = *pCreateInfo;
         info.createInfo.pNext = nullptr;
+        info.cbHandle = cbHandle;
 
         return res;
     }
@@ -1371,6 +1621,41 @@
         void* context, VkResult,
         VkDevice device, VkImage image, VkDeviceMemory memory,
         VkDeviceSize memoryOffset) {
+#ifdef VK_USE_PLATFORM_FUCHSIA
+        auto imageIt = info_VkImage.find(image);
+        if (imageIt == info_VkImage.end()) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+        auto& imageInfo = imageIt->second;
+
+        if (imageInfo.cbHandle) {
+            auto memoryIt = info_VkDeviceMemory.find(memory);
+            if (memoryIt == info_VkDeviceMemory.end()) {
+                return VK_ERROR_INITIALIZATION_FAILED;
+            }
+            auto& memoryInfo = memoryIt->second;
+
+            zx_status_t status;
+            if (memoryInfo.vmoHandle == ZX_HANDLE_INVALID) {
+                status = zx_vmo_create(memoryInfo.allocationSize, 0,
+                                       &memoryInfo.vmoHandle);
+                if (status != ZX_OK) {
+                    ALOGE("%s: failed to alloc vmo", __func__);
+                    abort();
+                }
+            }
+            status = zx_object_set_cookie(memoryInfo.vmoHandle,
+                                          memoryInfo.vmoHandle,
+                                          imageInfo.cbHandle);
+            if (status != ZX_OK) {
+                ALOGE("%s: failed to set color buffer cookie", __func__);
+                abort();
+            }
+            // Color buffer backed images are already bound.
+            return VK_SUCCESS;
+        }
+#endif
+
         VkEncoder* enc = (VkEncoder*)context;
         return enc->vkBindImageMemory(device, image, memory, memoryOffset);
     }
@@ -1466,13 +1751,46 @@
     }
 
     VkResult on_vkCreateSemaphore(
-        void* context, VkResult,
+        void* context, VkResult input_result,
         VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo,
         const VkAllocationCallbacks* pAllocator,
         VkSemaphore* pSemaphore) {
+
         VkEncoder* enc = (VkEncoder*)context;
-        return enc->vkCreateSemaphore(
+
+        input_result = enc->vkCreateSemaphore(
             device, pCreateInfo, pAllocator, pSemaphore);
+
+        if (input_result != VK_SUCCESS) return input_result;
+
+        bool exportFence = false;
+        zx_handle_t event_handle = ZX_HANDLE_INVALID;
+
+        VkExportSemaphoreCreateInfoKHR* exportSemaphoreInfoPtr =
+            (VkExportSemaphoreCreateInfoKHR*)vk_find_struct((vk_struct_common*)pCreateInfo,
+                VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR);
+
+        if (exportSemaphoreInfoPtr) {
+            exportFence =
+                exportSemaphoreInfoPtr->handleTypes &
+                VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FUCHSIA_FENCE_BIT_KHR;
+        }
+
+        if (exportFence) {
+            zx_event_create(0, &event_handle);
+        }
+
+        AutoLock lock(mLock);
+
+        auto it = info_VkSemaphore.find(*pSemaphore);
+        if (it == info_VkSemaphore.end()) return VK_ERROR_INITIALIZATION_FAILED;
+
+        auto& info = it->second;
+
+        info.device = device;
+        info.eventHandle = event_handle;
+
+        return VK_SUCCESS;
     }
 
     void on_vkDestroySemaphore(
@@ -1483,10 +1801,82 @@
     }
 
     VkResult on_vkQueueSubmit(
-        void* context, VkResult,
+        void* context, VkResult input_result,
         VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
+
+        std::vector<VkSemaphore> pre_signal_semaphores;
+        std::vector<zx_handle_t> post_wait_events;
+        VkDevice device = VK_NULL_HANDLE;
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+        AutoLock lock(mLock);
+
+        for (uint32_t i = 0; i < submitCount; ++i) {
+            for (uint32_t j = 0; j < pSubmits[i].waitSemaphoreCount; ++j) {
+                auto it = info_VkSemaphore.find(pSubmits[i].pWaitSemaphores[j]);
+                if (it != info_VkSemaphore.end()) {
+                    auto& semInfo = it->second;
+                    if (semInfo.eventHandle) {
+                        // Wait here instead of passing semaphore to host.
+                        zx_object_wait_one(semInfo.eventHandle,
+                                           ZX_EVENT_SIGNALED,
+                                           ZX_TIME_INFINITE,
+                                           nullptr);
+                        pre_signal_semaphores.push_back(pSubmits[i].pWaitSemaphores[j]);
+                    }
+                }
+            }
+            for (uint32_t j = 0; j < pSubmits[i].signalSemaphoreCount; ++j) {
+                auto it = info_VkSemaphore.find(pSubmits[i].pSignalSemaphores[j]);
+                if (it != info_VkSemaphore.end()) {
+                    auto& semInfo = it->second;
+                    if (semInfo.eventHandle) {
+                        post_wait_events.push_back(semInfo.eventHandle);
+                        device = semInfo.device;
+                    }
+                }
+            }
+        }
+        lock.unlock();
+#endif
+
         VkEncoder* enc = (VkEncoder*)context;
-        return enc->vkQueueSubmit(queue, submitCount, pSubmits, fence);
+
+        VkSubmitInfo submit_info = {
+            .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+            .waitSemaphoreCount = 0,
+            .pWaitSemaphores = nullptr,
+            .pWaitDstStageMask = nullptr,
+            .signalSemaphoreCount = static_cast<uint32_t>(pre_signal_semaphores.size()),
+            .pSignalSemaphores = pre_signal_semaphores.data()};
+        enc->vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
+
+        input_result = enc->vkQueueSubmit(queue, submitCount, pSubmits, fence);
+
+        if (input_result != VK_SUCCESS) return input_result;
+
+        if (post_wait_events.empty())
+            return VK_SUCCESS;
+
+        VkFenceCreateInfo fence_create_info = {
+            VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 0, 0,
+        };
+        enc->vkCreateFence(device, &fence_create_info, nullptr, &fence);
+        enc->vkQueueSubmit(queue, 0, nullptr, fence);
+
+        static constexpr uint64_t MAX_WAIT_NS =
+            5ULL * 1000ULL * 1000ULL * 1000ULL;
+
+        enc->vkWaitForFences(device, 1, &fence, VK_TRUE, MAX_WAIT_NS);
+        enc->vkDestroyFence(device, fence, nullptr);
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+        for (auto& event : post_wait_events) {
+            zx_object_signal(event, 0, ZX_EVENT_SIGNALED);
+        }
+#endif
+
+        return VK_SUCCESS;
     }
 
     void unwrap_VkNativeBufferANDROID(
@@ -1655,6 +2045,8 @@
     HostVisibleMemoryVirtualizationInfo mHostVisibleMemoryVirtInfo;
     std::unique_ptr<EmulatorFeatureInfo> mFeatureInfo;
     std::unique_ptr<GoldfishAddressSpaceBlockProvider> mGoldfishAddressSpaceBlockProvider;
+    PFN_CreateColorBuffer mCreateColorBuffer;
+    PFN_CloseColorBuffer mCloseColorBuffer;
 
     std::vector<VkExtensionProperties> mHostInstanceExtensions;
     std::vector<VkExtensionProperties> mHostDeviceExtensions;
@@ -1741,6 +2133,11 @@
     return mImpl->hasDeviceExtension(device, name);
 }
 
+void ResourceTracker::setColorBufferFunctions(
+    PFN_CreateColorBuffer create, PFN_CloseColorBuffer close) {
+    mImpl->setColorBufferFunctions(create, close);
+}
+
 VkResult ResourceTracker::on_vkEnumerateInstanceVersion(
     void* context,
     VkResult input_result,
@@ -2022,6 +2419,28 @@
     mImpl->unwrap_vkAcquireImageANDROID_nativeFenceFd(fd, fd_out);
 }
 
+#ifdef VK_USE_PLATFORM_FUCHSIA
+VkResult ResourceTracker::on_vkGetMemoryFuchsiaHandleKHR(
+    VkDevice device,
+    const VkMemoryGetFuchsiaHandleInfoKHR* pInfo,
+    uint32_t* pHandle) {
+    return mImpl->on_vkGetMemoryFuchsiaHandleKHR(device, pInfo, pHandle);
+}
+
+VkResult ResourceTracker::on_vkGetSemaphoreFuchsiaHandleKHR(
+    VkDevice device,
+    const VkSemaphoreGetFuchsiaHandleInfoKHR* pInfo,
+    uint32_t* pHandle) {
+    return mImpl->on_vkGetSemaphoreFuchsiaHandleKHR(device, pInfo, pHandle);
+}
+
+VkResult ResourceTracker::on_vkImportSemaphoreFuchsiaHandleKHR(
+    VkDevice device,
+    const VkImportSemaphoreFuchsiaHandleInfoKHR* pInfo) {
+    return mImpl->on_vkImportSemaphoreFuchsiaHandleKHR(device, pInfo);
+}
+#endif
+
 VkResult ResourceTracker::on_vkMapMemoryIntoAddressSpaceGOOGLE_pre(
     void* context,
     VkResult input_result,