async queue submit (guest)

Bug: 160996822
Change-Id: Ifeabf3de305ce35234cc191b905411f32c19cba7
diff --git a/system/OpenglSystemCommon/EmulatorFeatureInfo.h b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
index e1c414a..d0ae853 100644
--- a/system/OpenglSystemCommon/EmulatorFeatureInfo.h
+++ b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
@@ -111,6 +111,9 @@
 // Vulkan extension that required a protocol update (new marshaling structs)
 static const char kVulkanShaderFloat16Int8[] = "ANDROID_EMU_vulkan_shader_float16_int8";
 
+// Vulkan async queue submit
+static const char kVulkanAsyncQueueSubmit[] = "ANDROID_EMU_vulkan_async_queue_submit";
+
 // Struct describing available emulator features
 struct EmulatorFeatureInfo {
 
@@ -131,7 +134,8 @@
         hasSharedSlotsHostMemoryAllocator(false),
         hasVulkanFreeMemorySync(false),
         hasVirtioGpuNativeSync(false),
-        hasVulkanShaderFloat16Int8(false)
+        hasVulkanShaderFloat16Int8(false),
+        hasVulkanAsyncQueueSubmit(false)
     { }
 
     SyncImpl syncImpl;
@@ -151,6 +155,7 @@
     bool hasVulkanFreeMemorySync;
     bool hasVirtioGpuNativeSync;
     bool hasVulkanShaderFloat16Int8;
+    bool hasVulkanAsyncQueueSubmit;
 };
 
 enum HostConnectionType {
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index d7478d2..9c6cce3 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -639,6 +639,7 @@
         queryAndSetVulkanFreeMemorySync(rcEnc);
         queryAndSetVirtioGpuNativeSync(rcEnc);
         queryAndSetVulkanShaderFloat16Int8Support(rcEnc);
+        queryAndSetVulkanAsyncQueueSubmitSupport(m_rcEnc);
         if (m_processPipe) {
             m_processPipe->processPipeInit(m_connectionType, rcEnc);
         }
@@ -894,3 +895,10 @@
         rcEnc->featureInfo()->hasVulkanShaderFloat16Int8 = true;
     }
 }
+
+void HostConnection::queryAndSetVulkanAsyncQueueSubmitSupport(ExtendedRCEncoderContext* rcEnc) {
+    std::string glExtensions = queryGLExtensions(rcEnc);
+    if (glExtensions.find(kVulkanAsyncQueueSubmit) != std::string::npos) {
+        rcEnc->featureInfo()->hasVulkanAsyncQueueSubmit = true;
+    }
+}
diff --git a/system/OpenglSystemCommon/HostConnection.h b/system/OpenglSystemCommon/HostConnection.h
index 4ccd5a7..69d8a16 100644
--- a/system/OpenglSystemCommon/HostConnection.h
+++ b/system/OpenglSystemCommon/HostConnection.h
@@ -216,6 +216,7 @@
     void queryAndSetVulkanFreeMemorySync(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVirtioGpuNativeSync(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVulkanShaderFloat16Int8Support(ExtendedRCEncoderContext *rcEnc);
+    void queryAndSetVulkanAsyncQueueSubmitSupport(ExtendedRCEncoderContext *rcEnc);
 
 private:
     HostConnectionType m_connectionType;
diff --git a/system/vulkan/func_table.cpp b/system/vulkan/func_table.cpp
index 50e8a62..51913e8 100644
--- a/system/vulkan/func_table.cpp
+++ b/system/vulkan/func_table.cpp
@@ -240,6 +240,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueSubmit");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     VkResult vkQueueSubmit_VkResult_return = (VkResult)0;
     auto resources = ResourceTracker::get();
     vkQueueSubmit_VkResult_return = resources->on_vkQueueSubmit(vkEnc, VK_SUCCESS, queue, submitCount, pSubmits, fence);
@@ -250,6 +251,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueWaitIdle");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     VkResult vkQueueWaitIdle_VkResult_return = (VkResult)0;
     auto resources = ResourceTracker::get();
     vkQueueWaitIdle_VkResult_return = resources->on_vkQueueWaitIdle(vkEnc, VK_SUCCESS, queue);
@@ -418,6 +420,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueBindSparse");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     VkResult vkQueueBindSparse_VkResult_return = (VkResult)0;
     vkQueueBindSparse_VkResult_return = vkEnc->vkQueueBindSparse(queue, bindInfoCount, pBindInfo, fence);
     return vkQueueBindSparse_VkResult_return;
@@ -2258,6 +2261,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueuePresentKHR");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     VkResult vkQueuePresentKHR_VkResult_return = (VkResult)0;
     vkQueuePresentKHR_VkResult_return = vkEnc->vkQueuePresentKHR(queue, pPresentInfo);
     return vkQueuePresentKHR_VkResult_return;
@@ -3751,6 +3755,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueSignalReleaseImageANDROID");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     VkResult vkQueueSignalReleaseImageANDROID_VkResult_return = (VkResult)0;
     vkQueueSignalReleaseImageANDROID_VkResult_return = vkEnc->vkQueueSignalReleaseImageANDROID(queue, waitSemaphoreCount, pWaitSemaphores, image, pNativeFenceFd);
     return vkQueueSignalReleaseImageANDROID_VkResult_return;
@@ -4633,6 +4638,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueBeginDebugUtilsLabelEXT");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     vkEnc->vkQueueBeginDebugUtilsLabelEXT(queue, pLabelInfo);
 }
 static void entry_vkQueueEndDebugUtilsLabelEXT(
@@ -4640,6 +4646,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueEndDebugUtilsLabelEXT");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     vkEnc->vkQueueEndDebugUtilsLabelEXT(queue);
 }
 static void entry_vkQueueInsertDebugUtilsLabelEXT(
@@ -4648,6 +4655,7 @@
 {
     AEMU_SCOPED_TRACE("vkQueueInsertDebugUtilsLabelEXT");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     vkEnc->vkQueueInsertDebugUtilsLabelEXT(queue, pLabelInfo);
 }
 static void entry_vkCmdBeginDebugUtilsLabelEXT(
@@ -4992,6 +5000,7 @@
 {
     AEMU_SCOPED_TRACE("vkGetQueueCheckpointDataNV");
     auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
     vkEnc->vkGetQueueCheckpointDataNV(queue, pCheckpointDataCount, pCheckpointData);
 }
 #endif
@@ -5287,6 +5296,48 @@
     return vkFreeMemorySyncGOOGLE_VkResult_return;
 }
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+static void entry_vkQueueHostSyncGOOGLE(
+    VkQueue queue,
+    uint32_t needHostSync,
+    uint32_t sequenceNumber)
+{
+    AEMU_SCOPED_TRACE("vkQueueHostSyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
+    vkEnc->vkQueueHostSyncGOOGLE(queue, needHostSync, sequenceNumber);
+}
+static void entry_vkQueueSubmitAsyncGOOGLE(
+    VkQueue queue,
+    uint32_t submitCount,
+    const VkSubmitInfo* pSubmits,
+    VkFence fence)
+{
+    AEMU_SCOPED_TRACE("vkQueueSubmitAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
+    vkEnc->vkQueueSubmitAsyncGOOGLE(queue, submitCount, pSubmits, fence);
+}
+static void entry_vkQueueWaitIdleAsyncGOOGLE(
+    VkQueue queue)
+{
+    AEMU_SCOPED_TRACE("vkQueueWaitIdleAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
+    vkEnc->vkQueueWaitIdleAsyncGOOGLE(queue);
+}
+static void entry_vkQueueBindSparseAsyncGOOGLE(
+    VkQueue queue,
+    uint32_t bindInfoCount,
+    const VkBindSparseInfo* pBindInfo,
+    VkFence fence)
+{
+    AEMU_SCOPED_TRACE("vkQueueBindSparseAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    ResourceTracker::get()->syncEncodersForQueue(queue, vkEnc);
+    vkEnc->vkQueueBindSparseAsyncGOOGLE(queue, bindInfoCount, pBindInfo, fence);
+}
+#endif
 void* goldfish_vulkan_get_proc_address(const char* name){
 #ifdef VK_VERSION_1_0
     if (!strcmp(name, "vkCreateInstance"))
@@ -6744,6 +6795,24 @@
         return nullptr;
     }
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+    if (!strcmp(name, "vkQueueHostSyncGOOGLE"))
+    {
+        return nullptr;
+    }
+    if (!strcmp(name, "vkQueueSubmitAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+    if (!strcmp(name, "vkQueueWaitIdleAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+    if (!strcmp(name, "vkQueueBindSparseAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+#endif
     return nullptr;
 }
 void* goldfish_vulkan_get_instance_proc_address(VkInstance instance, const char* name){
@@ -8298,6 +8367,28 @@
         return (void*)dynCheck_entry_vkFreeMemorySyncGOOGLE;
     }
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+    if (!strcmp(name, "vkQueueHostSyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueHostSyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueSubmitAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueSubmitAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueWaitIdleAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueWaitIdleAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueBindSparseAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueBindSparseAsyncGOOGLE : nullptr;
+    }
+#endif
     return nullptr;
 }
 void* goldfish_vulkan_get_device_proc_address(VkDevice device, const char* name){
@@ -9923,6 +10014,28 @@
         return hasExt ? (void*)entry_vkFreeMemorySyncGOOGLE : nullptr;
     }
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+    if (!strcmp(name, "vkQueueHostSyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueHostSyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueSubmitAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueSubmitAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueWaitIdleAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueWaitIdleAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkQueueBindSparseAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_queue_submit");
+        return hasExt ? (void*)entry_vkQueueBindSparseAsyncGOOGLE : nullptr;
+    }
+#endif
     return nullptr;
 }
 
diff --git a/system/vulkan/func_table.h b/system/vulkan/func_table.h
index 3c0e457..9c2e5be 100644
--- a/system/vulkan/func_table.h
+++ b/system/vulkan/func_table.h
@@ -292,6 +292,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 void* goldfish_vulkan_get_proc_address(const char* name);
 void* goldfish_vulkan_get_instance_proc_address(VkInstance instance, const char* name);
 void* goldfish_vulkan_get_device_proc_address(VkDevice device, const char* name);
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index e7ff088..4fd2850 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -275,6 +275,11 @@
         uint32_t sequenceNumber = 0;
     };
 
+    struct VkQueue_Info {
+        VkEncoder** lastUsedEncoderPtr = nullptr;
+        uint32_t sequenceNumber = 0;
+    };
+
     // custom guest-side structs for images/buffers because of AHardwareBuffer :((
     struct VkImage_Info {
         VkDevice device;
@@ -401,6 +406,24 @@
         info_VkCommandBuffer.erase(commandBuffer);
     }
 
+    void unregister_VkQueue(VkQueue queue) {
+        AutoLock lock(mLock);
+
+        auto it = info_VkQueue.find(queue);
+        if (it == info_VkQueue.end()) return;
+        auto& info = it->second;
+        auto lastUsedEncoder =
+            info.lastUsedEncoderPtr ?
+            *(info.lastUsedEncoderPtr) : nullptr;
+
+        if (lastUsedEncoder) {
+            lastUsedEncoder->unregisterCleanupCallback(queue);
+            delete info.lastUsedEncoderPtr;
+        }
+
+        info_VkQueue.erase(queue);
+    }
+
     void unregister_VkDeviceMemory(VkDeviceMemory mem) {
         AutoLock lock(mLock);
 
@@ -839,6 +862,11 @@
         return mFeatureInfo->hasDeferredVulkanCommands;
     }
 
+    bool supportsAsyncQueueSubmit() const {
+        if (!mFeatureInfo) return false;
+        return mFeatureInfo->hasVulkanAsyncQueueSubmit;
+    }
+
     bool supportsCreateResourcesWithRequirements() const {
         if (!mFeatureInfo) return false;
         return mFeatureInfo->hasVulkanCreateResourcesWithRequirements;
@@ -4101,8 +4129,13 @@
         lock.unlock();
 
         if (pre_signal_semaphores.empty()) {
-            input_result = enc->vkQueueSubmit(queue, submitCount, pSubmits, fence);
-            if (input_result != VK_SUCCESS) return input_result;
+            if (supportsAsyncQueueSubmit()) {
+                enc->vkQueueSubmitAsyncGOOGLE(queue, submitCount, pSubmits, fence);
+                input_result = VK_SUCCESS;
+            } else {
+                input_result = enc->vkQueueSubmit(queue, submitCount, pSubmits, fence);
+                if (input_result != VK_SUCCESS) return input_result;
+            }
         } else {
             // Schedule waits on the OS external objects and
             // signal the wait semaphores
@@ -4137,10 +4170,20 @@
                 .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 (supportsAsyncQueueSubmit()) {
+                enc->vkQueueSubmitAsyncGOOGLE(queue, 1, &submit_info, VK_NULL_HANDLE);
+            } else {
+                enc->vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
+            }
+
+            if (supportsAsyncQueueSubmit()) {
+                enc->vkQueueSubmitAsyncGOOGLE(queue, submitCount, pSubmits, fence);
+                input_result = VK_SUCCESS;
+            } else {
+                input_result = enc->vkQueueSubmit(queue, submitCount, pSubmits, fence);
+                if (input_result != VK_SUCCESS) return input_result;
+            }
         }
 
         lock.lock();
@@ -4612,11 +4655,13 @@
         if (!lastEncoder) return 0;
         if (lastEncoder == currentEncoder) return 0;
 
-        info.sequenceNumber++;
-        lastEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, false, info.sequenceNumber);
+        auto oldSeq = info.sequenceNumber;
+
+        lock.unlock();
+
+        lastEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, false, oldSeq + 1);
         lastEncoder->flush();
-        info.sequenceNumber++;
-        currentEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, true, info.sequenceNumber);
+        currentEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, true, oldSeq + 2);
 
         lastEncoder->unregisterCleanupCallback(commandBuffer);
 
@@ -4629,6 +4674,54 @@
         return 1;
     }
 
+    uint32_t syncEncodersForQueue(VkQueue queue, VkEncoder* currentEncoder) {
+        AutoLock lock(mLock);
+
+        auto it = info_VkQueue.find(queue);
+        if (it == info_VkQueue.end()) return 0;
+
+        auto& info = it->second;
+
+        if (!info.lastUsedEncoderPtr) {
+            info.lastUsedEncoderPtr = new VkEncoder*;
+            *(info.lastUsedEncoderPtr) = currentEncoder;
+        }
+
+        auto lastUsedEncoderPtr = info.lastUsedEncoderPtr;
+
+        auto lastEncoder = *(lastUsedEncoderPtr);
+
+        // We always make lastUsedEncoderPtr track
+        // the current encoder, even if the last encoder
+        // is null.
+        *(lastUsedEncoderPtr) = currentEncoder;
+
+        if (!lastEncoder) return 0;
+        if (lastEncoder == currentEncoder) return 0;
+
+        auto oldSeq = info.sequenceNumber;
+
+        info.sequenceNumber += 2;
+
+        lock.unlock();
+
+        // at this point the seqno for the old thread is determined
+
+        lastEncoder->vkQueueHostSyncGOOGLE(queue, false, oldSeq + 1);
+        lastEncoder->flush();
+        currentEncoder->vkQueueHostSyncGOOGLE(queue, true, oldSeq + 2);
+
+        lastEncoder->unregisterCleanupCallback(queue);
+
+        currentEncoder->registerCleanupCallback(queue, [currentEncoder, lastUsedEncoderPtr]() {
+            if (*(lastUsedEncoderPtr) == currentEncoder) {
+                *(lastUsedEncoderPtr) = nullptr;
+            }
+        });
+
+        return 1;
+    }
+
     VkResult on_vkBeginCommandBuffer(
         void* context, VkResult input_result,
         VkCommandBuffer commandBuffer,
@@ -5560,6 +5653,10 @@
     return mImpl->syncEncodersForCommandBuffer(commandBuffer, current);
 }
 
+uint32_t ResourceTracker::syncEncodersForQueue(VkQueue queue, VkEncoder* current) {
+    return mImpl->syncEncodersForQueue(queue, current);
+}
+
 VkResult ResourceTracker::on_vkBeginCommandBuffer(
     void* context, VkResult input_result,
     VkCommandBuffer commandBuffer,
diff --git a/system/vulkan_enc/ResourceTracker.h b/system/vulkan_enc/ResourceTracker.h
index db33285..9a5fae5 100644
--- a/system/vulkan_enc/ResourceTracker.h
+++ b/system/vulkan_enc/ResourceTracker.h
@@ -481,6 +481,7 @@
         VkImageFormatProperties2* pImageFormatProperties);
 
     uint32_t syncEncodersForCommandBuffer(VkCommandBuffer commandBuffer, VkEncoder* current);
+    uint32_t syncEncodersForQueue(VkQueue queue, VkEncoder* current);
 
     VkResult on_vkBeginCommandBuffer(
         void* context, VkResult input_result,
diff --git a/system/vulkan_enc/VkEncoder.cpp b/system/vulkan_enc/VkEncoder.cpp
index 8a76c92..4313008 100644
--- a/system/vulkan_enc/VkEncoder.cpp
+++ b/system/vulkan_enc/VkEncoder.cpp
@@ -18280,6 +18280,7 @@
     countingStream->clearPool();
     stream->clearPool();
     pool->freeAll();
+    stream->flush();
     mImpl->log("finish vkQueueSignalReleaseImageANDROID");;
     return vkQueueSignalReleaseImageANDROID_VkResult_return;
 }
@@ -23030,6 +23031,7 @@
     stream->write((uint64_t*)&cgen_var_1515, 1 * 8);
     AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE readParams");
     AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE returnUnmarshal");
+    stream->flush();
     mImpl->log("finish vkEndCommandBufferAsyncGOOGLE");;
 }
 
@@ -23520,5 +23522,228 @@
 }
 
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+void VkEncoder::vkQueueHostSyncGOOGLE(
+    VkQueue queue,
+    uint32_t needHostSync,
+    uint32_t sequenceNumber)
+{
+    AutoLock encoderLock(mImpl->lock);
+    AEMU_SCOPED_TRACE("vkQueueHostSyncGOOGLE encode");
+    mImpl->log("start vkQueueHostSyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkQueue local_queue;
+    uint32_t local_needHostSync;
+    uint32_t local_sequenceNumber;
+    local_queue = queue;
+    local_needHostSync = needHostSync;
+    local_sequenceNumber = sequenceNumber;
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1553;
+        countingStream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1553, 1);
+        countingStream->write((uint64_t*)&cgen_var_1553, 1 * 8);
+        countingStream->write((uint32_t*)&local_needHostSync, sizeof(uint32_t));
+        countingStream->write((uint32_t*)&local_sequenceNumber, sizeof(uint32_t));
+    }
+    uint32_t packetSize_vkQueueHostSyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkQueueHostSyncGOOGLE = OP_vkQueueHostSyncGOOGLE;
+    stream->write(&opcode_vkQueueHostSyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkQueueHostSyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1554;
+    stream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1554, 1);
+    stream->write((uint64_t*)&cgen_var_1554, 1 * 8);
+    stream->write((uint32_t*)&local_needHostSync, sizeof(uint32_t));
+    stream->write((uint32_t*)&local_sequenceNumber, sizeof(uint32_t));
+    AEMU_SCOPED_TRACE("vkQueueHostSyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkQueueHostSyncGOOGLE returnUnmarshal");
+    mImpl->log("finish vkQueueHostSyncGOOGLE");;
+}
+
+void VkEncoder::vkQueueSubmitAsyncGOOGLE(
+    VkQueue queue,
+    uint32_t submitCount,
+    const VkSubmitInfo* pSubmits,
+    VkFence fence)
+{
+    AutoLock encoderLock(mImpl->lock);
+    AEMU_SCOPED_TRACE("vkQueueSubmitAsyncGOOGLE encode");
+    mImpl->log("start vkQueueSubmitAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkQueue local_queue;
+    uint32_t local_submitCount;
+    VkSubmitInfo* local_pSubmits;
+    VkFence local_fence;
+    local_queue = queue;
+    local_submitCount = submitCount;
+    local_pSubmits = nullptr;
+    if (pSubmits)
+    {
+        local_pSubmits = (VkSubmitInfo*)pool->alloc(((submitCount)) * sizeof(const VkSubmitInfo));
+        for (uint32_t i = 0; i < (uint32_t)((submitCount)); ++i)
+        {
+            deepcopy_VkSubmitInfo(pool, pSubmits + i, (VkSubmitInfo*)(local_pSubmits + i));
+        }
+    }
+    local_fence = fence;
+    if (local_pSubmits)
+    {
+        for (uint32_t i = 0; i < (uint32_t)((submitCount)); ++i)
+        {
+            transform_tohost_VkSubmitInfo(mImpl->resources(), (VkSubmitInfo*)(local_pSubmits + i));
+        }
+    }
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1555;
+        countingStream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1555, 1);
+        countingStream->write((uint64_t*)&cgen_var_1555, 1 * 8);
+        countingStream->write((uint32_t*)&local_submitCount, sizeof(uint32_t));
+        for (uint32_t i = 0; i < (uint32_t)((submitCount)); ++i)
+        {
+            marshal_VkSubmitInfo(countingStream, (VkSubmitInfo*)(local_pSubmits + i));
+        }
+        uint64_t cgen_var_1556;
+        countingStream->handleMapping()->mapHandles_VkFence_u64(&local_fence, &cgen_var_1556, 1);
+        countingStream->write((uint64_t*)&cgen_var_1556, 1 * 8);
+    }
+    uint32_t packetSize_vkQueueSubmitAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkQueueSubmitAsyncGOOGLE = OP_vkQueueSubmitAsyncGOOGLE;
+    stream->write(&opcode_vkQueueSubmitAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkQueueSubmitAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1557;
+    stream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1557, 1);
+    stream->write((uint64_t*)&cgen_var_1557, 1 * 8);
+    stream->write((uint32_t*)&local_submitCount, sizeof(uint32_t));
+    for (uint32_t i = 0; i < (uint32_t)((submitCount)); ++i)
+    {
+        marshal_VkSubmitInfo(stream, (VkSubmitInfo*)(local_pSubmits + i));
+    }
+    uint64_t cgen_var_1558;
+    stream->handleMapping()->mapHandles_VkFence_u64(&local_fence, &cgen_var_1558, 1);
+    stream->write((uint64_t*)&cgen_var_1558, 1 * 8);
+    AEMU_SCOPED_TRACE("vkQueueSubmitAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkQueueSubmitAsyncGOOGLE returnUnmarshal");
+    stream->flush();
+    mImpl->log("finish vkQueueSubmitAsyncGOOGLE");;
+}
+
+void VkEncoder::vkQueueWaitIdleAsyncGOOGLE(
+    VkQueue queue)
+{
+    AutoLock encoderLock(mImpl->lock);
+    AEMU_SCOPED_TRACE("vkQueueWaitIdleAsyncGOOGLE encode");
+    mImpl->log("start vkQueueWaitIdleAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkQueue local_queue;
+    local_queue = queue;
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1559;
+        countingStream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1559, 1);
+        countingStream->write((uint64_t*)&cgen_var_1559, 1 * 8);
+    }
+    uint32_t packetSize_vkQueueWaitIdleAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkQueueWaitIdleAsyncGOOGLE = OP_vkQueueWaitIdleAsyncGOOGLE;
+    stream->write(&opcode_vkQueueWaitIdleAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkQueueWaitIdleAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1560;
+    stream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1560, 1);
+    stream->write((uint64_t*)&cgen_var_1560, 1 * 8);
+    AEMU_SCOPED_TRACE("vkQueueWaitIdleAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkQueueWaitIdleAsyncGOOGLE returnUnmarshal");
+    stream->flush();
+    mImpl->log("finish vkQueueWaitIdleAsyncGOOGLE");;
+}
+
+void VkEncoder::vkQueueBindSparseAsyncGOOGLE(
+    VkQueue queue,
+    uint32_t bindInfoCount,
+    const VkBindSparseInfo* pBindInfo,
+    VkFence fence)
+{
+    AutoLock encoderLock(mImpl->lock);
+    AEMU_SCOPED_TRACE("vkQueueBindSparseAsyncGOOGLE encode");
+    mImpl->log("start vkQueueBindSparseAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkQueue local_queue;
+    uint32_t local_bindInfoCount;
+    VkBindSparseInfo* local_pBindInfo;
+    VkFence local_fence;
+    local_queue = queue;
+    local_bindInfoCount = bindInfoCount;
+    local_pBindInfo = nullptr;
+    if (pBindInfo)
+    {
+        local_pBindInfo = (VkBindSparseInfo*)pool->alloc(((bindInfoCount)) * sizeof(const VkBindSparseInfo));
+        for (uint32_t i = 0; i < (uint32_t)((bindInfoCount)); ++i)
+        {
+            deepcopy_VkBindSparseInfo(pool, pBindInfo + i, (VkBindSparseInfo*)(local_pBindInfo + i));
+        }
+    }
+    local_fence = fence;
+    if (local_pBindInfo)
+    {
+        for (uint32_t i = 0; i < (uint32_t)((bindInfoCount)); ++i)
+        {
+            transform_tohost_VkBindSparseInfo(mImpl->resources(), (VkBindSparseInfo*)(local_pBindInfo + i));
+        }
+    }
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1561;
+        countingStream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1561, 1);
+        countingStream->write((uint64_t*)&cgen_var_1561, 1 * 8);
+        countingStream->write((uint32_t*)&local_bindInfoCount, sizeof(uint32_t));
+        for (uint32_t i = 0; i < (uint32_t)((bindInfoCount)); ++i)
+        {
+            marshal_VkBindSparseInfo(countingStream, (VkBindSparseInfo*)(local_pBindInfo + i));
+        }
+        uint64_t cgen_var_1562;
+        countingStream->handleMapping()->mapHandles_VkFence_u64(&local_fence, &cgen_var_1562, 1);
+        countingStream->write((uint64_t*)&cgen_var_1562, 1 * 8);
+    }
+    uint32_t packetSize_vkQueueBindSparseAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkQueueBindSparseAsyncGOOGLE = OP_vkQueueBindSparseAsyncGOOGLE;
+    stream->write(&opcode_vkQueueBindSparseAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkQueueBindSparseAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1563;
+    stream->handleMapping()->mapHandles_VkQueue_u64(&local_queue, &cgen_var_1563, 1);
+    stream->write((uint64_t*)&cgen_var_1563, 1 * 8);
+    stream->write((uint32_t*)&local_bindInfoCount, sizeof(uint32_t));
+    for (uint32_t i = 0; i < (uint32_t)((bindInfoCount)); ++i)
+    {
+        marshal_VkBindSparseInfo(stream, (VkBindSparseInfo*)(local_pBindInfo + i));
+    }
+    uint64_t cgen_var_1564;
+    stream->handleMapping()->mapHandles_VkFence_u64(&local_fence, &cgen_var_1564, 1);
+    stream->write((uint64_t*)&cgen_var_1564, 1 * 8);
+    AEMU_SCOPED_TRACE("vkQueueBindSparseAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkQueueBindSparseAsyncGOOGLE returnUnmarshal");
+    stream->flush();
+    mImpl->log("finish vkQueueBindSparseAsyncGOOGLE");;
+}
+
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/VkEncoder.h b/system/vulkan_enc/VkEncoder.h
index 7e2e739..0dec1b6 100644
--- a/system/vulkan_enc/VkEncoder.h
+++ b/system/vulkan_enc/VkEncoder.h
@@ -1823,6 +1823,24 @@
         VkDeviceMemory memory,
         const VkAllocationCallbacks* pAllocator);
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+    void vkQueueHostSyncGOOGLE(
+    VkQueue queue,
+        uint32_t needHostSync,
+        uint32_t sequenceNumber);
+    void vkQueueSubmitAsyncGOOGLE(
+    VkQueue queue,
+        uint32_t submitCount,
+        const VkSubmitInfo* pSubmits,
+        VkFence fence);
+    void vkQueueWaitIdleAsyncGOOGLE(
+    VkQueue queue);
+    void vkQueueBindSparseAsyncGOOGLE(
+    VkQueue queue,
+        uint32_t bindInfoCount,
+        const VkBindSparseInfo* pBindInfo,
+        VkFence fence);
+#endif
 
 private:
     class Impl;
diff --git a/system/vulkan_enc/VulkanHandles.h b/system/vulkan_enc/VulkanHandles.h
index 54699aa..ca1e5f2 100644
--- a/system/vulkan_enc/VulkanHandles.h
+++ b/system/vulkan_enc/VulkanHandles.h
@@ -18,12 +18,12 @@
 
 #define GOLDFISH_VK_LIST_TRIVIAL_DISPATCHABLE_HANDLE_TYPES(f) \
     f(VkPhysicalDevice) \
-    f(VkQueue) \
 
 #define GOLDFISH_VK_LIST_DISPATCHABLE_HANDLE_TYPES(f) \
     f(VkInstance) \
     f(VkDevice) \
     f(VkCommandBuffer) \
+    f(VkQueue) \
     GOLDFISH_VK_LIST_TRIVIAL_DISPATCHABLE_HANDLE_TYPES(f)
 
 #ifdef VK_NVX_device_generated_commands
diff --git a/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp b/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
index 62fc0f0..4f59d21 100644
--- a/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
@@ -6424,6 +6424,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 void deepcopy_extension_struct(
     Pool* pool,
     const void* structExtension,
diff --git a/system/vulkan_enc/goldfish_vk_deepcopy_guest.h b/system/vulkan_enc/goldfish_vk_deepcopy_guest.h
index 07c2650..243240a 100644
--- a/system/vulkan_enc/goldfish_vk_deepcopy_guest.h
+++ b/system/vulkan_enc/goldfish_vk_deepcopy_guest.h
@@ -2044,5 +2044,7 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp b/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp
index 9d8253b..1bc69fa 100644
--- a/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp
@@ -290,6 +290,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 uint32_t goldfish_vk_struct_type(
     const void* structExtension)
 {
diff --git a/system/vulkan_enc/goldfish_vk_extension_structs_guest.h b/system/vulkan_enc/goldfish_vk_extension_structs_guest.h
index d615608..9d91cdc 100644
--- a/system/vulkan_enc/goldfish_vk_extension_structs_guest.h
+++ b/system/vulkan_enc/goldfish_vk_extension_structs_guest.h
@@ -311,5 +311,7 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp b/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp
index 65af4b3..2e22fb0 100644
--- a/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp
@@ -4780,6 +4780,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 void handlemap_extension_struct(
     VulkanHandleMapping* handlemap,
     void* structExtension_out)
diff --git a/system/vulkan_enc/goldfish_vk_handlemap_guest.h b/system/vulkan_enc/goldfish_vk_handlemap_guest.h
index 49eafa2..79eeaf6 100644
--- a/system/vulkan_enc/goldfish_vk_handlemap_guest.h
+++ b/system/vulkan_enc/goldfish_vk_handlemap_guest.h
@@ -1695,5 +1695,7 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp b/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp
index 706bb91..f857a2a 100644
--- a/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp
@@ -10368,6 +10368,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 void marshal_extension_struct(
     VulkanStreamGuest* vkStream,
     const void* structExtension)
@@ -13221,6 +13223,24 @@
             return "OP_vkFreeMemorySyncGOOGLE";
         }
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+        case OP_vkQueueHostSyncGOOGLE:
+        {
+            return "OP_vkQueueHostSyncGOOGLE";
+        }
+        case OP_vkQueueSubmitAsyncGOOGLE:
+        {
+            return "OP_vkQueueSubmitAsyncGOOGLE";
+        }
+        case OP_vkQueueWaitIdleAsyncGOOGLE:
+        {
+            return "OP_vkQueueWaitIdleAsyncGOOGLE";
+        }
+        case OP_vkQueueBindSparseAsyncGOOGLE:
+        {
+            return "OP_vkQueueBindSparseAsyncGOOGLE";
+        }
+#endif
         default:
         {
             return "OP_UNKNOWN_API_CALL";
diff --git a/system/vulkan_enc/goldfish_vk_marshaling_guest.h b/system/vulkan_enc/goldfish_vk_marshaling_guest.h
index 88ae45e..df07272 100644
--- a/system/vulkan_enc/goldfish_vk_marshaling_guest.h
+++ b/system/vulkan_enc/goldfish_vk_marshaling_guest.h
@@ -3418,6 +3418,12 @@
 #ifdef VK_GOOGLE_free_memory_sync
 #define OP_vkFreeMemorySyncGOOGLE 20328
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#define OP_vkQueueHostSyncGOOGLE 20329
+#define OP_vkQueueSubmitAsyncGOOGLE 20330
+#define OP_vkQueueWaitIdleAsyncGOOGLE 20331
+#define OP_vkQueueBindSparseAsyncGOOGLE 20332
+#endif
 const char* api_opcode_to_string(
     const uint32_t opcode);
 
diff --git a/system/vulkan_enc/goldfish_vk_private_defs.h b/system/vulkan_enc/goldfish_vk_private_defs.h
index e7b0622..dc4b7c6 100644
--- a/system/vulkan_enc/goldfish_vk_private_defs.h
+++ b/system/vulkan_enc/goldfish_vk_private_defs.h
@@ -608,6 +608,16 @@
 
 #endif
 
+#define VK_GOOGLE_async_queue_submit 1
+
+typedef void (VKAPI_PTR *PFN_vkQueueHostSyncGOOGLE)(
+    VkQueue queue, uint32_t needHostSync, uint32_t sequenceNumber);
+typedef void (VKAPI_PTR *PFN_vkQueueSubmitAsyncGOOGLE)(
+    VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
+typedef void (VKAPI_PTR *PFN_vkQueueWaitIdleAsyncGOOGLE)(VkQueue queue);
+typedef void (VKAPI_PTR *PFN_vkQueueBindSparseAsyncGOOGLE)(
+    VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/system/vulkan_enc/goldfish_vk_transform_guest.cpp b/system/vulkan_enc/goldfish_vk_transform_guest.cpp
index 4bf4032..6fc3e46 100644
--- a/system/vulkan_enc/goldfish_vk_transform_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_transform_guest.cpp
@@ -9001,6 +9001,8 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 void transform_tohost_extension_struct(
     ResourceTracker* resourceTracker,
     void* structExtension_out)
diff --git a/system/vulkan_enc/goldfish_vk_transform_guest.h b/system/vulkan_enc/goldfish_vk_transform_guest.h
index 1779eae..b969958 100644
--- a/system/vulkan_enc/goldfish_vk_transform_guest.h
+++ b/system/vulkan_enc/goldfish_vk_transform_guest.h
@@ -3091,5 +3091,7 @@
 #endif
 #ifdef VK_GOOGLE_free_memory_sync
 #endif
+#ifdef VK_GOOGLE_async_queue_submit
+#endif
 
 } // namespace goldfish_vk