[vulkan] deferred command buffers (guest)

bug: 111137294

When we get "ANDROID_EMU_deferred_vulkan_commands" in the
renderControl string from the host, enable
deferred command buffer recording, where we
avoid a round trip + immediate recording on
vkBegin/End/ResetCommandBuffer; this has the effect of
delaying command submission to the next round trip, which
is no later than when these commands are submitted.

To the guest, command recording will feel much faster.

It is true that we will end up delaying command buffer submission
on host until the next vkQueueSubmit; however, it may work out,
because we can record + submit on the host with possibly less
scheduling overhead than before.

Test: glmark2 ANGLE score rose from high 400s-500s to 600s

Change-Id: Ib76be4de05c768d395135ad2c9525d3e156daf31
diff --git a/system/OpenglSystemCommon/EmulatorFeatureInfo.h b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
index ba4111e..518273a 100644
--- a/system/OpenglSystemCommon/EmulatorFeatureInfo.h
+++ b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
@@ -76,6 +76,9 @@
 // - Full gralloc interop: External memory, AHB
 static const char kVulkan[] = "ANDROID_EMU_vulkan";
 
+// Deferred Vulkan commands
+static const char kDeferredVulkanCommands[] = "ANDROID_EMU_deferred_vulkan_commands";
+
 // Struct describing available emulator features
 struct EmulatorFeatureInfo {
 
@@ -85,7 +88,8 @@
         hostComposition(HOST_COMPOSITION_NONE),
         glesMaxVersion(GLES_MAX_VERSION_2),
         hasDirectMem(false),
-        hasVulkan(false) { }
+        hasVulkan(false),
+        hasDeferredVulkanCommands(false) { }
 
     SyncImpl syncImpl;
     DmaImpl dmaImpl;
@@ -93,6 +97,7 @@
     GLESMaxVersion glesMaxVersion;
     bool hasDirectMem;
     bool hasVulkan;
+    bool hasDeferredVulkanCommands;
 };
 
 #endif // __COMMON_EMULATOR_FEATURE_INFO_H
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index 5e05073..b6d465c 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -440,3 +440,10 @@
         rcEnc->featureInfo()->hasVulkan = true;
     }
 }
+
+void HostConnection::queryAndSetDeferredVulkanCommandsSupport(ExtendedRCEncoderContext* rcEnc) {
+    std::string glExtensions = queryGLExtensions(rcEnc);
+    if (glExtensions.find(kDeferredVulkanCommands) != std::string::npos) {
+        rcEnc->featureInfo()->hasDeferredVulkanCommands = true;
+    }
+}
diff --git a/system/OpenglSystemCommon/HostConnection.h b/system/OpenglSystemCommon/HostConnection.h
index c58e6d2..922c35c 100644
--- a/system/OpenglSystemCommon/HostConnection.h
+++ b/system/OpenglSystemCommon/HostConnection.h
@@ -165,6 +165,7 @@
     void queryAndSetHostCompositionImpl(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetDirectMemSupport(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVulkanSupport(ExtendedRCEncoderContext *rcEnc);
+    void queryAndSetDeferredVulkanCommandsSupport(ExtendedRCEncoderContext *rcEnc);
 
 private:
     IOStream *m_stream;
diff --git a/system/vulkan/func_table.cpp b/system/vulkan/func_table.cpp
index 3248d76..14c8c32 100644
--- a/system/vulkan/func_table.cpp
+++ b/system/vulkan/func_table.cpp
@@ -1021,7 +1021,8 @@
     AEMU_SCOPED_TRACE("vkBeginCommandBuffer");
     auto vkEnc = HostConnection::get()->vkEncoder();
     VkResult vkBeginCommandBuffer_VkResult_return = (VkResult)0;
-    vkBeginCommandBuffer_VkResult_return = vkEnc->vkBeginCommandBuffer(commandBuffer, pBeginInfo);
+    auto resources = ResourceTracker::get();
+    vkBeginCommandBuffer_VkResult_return = resources->on_vkBeginCommandBuffer(vkEnc, VK_SUCCESS, commandBuffer, pBeginInfo);
     return vkBeginCommandBuffer_VkResult_return;
 }
 static VkResult entry_vkEndCommandBuffer(
@@ -1030,7 +1031,8 @@
     AEMU_SCOPED_TRACE("vkEndCommandBuffer");
     auto vkEnc = HostConnection::get()->vkEncoder();
     VkResult vkEndCommandBuffer_VkResult_return = (VkResult)0;
-    vkEndCommandBuffer_VkResult_return = vkEnc->vkEndCommandBuffer(commandBuffer);
+    auto resources = ResourceTracker::get();
+    vkEndCommandBuffer_VkResult_return = resources->on_vkEndCommandBuffer(vkEnc, VK_SUCCESS, commandBuffer);
     return vkEndCommandBuffer_VkResult_return;
 }
 static VkResult entry_vkResetCommandBuffer(
@@ -1040,7 +1042,8 @@
     AEMU_SCOPED_TRACE("vkResetCommandBuffer");
     auto vkEnc = HostConnection::get()->vkEncoder();
     VkResult vkResetCommandBuffer_VkResult_return = (VkResult)0;
-    vkResetCommandBuffer_VkResult_return = vkEnc->vkResetCommandBuffer(commandBuffer, flags);
+    auto resources = ResourceTracker::get();
+    vkResetCommandBuffer_VkResult_return = resources->on_vkResetCommandBuffer(vkEnc, VK_SUCCESS, commandBuffer, flags);
     return vkResetCommandBuffer_VkResult_return;
 }
 static void entry_vkCmdBindPipeline(
@@ -3727,6 +3730,31 @@
     vkEnc->vkUpdateDescriptorSetWithTemplateSizedGOOGLE(device, descriptorSet, descriptorUpdateTemplate, imageInfoCount, bufferInfoCount, bufferViewCount, pImageInfoEntryIndices, pBufferInfoEntryIndices, pBufferViewEntryIndices, pImageInfos, pBufferInfos, pBufferViews);
 }
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+static void entry_vkBeginCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+    const VkCommandBufferBeginInfo* pBeginInfo)
+{
+    AEMU_SCOPED_TRACE("vkBeginCommandBufferAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    vkEnc->vkBeginCommandBufferAsyncGOOGLE(commandBuffer, pBeginInfo);
+}
+static void entry_vkEndCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer)
+{
+    AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    vkEnc->vkEndCommandBufferAsyncGOOGLE(commandBuffer);
+}
+static void entry_vkResetCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+    VkCommandBufferResetFlags flags)
+{
+    AEMU_SCOPED_TRACE("vkResetCommandBufferAsyncGOOGLE");
+    auto vkEnc = HostConnection::get()->vkEncoder();
+    vkEnc->vkResetCommandBufferAsyncGOOGLE(commandBuffer, flags);
+}
+#endif
 void* goldfish_vulkan_get_proc_address(const char* name){
 #ifdef VK_VERSION_1_0
     if (!strcmp(name, "vkCreateInstance"))
@@ -5144,6 +5172,20 @@
         return nullptr;
     }
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+    if (!strcmp(name, "vkBeginCommandBufferAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+    if (!strcmp(name, "vkEndCommandBufferAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+    if (!strcmp(name, "vkResetCommandBufferAsyncGOOGLE"))
+    {
+        return nullptr;
+    }
+#endif
     return nullptr;
 }
 void* goldfish_vulkan_get_instance_proc_address(VkInstance instance, const char* name){
@@ -6721,6 +6763,23 @@
         return hasExt ? (void*)entry_vkUpdateDescriptorSetWithTemplateSizedGOOGLE : nullptr;
     }
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+    if (!strcmp(name, "vkBeginCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkBeginCommandBufferAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkEndCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkEndCommandBufferAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkResetCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasInstanceExtension(instance, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkResetCommandBufferAsyncGOOGLE : nullptr;
+    }
+#endif
     return nullptr;
 }
 void* goldfish_vulkan_get_device_proc_address(VkDevice device, const char* name){
@@ -8298,6 +8357,23 @@
         return hasExt ? (void*)entry_vkUpdateDescriptorSetWithTemplateSizedGOOGLE : nullptr;
     }
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+    if (!strcmp(name, "vkBeginCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkBeginCommandBufferAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkEndCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkEndCommandBufferAsyncGOOGLE : nullptr;
+    }
+    if (!strcmp(name, "vkResetCommandBufferAsyncGOOGLE"))
+    {
+        bool hasExt = resources->hasDeviceExtension(device, "VK_GOOGLE_async_command_buffers");
+        return hasExt ? (void*)entry_vkResetCommandBufferAsyncGOOGLE : nullptr;
+    }
+#endif
     return nullptr;
 }
 
diff --git a/system/vulkan/func_table.h b/system/vulkan/func_table.h
index 565b12a..4d0e977 100644
--- a/system/vulkan/func_table.h
+++ b/system/vulkan/func_table.h
@@ -282,6 +282,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 a26ed5b..a723a72 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -584,6 +584,11 @@
         return mHostVisibleMemoryVirtInfo.virtualizationSupported;
     }
 
+    bool supportsDeferredCommands() const {
+        if (!mFeatureInfo) return false;
+        return mFeatureInfo->hasDeferredVulkanCommands;
+    }
+
     int getHostInstanceExtensionIndex(const std::string& extName) const {
         int i = 0;
         for (const auto& prop : mHostInstanceExtensions) {
@@ -3075,6 +3080,53 @@
             physicalDevice, pImageFormatInfo, pImageFormatProperties);
     }
 
+    VkResult on_vkBeginCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer,
+        const VkCommandBufferBeginInfo* pBeginInfo) {
+
+        VkEncoder* enc = (VkEncoder*)context;
+        (void)input_result;
+
+        if (!supportsDeferredCommands()) {
+            return enc->vkBeginCommandBuffer(commandBuffer, pBeginInfo);
+        }
+
+        enc->vkBeginCommandBufferAsyncGOOGLE(commandBuffer, pBeginInfo);
+        return VK_SUCCESS;
+    }
+
+    VkResult on_vkEndCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer) {
+
+        VkEncoder* enc = (VkEncoder*)context;
+        (void)input_result;
+
+        if (!supportsDeferredCommands()) {
+            return enc->vkEndCommandBuffer(commandBuffer);
+        }
+
+        enc->vkEndCommandBufferAsyncGOOGLE(commandBuffer);
+        return VK_SUCCESS;
+    }
+
+    VkResult on_vkResetCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer,
+        VkCommandBufferResetFlags flags) {
+
+        VkEncoder* enc = (VkEncoder*)context;
+        (void)input_result;
+
+        if (!supportsDeferredCommands()) {
+            return enc->vkResetCommandBuffer(commandBuffer, flags);
+        }
+
+        enc->vkResetCommandBufferAsyncGOOGLE(commandBuffer, flags);
+        return VK_SUCCESS;
+    }
+
     uint32_t getApiVersionFromInstance(VkInstance instance) const {
         AutoLock lock(mLock);
         uint32_t api = kMinApiVersion;
@@ -3693,6 +3745,29 @@
         pImageFormatProperties);
 }
 
+VkResult ResourceTracker::on_vkBeginCommandBuffer(
+    void* context, VkResult input_result,
+    VkCommandBuffer commandBuffer,
+    const VkCommandBufferBeginInfo* pBeginInfo) {
+    return mImpl->on_vkBeginCommandBuffer(
+        context, input_result, commandBuffer, pBeginInfo);
+}
+
+VkResult ResourceTracker::on_vkEndCommandBuffer(
+    void* context, VkResult input_result,
+    VkCommandBuffer commandBuffer) {
+    return mImpl->on_vkEndCommandBuffer(
+        context, input_result, commandBuffer);
+}
+
+VkResult ResourceTracker::on_vkResetCommandBuffer(
+    void* context, VkResult input_result,
+    VkCommandBuffer commandBuffer,
+    VkCommandBufferResetFlags flags) {
+    return mImpl->on_vkResetCommandBuffer(
+        context, input_result, commandBuffer, flags);
+}
+
 void ResourceTracker::deviceMemoryTransform_tohost(
     VkDeviceMemory* memory, uint32_t memoryCount,
     VkDeviceSize* offset, uint32_t offsetCount,
diff --git a/system/vulkan_enc/ResourceTracker.h b/system/vulkan_enc/ResourceTracker.h
index 3217f45..200bfb6 100644
--- a/system/vulkan_enc/ResourceTracker.h
+++ b/system/vulkan_enc/ResourceTracker.h
@@ -321,6 +321,18 @@
         const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
         VkImageFormatProperties2* pImageFormatProperties);
 
+    VkResult on_vkBeginCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer,
+        const VkCommandBufferBeginInfo* pBeginInfo);
+    VkResult on_vkEndCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer);
+    VkResult on_vkResetCommandBuffer(
+        void* context, VkResult input_result,
+        VkCommandBuffer commandBuffer,
+        VkCommandBufferResetFlags flags);
+
     bool isMemoryTypeHostVisible(VkDevice device, uint32_t typeIndex) const;
     uint8_t* getMappedPointer(VkDeviceMemory memory);
     VkDeviceSize getMappedSize(VkDeviceMemory memory);
diff --git a/system/vulkan_enc/VkEncoder.cpp b/system/vulkan_enc/VkEncoder.cpp
index 8f38e54..b97f120 100644
--- a/system/vulkan_enc/VkEncoder.cpp
+++ b/system/vulkan_enc/VkEncoder.cpp
@@ -22501,5 +22501,119 @@
 }
 
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+void VkEncoder::vkBeginCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+    const VkCommandBufferBeginInfo* pBeginInfo)
+{
+    AEMU_SCOPED_TRACE("vkBeginCommandBufferAsyncGOOGLE encode");
+    mImpl->log("start vkBeginCommandBufferAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkCommandBuffer local_commandBuffer;
+    VkCommandBufferBeginInfo* local_pBeginInfo;
+    local_commandBuffer = commandBuffer;
+    local_pBeginInfo = nullptr;
+    if (pBeginInfo)
+    {
+        local_pBeginInfo = (VkCommandBufferBeginInfo*)pool->alloc(sizeof(const VkCommandBufferBeginInfo));
+        deepcopy_VkCommandBufferBeginInfo(pool, pBeginInfo, (VkCommandBufferBeginInfo*)(local_pBeginInfo));
+    }
+    if (local_pBeginInfo)
+    {
+        transform_tohost_VkCommandBufferBeginInfo(mImpl->resources(), (VkCommandBufferBeginInfo*)(local_pBeginInfo));
+    }
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1508;
+        countingStream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1508, 1);
+        countingStream->write((uint64_t*)&cgen_var_1508, 1 * 8);
+        marshal_VkCommandBufferBeginInfo(countingStream, (VkCommandBufferBeginInfo*)(local_pBeginInfo));
+    }
+    uint32_t packetSize_vkBeginCommandBufferAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkBeginCommandBufferAsyncGOOGLE = OP_vkBeginCommandBufferAsyncGOOGLE;
+    stream->write(&opcode_vkBeginCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkBeginCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1509;
+    stream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1509, 1);
+    stream->write((uint64_t*)&cgen_var_1509, 1 * 8);
+    marshal_VkCommandBufferBeginInfo(stream, (VkCommandBufferBeginInfo*)(local_pBeginInfo));
+    AEMU_SCOPED_TRACE("vkBeginCommandBufferAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkBeginCommandBufferAsyncGOOGLE returnUnmarshal");
+    mImpl->log("finish vkBeginCommandBufferAsyncGOOGLE");;
+}
+
+void VkEncoder::vkEndCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer)
+{
+    AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE encode");
+    mImpl->log("start vkEndCommandBufferAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkCommandBuffer local_commandBuffer;
+    local_commandBuffer = commandBuffer;
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1510;
+        countingStream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1510, 1);
+        countingStream->write((uint64_t*)&cgen_var_1510, 1 * 8);
+    }
+    uint32_t packetSize_vkEndCommandBufferAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkEndCommandBufferAsyncGOOGLE = OP_vkEndCommandBufferAsyncGOOGLE;
+    stream->write(&opcode_vkEndCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkEndCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1511;
+    stream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1511, 1);
+    stream->write((uint64_t*)&cgen_var_1511, 1 * 8);
+    AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkEndCommandBufferAsyncGOOGLE returnUnmarshal");
+    mImpl->log("finish vkEndCommandBufferAsyncGOOGLE");;
+}
+
+void VkEncoder::vkResetCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+    VkCommandBufferResetFlags flags)
+{
+    AEMU_SCOPED_TRACE("vkResetCommandBufferAsyncGOOGLE encode");
+    mImpl->log("start vkResetCommandBufferAsyncGOOGLE");
+    auto stream = mImpl->stream();
+    auto countingStream = mImpl->countingStream();
+    auto resources = mImpl->resources();
+    auto pool = mImpl->pool();
+    stream->setHandleMapping(resources->unwrapMapping());
+    VkCommandBuffer local_commandBuffer;
+    VkCommandBufferResetFlags local_flags;
+    local_commandBuffer = commandBuffer;
+    local_flags = flags;
+    countingStream->rewind();
+    {
+        uint64_t cgen_var_1512;
+        countingStream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1512, 1);
+        countingStream->write((uint64_t*)&cgen_var_1512, 1 * 8);
+        countingStream->write((VkCommandBufferResetFlags*)&local_flags, sizeof(VkCommandBufferResetFlags));
+    }
+    uint32_t packetSize_vkResetCommandBufferAsyncGOOGLE = 4 + 4 + (uint32_t)countingStream->bytesWritten();
+    countingStream->rewind();
+    uint32_t opcode_vkResetCommandBufferAsyncGOOGLE = OP_vkResetCommandBufferAsyncGOOGLE;
+    stream->write(&opcode_vkResetCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    stream->write(&packetSize_vkResetCommandBufferAsyncGOOGLE, sizeof(uint32_t));
+    uint64_t cgen_var_1513;
+    stream->handleMapping()->mapHandles_VkCommandBuffer_u64(&local_commandBuffer, &cgen_var_1513, 1);
+    stream->write((uint64_t*)&cgen_var_1513, 1 * 8);
+    stream->write((VkCommandBufferResetFlags*)&local_flags, sizeof(VkCommandBufferResetFlags));
+    AEMU_SCOPED_TRACE("vkResetCommandBufferAsyncGOOGLE readParams");
+    AEMU_SCOPED_TRACE("vkResetCommandBufferAsyncGOOGLE returnUnmarshal");
+    mImpl->log("finish vkResetCommandBufferAsyncGOOGLE");;
+}
+
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/VkEncoder.h b/system/vulkan_enc/VkEncoder.h
index 36b768c..16df497 100644
--- a/system/vulkan_enc/VkEncoder.h
+++ b/system/vulkan_enc/VkEncoder.h
@@ -1772,6 +1772,16 @@
         const VkDescriptorBufferInfo* pBufferInfos,
         const VkBufferView* pBufferViews);
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+    void vkBeginCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+        const VkCommandBufferBeginInfo* pBeginInfo);
+    void vkEndCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer);
+    void vkResetCommandBufferAsyncGOOGLE(
+    VkCommandBuffer commandBuffer,
+        VkCommandBufferResetFlags flags);
+#endif
 
 private:
     class Impl;
diff --git a/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp b/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
index 962a6be..5b004c0 100644
--- a/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_deepcopy_guest.cpp
@@ -6382,6 +6382,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 fbddca8..b367ee0 100644
--- a/system/vulkan_enc/goldfish_vk_deepcopy_guest.h
+++ b/system/vulkan_enc/goldfish_vk_deepcopy_guest.h
@@ -2024,5 +2024,7 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 ffe1af5..c42f6e8 100644
--- a/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_extension_structs_guest.cpp
@@ -280,6 +280,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 1d9c327..1d671f8 100644
--- a/system/vulkan_enc/goldfish_vk_extension_structs_guest.h
+++ b/system/vulkan_enc/goldfish_vk_extension_structs_guest.h
@@ -297,5 +297,7 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 3110cb5..7d16b31 100644
--- a/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_handlemap_guest.cpp
@@ -4746,6 +4746,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 8fc6584..e973064 100644
--- a/system/vulkan_enc/goldfish_vk_handlemap_guest.h
+++ b/system/vulkan_enc/goldfish_vk_handlemap_guest.h
@@ -1677,5 +1677,7 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 77d3be3..a67e9ad 100644
--- a/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_marshaling_guest.cpp
@@ -13331,6 +13331,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#endif
 void marshal_extension_struct(
     VulkanStreamGuest* vkStream,
     const void* structExtension)
diff --git a/system/vulkan_enc/goldfish_vk_marshaling_guest.h b/system/vulkan_enc/goldfish_vk_marshaling_guest.h
index 0a908d9..283e0ad 100644
--- a/system/vulkan_enc/goldfish_vk_marshaling_guest.h
+++ b/system/vulkan_enc/goldfish_vk_marshaling_guest.h
@@ -3384,5 +3384,10 @@
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #define OP_vkUpdateDescriptorSetWithTemplateSizedGOOGLE 20320
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#define OP_vkBeginCommandBufferAsyncGOOGLE 20321
+#define OP_vkEndCommandBufferAsyncGOOGLE 20322
+#define OP_vkResetCommandBufferAsyncGOOGLE 20323
+#endif
 
 } // namespace goldfish_vk
diff --git a/system/vulkan_enc/goldfish_vk_private_defs.h b/system/vulkan_enc/goldfish_vk_private_defs.h
index 89a355e..3eed244 100644
--- a/system/vulkan_enc/goldfish_vk_private_defs.h
+++ b/system/vulkan_enc/goldfish_vk_private_defs.h
@@ -414,6 +414,17 @@
     const VkDescriptorBufferInfo* pBufferInfos,
     const VkBufferView* pBufferViews);
 
+#define VK_GOOGLE_async_command_buffers 1
+
+typedef void (VKAPI_PTR *PFN_vkBeginCommandBufferAsyncGOOGLE)(
+    VkCommandBuffer commandBuffer,
+    const VkCommandBufferBeginInfo* pBeginInfo);
+typedef void (VKAPI_PTR *PFN_vkEndCommandBufferAsyncGOOGLE)(
+    VkCommandBuffer commandBuffer);
+typedef void (VKAPI_PTR *PFN_vkResetCommandBufferAsyncGOOGLE)(
+    VkCommandBuffer commandBuffer,
+    VkCommandBufferResetFlags flags);
+
 #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 2c651db..aa42af7 100644
--- a/system/vulkan_enc/goldfish_vk_transform_guest.cpp
+++ b/system/vulkan_enc/goldfish_vk_transform_guest.cpp
@@ -8943,6 +8943,8 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#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 0f7aea5..754c033 100644
--- a/system/vulkan_enc/goldfish_vk_transform_guest.h
+++ b/system/vulkan_enc/goldfish_vk_transform_guest.h
@@ -3065,5 +3065,7 @@
 #endif
 #ifdef VK_GOOGLE_sized_descriptor_update_template
 #endif
+#ifdef VK_GOOGLE_async_command_buffers
+#endif
 
 } // namespace goldfish_vk