Fix for compressed textures when using CopyCommands2

Test: Running dEQP-VK.api.copy_and_blit.copy_commands2.image_to_image.all_formats.{eac|astc|etc).*
Bug: 300460096
Change-Id: I56b094411e7cf6dbe7323796338e4032c42ebd71
diff --git a/host/vulkan/VkDecoder.cpp b/host/vulkan/VkDecoder.cpp
index 6a23cb9..8d8692a 100644
--- a/host/vulkan/VkDecoder.cpp
+++ b/host/vulkan/VkDecoder.cpp
@@ -12001,15 +12001,12 @@
                 android::base::beginTrace("vkCmdCopyImage2 decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyImageInfo2* pCopyImageInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyImageInfo, sizeof(const VkCopyImageInfo2));
                 reservedunmarshal_VkCopyImageInfo2(vkReadStream, VK_STRUCTURE_TYPE_MAX_ENUM,
                                                    (VkCopyImageInfo2*)(pCopyImageInfo),
@@ -12021,7 +12018,7 @@
                     fprintf(stderr, "stream %p: call vkCmdCopyImage2 0x%llx 0x%llx \n", ioStream,
                             (unsigned long long)commandBuffer, (unsigned long long)pCopyImageInfo);
                 }
-                vk->vkCmdCopyImage2(unboxed_commandBuffer, pCopyImageInfo);
+                m_state->on_vkCmdCopyImage2(&m_pool, commandBuffer, pCopyImageInfo);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
@@ -12040,15 +12037,12 @@
                 android::base::beginTrace("vkCmdCopyBufferToImage2 decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyBufferToImageInfo,
                                     sizeof(const VkCopyBufferToImageInfo2));
                 reservedunmarshal_VkCopyBufferToImageInfo2(
@@ -12063,7 +12057,8 @@
                             ioStream, (unsigned long long)commandBuffer,
                             (unsigned long long)pCopyBufferToImageInfo);
                 }
-                vk->vkCmdCopyBufferToImage2(unboxed_commandBuffer, pCopyBufferToImageInfo);
+                m_state->on_vkCmdCopyBufferToImage2(&m_pool, commandBuffer, pCopyBufferToImageInfo,
+                                                    context);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
@@ -12083,15 +12078,12 @@
                 android::base::beginTrace("vkCmdCopyImageToBuffer2 decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyImageToBufferInfo,
                                     sizeof(const VkCopyImageToBufferInfo2));
                 reservedunmarshal_VkCopyImageToBufferInfo2(
@@ -12106,7 +12098,7 @@
                             ioStream, (unsigned long long)commandBuffer,
                             (unsigned long long)pCopyImageToBufferInfo);
                 }
-                vk->vkCmdCopyImageToBuffer2(unboxed_commandBuffer, pCopyImageToBufferInfo);
+                m_state->on_vkCmdCopyImageToBuffer2(&m_pool, commandBuffer, pCopyImageToBufferInfo);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
@@ -22311,15 +22303,12 @@
                 android::base::beginTrace("vkCmdCopyImage2KHR decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyImageInfo2* pCopyImageInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyImageInfo, sizeof(const VkCopyImageInfo2));
                 reservedunmarshal_VkCopyImageInfo2(vkReadStream, VK_STRUCTURE_TYPE_MAX_ENUM,
                                                    (VkCopyImageInfo2*)(pCopyImageInfo),
@@ -22331,7 +22320,7 @@
                     fprintf(stderr, "stream %p: call vkCmdCopyImage2KHR 0x%llx 0x%llx \n", ioStream,
                             (unsigned long long)commandBuffer, (unsigned long long)pCopyImageInfo);
                 }
-                vk->vkCmdCopyImage2KHR(unboxed_commandBuffer, pCopyImageInfo);
+                m_state->on_vkCmdCopyImage2KHR(&m_pool, commandBuffer, pCopyImageInfo);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
@@ -22350,15 +22339,12 @@
                 android::base::beginTrace("vkCmdCopyBufferToImage2KHR decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyBufferToImageInfo,
                                     sizeof(const VkCopyBufferToImageInfo2));
                 reservedunmarshal_VkCopyBufferToImageInfo2(
@@ -22373,7 +22359,8 @@
                             ioStream, (unsigned long long)commandBuffer,
                             (unsigned long long)pCopyBufferToImageInfo);
                 }
-                vk->vkCmdCopyBufferToImage2KHR(unboxed_commandBuffer, pCopyBufferToImageInfo);
+                m_state->on_vkCmdCopyBufferToImage2KHR(&m_pool, commandBuffer,
+                                                       pCopyBufferToImageInfo, context);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
@@ -22393,15 +22380,12 @@
                 android::base::beginTrace("vkCmdCopyImageToBuffer2KHR decode");
                 VkCommandBuffer commandBuffer;
                 const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo;
-                // Begin non wrapped dispatchable handle unboxing for commandBuffer;
+                // Begin global wrapped dispatchable handle unboxing for commandBuffer;
                 uint64_t cgen_var_0;
                 memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8);
                 *readStreamPtrPtr += 1 * 8;
                 *(VkCommandBuffer*)&commandBuffer =
                     (VkCommandBuffer)(VkCommandBuffer)((VkCommandBuffer)(*&cgen_var_0));
-                auto unboxed_commandBuffer = unbox_VkCommandBuffer(commandBuffer);
-                auto vk = dispatch_VkCommandBuffer(commandBuffer);
-                // End manual dispatchable handle unboxing for commandBuffer;
                 vkReadStream->alloc((void**)&pCopyImageToBufferInfo,
                                     sizeof(const VkCopyImageToBufferInfo2));
                 reservedunmarshal_VkCopyImageToBufferInfo2(
@@ -22416,7 +22400,8 @@
                             ioStream, (unsigned long long)commandBuffer,
                             (unsigned long long)pCopyImageToBufferInfo);
                 }
-                vk->vkCmdCopyImageToBuffer2KHR(unboxed_commandBuffer, pCopyImageToBufferInfo);
+                m_state->on_vkCmdCopyImageToBuffer2KHR(&m_pool, commandBuffer,
+                                                       pCopyImageToBufferInfo);
                 vkStream->unsetHandleMapping();
                 vkReadStream->setReadPos((uintptr_t)(*readStreamPtrPtr) -
                                          (uintptr_t)snapshotTraceBegin);
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index 608a757..aabd957 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -2717,6 +2717,152 @@
         }
     }
 
+    void on_vkCmdCopyImage2(android::base::BumpPool* pool,
+                           VkCommandBuffer boxed_commandBuffer,
+                           const VkCopyImageInfo2* pCopyImageInfo) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* srcImg = android::base::find(mImageInfo, pCopyImageInfo->srcImage);
+        auto* dstImg = android::base::find(mImageInfo, pCopyImageInfo->dstImage);
+        if (!srcImg || !dstImg) return;
+
+        VkDevice device = srcImg->cmpInfo.device();
+        auto* deviceInfo = android::base::find(mDeviceInfo, device);
+        if (!deviceInfo) return;
+
+        bool needEmulatedSrc = deviceInfo->needEmulatedDecompression(srcImg->cmpInfo);
+        bool needEmulatedDst = deviceInfo->needEmulatedDecompression(dstImg->cmpInfo);
+        if (!needEmulatedSrc && !needEmulatedDst) {
+            vk->vkCmdCopyImage2(commandBuffer, pCopyImageInfo);
+            return;
+        }
+        VkImage srcImageMip = pCopyImageInfo->srcImage;
+        VkImage dstImageMip = pCopyImageInfo->dstImage;
+        for (uint32_t r = 0; r < pCopyImageInfo->regionCount; r++) {
+            if (needEmulatedSrc) {
+                srcImageMip = srcImg->cmpInfo.compressedMipmap(pCopyImageInfo->pRegions[r].srcSubresource.mipLevel);
+            }
+            if (needEmulatedDst) {
+                dstImageMip = dstImg->cmpInfo.compressedMipmap(pCopyImageInfo->pRegions[r].dstSubresource.mipLevel);
+            }
+
+            VkCopyImageInfo2 inf2 = *pCopyImageInfo;
+            inf2.regionCount = 1;
+            inf2.srcImage = srcImageMip;
+            inf2.dstImage = dstImageMip;
+
+            VkImageCopy2 region = CompressedImageInfo::getCompressedMipmapsImageCopy(
+                pCopyImageInfo->pRegions[r], srcImg->cmpInfo, dstImg->cmpInfo, needEmulatedSrc, needEmulatedDst);
+            inf2.pRegions = &region;
+
+            vk->vkCmdCopyImage2(commandBuffer, &inf2);
+        }
+    }
+
+    void on_vkCmdCopyImageToBuffer2(android::base::BumpPool* pool,
+                                   VkCommandBuffer boxed_commandBuffer,
+                                   const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* imageInfo = android::base::find(mImageInfo, pCopyImageToBufferInfo->srcImage);
+        auto* bufferInfo = android::base::find(mBufferInfo, pCopyImageToBufferInfo->dstBuffer);
+        if (!imageInfo || !bufferInfo) return;
+        auto* deviceInfo = android::base::find(mDeviceInfo, bufferInfo->device);
+        if (!deviceInfo) return;
+        CompressedImageInfo& cmpInfo = imageInfo->cmpInfo;
+        if (!deviceInfo->needEmulatedDecompression(cmpInfo)) {
+            vk->vkCmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo);
+            return;
+        }
+        for (uint32_t r = 0; r < pCopyImageToBufferInfo->regionCount; r++) {
+            uint32_t mipLevel = pCopyImageToBufferInfo->pRegions[r].imageSubresource.mipLevel;
+            VkBufferImageCopy2 region = cmpInfo.getBufferImageCopy(pCopyImageToBufferInfo->pRegions[r]);
+            VkCopyImageToBufferInfo2 inf = *pCopyImageToBufferInfo;
+            inf.regionCount = 1;
+            inf.pRegions = &region;
+            inf.srcImage = cmpInfo.compressedMipmap(mipLevel);
+
+            vk->vkCmdCopyImageToBuffer2(commandBuffer, &inf);
+        }
+    }
+
+    void on_vkCmdCopyImage2KHR(android::base::BumpPool* pool,
+                           VkCommandBuffer boxed_commandBuffer,
+                           const VkCopyImageInfo2KHR* pCopyImageInfo) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* srcImg = android::base::find(mImageInfo, pCopyImageInfo->srcImage);
+        auto* dstImg = android::base::find(mImageInfo, pCopyImageInfo->dstImage);
+        if (!srcImg || !dstImg) return;
+
+        VkDevice device = srcImg->cmpInfo.device();
+        auto* deviceInfo = android::base::find(mDeviceInfo, device);
+        if (!deviceInfo) return;
+
+        bool needEmulatedSrc = deviceInfo->needEmulatedDecompression(srcImg->cmpInfo);
+        bool needEmulatedDst = deviceInfo->needEmulatedDecompression(dstImg->cmpInfo);
+        if (!needEmulatedSrc && !needEmulatedDst) {
+            vk->vkCmdCopyImage2KHR(commandBuffer, pCopyImageInfo);
+            return;
+        }
+        VkImage srcImageMip = pCopyImageInfo->srcImage;
+        VkImage dstImageMip = pCopyImageInfo->dstImage;
+        for (uint32_t r = 0; r < pCopyImageInfo->regionCount; r++) {
+            if (needEmulatedSrc) {
+                srcImageMip = srcImg->cmpInfo.compressedMipmap(pCopyImageInfo->pRegions[r].srcSubresource.mipLevel);
+            }
+            if (needEmulatedDst) {
+                dstImageMip = dstImg->cmpInfo.compressedMipmap(pCopyImageInfo->pRegions[r].dstSubresource.mipLevel);
+            }
+
+            VkCopyImageInfo2KHR inf2 = *pCopyImageInfo;
+            inf2.regionCount = 1;
+            inf2.srcImage = srcImageMip;
+            inf2.dstImage = dstImageMip;
+
+            VkImageCopy2KHR region = CompressedImageInfo::getCompressedMipmapsImageCopy(
+                pCopyImageInfo->pRegions[r], srcImg->cmpInfo, dstImg->cmpInfo, needEmulatedSrc, needEmulatedDst);
+            inf2.pRegions = &region;
+
+            vk->vkCmdCopyImage2KHR(commandBuffer, &inf2);
+        }
+    }
+
+    void on_vkCmdCopyImageToBuffer2KHR(android::base::BumpPool* pool,
+                                   VkCommandBuffer boxed_commandBuffer,
+                                   const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* imageInfo = android::base::find(mImageInfo, pCopyImageToBufferInfo->srcImage);
+        auto* bufferInfo = android::base::find(mBufferInfo, pCopyImageToBufferInfo->dstBuffer);
+        if (!imageInfo || !bufferInfo) return;
+        auto* deviceInfo = android::base::find(mDeviceInfo, bufferInfo->device);
+        if (!deviceInfo) return;
+        CompressedImageInfo& cmpInfo = imageInfo->cmpInfo;
+        if (!deviceInfo->needEmulatedDecompression(cmpInfo)) {
+            vk->vkCmdCopyImageToBuffer2KHR(commandBuffer, pCopyImageToBufferInfo);
+            return;
+        }
+        for (uint32_t r = 0; r < pCopyImageToBufferInfo->regionCount; r++) {
+            uint32_t mipLevel = pCopyImageToBufferInfo->pRegions[r].imageSubresource.mipLevel;
+            VkBufferImageCopy2KHR region = cmpInfo.getBufferImageCopy(pCopyImageToBufferInfo->pRegions[r]);
+            VkCopyImageToBufferInfo2KHR inf = *pCopyImageToBufferInfo;
+            inf.regionCount = 1;
+            inf.pRegions = &region;
+            inf.srcImage = cmpInfo.compressedMipmap(mipLevel);
+
+            vk->vkCmdCopyImageToBuffer2KHR(commandBuffer, &inf);
+        }
+    }
+
     void on_vkGetImageMemoryRequirements(android::base::BumpPool* pool, VkDevice boxed_device,
                                          VkImage image, VkMemoryRequirements* pMemoryRequirements) {
         auto device = unbox_VkDevice(boxed_device);
@@ -2857,6 +3003,120 @@
         }
     }
 
+    void on_vkCmdCopyBufferToImage2(android::base::BumpPool* pool,
+                                    VkCommandBuffer boxed_commandBuffer,
+                                    const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo,
+                                    const VkDecoderContext& context) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* imageInfo = android::base::find(mImageInfo, pCopyBufferToImageInfo->dstImage);
+        if (!imageInfo) return;
+        auto* bufferInfo = android::base::find(mBufferInfo, pCopyBufferToImageInfo->srcBuffer);
+        if (!bufferInfo) {
+            return;
+        }
+        VkDevice device = bufferInfo->device;
+        auto* deviceInfo = android::base::find(mDeviceInfo, device);
+        if (!deviceInfo) {
+            return;
+        }
+        if (!deviceInfo->needEmulatedDecompression(imageInfo->cmpInfo)) {
+            vk->vkCmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo);
+            return;
+        }
+        auto* cmdBufferInfo = android::base::find(mCmdBufferInfo, commandBuffer);
+        if (!cmdBufferInfo) {
+            return;
+        }
+        CompressedImageInfo& cmpInfo = imageInfo->cmpInfo;
+
+        for (uint32_t r = 0; r < pCopyBufferToImageInfo->regionCount; r++) {
+            VkCopyBufferToImageInfo2 inf;
+            uint32_t mipLevel = pCopyBufferToImageInfo->pRegions[r].imageSubresource.mipLevel;
+            inf.dstImage = cmpInfo.compressedMipmap(mipLevel);
+            VkBufferImageCopy2 region = cmpInfo.getBufferImageCopy(pCopyBufferToImageInfo->pRegions[r]);
+            inf.regionCount = 1;
+            inf.pRegions = &region;
+
+            vk->vkCmdCopyBufferToImage2(commandBuffer, &inf);
+        }
+
+        if (cmpInfo.canDecompressOnCpu()) {
+            // Get a pointer to the compressed image memory
+            const MemoryInfo* memoryInfo = android::base::find(mMemoryInfo, bufferInfo->memory);
+            if (!memoryInfo) {
+                WARN("ASTC CPU decompression: couldn't find mapped memory info");
+                return;
+            }
+            if (!memoryInfo->ptr) {
+                WARN("ASTC CPU decompression: VkBuffer memory isn't host-visible");
+                return;
+            }
+            uint8_t* astcData = (uint8_t*)(memoryInfo->ptr) + bufferInfo->memoryOffset;
+
+            cmpInfo.decompressOnCpu(commandBuffer, astcData, bufferInfo->size, pCopyBufferToImageInfo, context);
+        }
+    }
+
+    void on_vkCmdCopyBufferToImage2KHR(android::base::BumpPool* pool,
+                                    VkCommandBuffer boxed_commandBuffer,
+                                    const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo,
+                                    const VkDecoderContext& context) {
+        auto commandBuffer = unbox_VkCommandBuffer(boxed_commandBuffer);
+        auto vk = dispatch_VkCommandBuffer(boxed_commandBuffer);
+
+        std::lock_guard<std::recursive_mutex> lock(mLock);
+        auto* imageInfo = android::base::find(mImageInfo, pCopyBufferToImageInfo->dstImage);
+        if (!imageInfo) return;
+        auto* bufferInfo = android::base::find(mBufferInfo, pCopyBufferToImageInfo->srcBuffer);
+        if (!bufferInfo) {
+            return;
+        }
+        VkDevice device = bufferInfo->device;
+        auto* deviceInfo = android::base::find(mDeviceInfo, device);
+        if (!deviceInfo) {
+            return;
+        }
+        if (!deviceInfo->needEmulatedDecompression(imageInfo->cmpInfo)) {
+            vk->vkCmdCopyBufferToImage2KHR(commandBuffer, pCopyBufferToImageInfo);
+            return;
+        }
+        auto* cmdBufferInfo = android::base::find(mCmdBufferInfo, commandBuffer);
+        if (!cmdBufferInfo) {
+            return;
+        }
+        CompressedImageInfo& cmpInfo = imageInfo->cmpInfo;
+
+        for (uint32_t r = 0; r < pCopyBufferToImageInfo->regionCount; r++) {
+            VkCopyBufferToImageInfo2KHR inf;
+            uint32_t mipLevel = pCopyBufferToImageInfo->pRegions[r].imageSubresource.mipLevel;
+            inf.dstImage = cmpInfo.compressedMipmap(mipLevel);
+            VkBufferImageCopy2KHR region = cmpInfo.getBufferImageCopy(pCopyBufferToImageInfo->pRegions[r]);
+            inf.regionCount = 1;
+            inf.pRegions = &region;
+
+            vk->vkCmdCopyBufferToImage2KHR(commandBuffer, &inf);
+        }
+
+        if (cmpInfo.canDecompressOnCpu()) {
+            // Get a pointer to the compressed image memory
+            const MemoryInfo* memoryInfo = android::base::find(mMemoryInfo, bufferInfo->memory);
+            if (!memoryInfo) {
+                WARN("ASTC CPU decompression: couldn't find mapped memory info");
+                return;
+            }
+            if (!memoryInfo->ptr) {
+                WARN("ASTC CPU decompression: VkBuffer memory isn't host-visible");
+                return;
+            }
+            uint8_t* astcData = (uint8_t*)(memoryInfo->ptr) + bufferInfo->memoryOffset;
+
+            cmpInfo.decompressOnCpu(commandBuffer, astcData, bufferInfo->size, pCopyBufferToImageInfo, context);
+        }
+    }
+
     inline void convertQueueFamilyForeignToExternal(uint32_t* queueFamilyIndexPtr) {
         if (*queueFamilyIndexPtr == VK_QUEUE_FAMILY_FOREIGN_EXT) {
             *queueFamilyIndexPtr = VK_QUEUE_FAMILY_EXTERNAL;
@@ -6912,6 +7172,44 @@
                                      regionCount, pRegions);
 }
 
+void VkDecoderGlobalState::on_vkCmdCopyBufferToImage2(android::base::BumpPool* pool,
+                                VkCommandBuffer commandBuffer,
+                                const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo,
+                                const VkDecoderContext& context) {
+    mImpl->on_vkCmdCopyBufferToImage2(pool, commandBuffer, pCopyBufferToImageInfo, context);
+}
+
+void VkDecoderGlobalState::on_vkCmdCopyImage2(android::base::BumpPool* pool,
+    VkCommandBuffer commandBuffer,
+    const VkCopyImageInfo2* pCopyImageInfo) {
+    mImpl->on_vkCmdCopyImage2(pool, commandBuffer, pCopyImageInfo);
+}
+
+void VkDecoderGlobalState::on_vkCmdCopyImageToBuffer2(android::base::BumpPool* pool,
+                                VkCommandBuffer commandBuffer,
+                                const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
+    mImpl->on_vkCmdCopyImageToBuffer2(pool, commandBuffer, pCopyImageToBufferInfo);
+}
+
+void VkDecoderGlobalState::on_vkCmdCopyBufferToImage2KHR(android::base::BumpPool* pool,
+                                VkCommandBuffer commandBuffer,
+                                const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo,
+                                const VkDecoderContext& context) {
+    mImpl->on_vkCmdCopyBufferToImage2KHR(pool, commandBuffer, pCopyBufferToImageInfo, context);
+}
+
+void VkDecoderGlobalState::on_vkCmdCopyImage2KHR(android::base::BumpPool* pool,
+    VkCommandBuffer commandBuffer,
+    const VkCopyImageInfo2KHR* pCopyImageInfo) {
+    mImpl->on_vkCmdCopyImage2KHR(pool, commandBuffer, pCopyImageInfo);
+}
+
+void VkDecoderGlobalState::on_vkCmdCopyImageToBuffer2KHR(android::base::BumpPool* pool,
+                                VkCommandBuffer commandBuffer,
+                                const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo) {
+    mImpl->on_vkCmdCopyImageToBuffer2KHR(pool, commandBuffer, pCopyImageToBufferInfo);
+}
+
 void VkDecoderGlobalState::on_vkGetImageMemoryRequirements(
     android::base::BumpPool* pool, VkDevice device, VkImage image,
     VkMemoryRequirements* pMemoryRequirements) {
diff --git a/host/vulkan/VkDecoderGlobalState.h b/host/vulkan/VkDecoderGlobalState.h
index 7eec8e1..7706fd0 100644
--- a/host/vulkan/VkDecoderGlobalState.h
+++ b/host/vulkan/VkDecoderGlobalState.h
@@ -319,6 +319,30 @@
                                    VkBuffer dstBuffer, uint32_t regionCount,
                                    const VkBufferImageCopy* pRegions);
 
+    void on_vkCmdCopyBufferToImage2(android::base::BumpPool* pool,
+                                    VkCommandBuffer commandBuffer,
+                                    const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo,
+                                    const VkDecoderContext& context);
+
+    void on_vkCmdCopyImage2(android::base::BumpPool* pool,
+                           VkCommandBuffer commandBuffer,
+                           const VkCopyImageInfo2* pCopyImageInfo);
+    void on_vkCmdCopyImageToBuffer2(android::base::BumpPool* pool,
+                                   VkCommandBuffer commandBuffer,
+                                   const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo);
+
+    void on_vkCmdCopyBufferToImage2KHR(android::base::BumpPool* pool,
+                                    VkCommandBuffer commandBuffer,
+                                    const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo,
+                                    const VkDecoderContext& context);
+
+    void on_vkCmdCopyImage2KHR(android::base::BumpPool* pool,
+                           VkCommandBuffer commandBuffer,
+                           const VkCopyImageInfo2KHR* pCopyImageInfo);
+    void on_vkCmdCopyImageToBuffer2KHR(android::base::BumpPool* pool,
+                                   VkCommandBuffer commandBuffer,
+                                   const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo);
+
     void on_vkGetImageMemoryRequirements(android::base::BumpPool* pool, VkDevice device,
                                          VkImage image, VkMemoryRequirements* pMemoryRequirements);
 
diff --git a/host/vulkan/VkSubDecoder.cpp b/host/vulkan/VkSubDecoder.cpp
index 4b4a1f4..08c919f 100644
--- a/host/vulkan/VkSubDecoder.cpp
+++ b/host/vulkan/VkSubDecoder.cpp
@@ -1680,7 +1680,8 @@
                     transform_tohost_VkCopyImageInfo2(globalstate,
                                                       (VkCopyImageInfo2*)(pCopyImageInfo));
                 }
-                vk->vkCmdCopyImage2((VkCommandBuffer)dispatchHandle, pCopyImageInfo);
+                this->on_vkCmdCopyImage2(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                         pCopyImageInfo);
                 android::base::endTrace();
                 break;
             }
@@ -1696,8 +1697,8 @@
                     transform_tohost_VkCopyBufferToImageInfo2(
                         globalstate, (VkCopyBufferToImageInfo2*)(pCopyBufferToImageInfo));
                 }
-                vk->vkCmdCopyBufferToImage2((VkCommandBuffer)dispatchHandle,
-                                            pCopyBufferToImageInfo);
+                this->on_vkCmdCopyBufferToImage2(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                                 pCopyBufferToImageInfo, context);
                 android::base::endTrace();
                 break;
             }
@@ -1713,8 +1714,8 @@
                     transform_tohost_VkCopyImageToBufferInfo2(
                         globalstate, (VkCopyImageToBufferInfo2*)(pCopyImageToBufferInfo));
                 }
-                vk->vkCmdCopyImageToBuffer2((VkCommandBuffer)dispatchHandle,
-                                            pCopyImageToBufferInfo);
+                this->on_vkCmdCopyImageToBuffer2(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                                 pCopyImageToBufferInfo);
                 android::base::endTrace();
                 break;
             }
@@ -2742,7 +2743,8 @@
                     transform_tohost_VkCopyImageInfo2(globalstate,
                                                       (VkCopyImageInfo2*)(pCopyImageInfo));
                 }
-                vk->vkCmdCopyImage2KHR((VkCommandBuffer)dispatchHandle, pCopyImageInfo);
+                this->on_vkCmdCopyImage2KHR(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                            pCopyImageInfo);
                 android::base::endTrace();
                 break;
             }
@@ -2758,8 +2760,8 @@
                     transform_tohost_VkCopyBufferToImageInfo2(
                         globalstate, (VkCopyBufferToImageInfo2*)(pCopyBufferToImageInfo));
                 }
-                vk->vkCmdCopyBufferToImage2KHR((VkCommandBuffer)dispatchHandle,
-                                               pCopyBufferToImageInfo);
+                this->on_vkCmdCopyBufferToImage2KHR(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                                    pCopyBufferToImageInfo, context);
                 android::base::endTrace();
                 break;
             }
@@ -2775,8 +2777,8 @@
                     transform_tohost_VkCopyImageToBufferInfo2(
                         globalstate, (VkCopyImageToBufferInfo2*)(pCopyImageToBufferInfo));
                 }
-                vk->vkCmdCopyImageToBuffer2KHR((VkCommandBuffer)dispatchHandle,
-                                               pCopyImageToBufferInfo);
+                this->on_vkCmdCopyImageToBuffer2KHR(pool, (VkCommandBuffer)(boxed_dispatchHandle),
+                                                    pCopyImageToBufferInfo);
                 android::base::endTrace();
                 break;
             }
diff --git a/host/vulkan/emulated_textures/AstcTexture.cpp b/host/vulkan/emulated_textures/AstcTexture.cpp
index b7c1ecf..0749da2 100644
--- a/host/vulkan/emulated_textures/AstcTexture.cpp
+++ b/host/vulkan/emulated_textures/AstcTexture.cpp
@@ -157,13 +157,14 @@
     }
 }
 
-void AstcTexture::on_vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
+template<typename T>
+void AstcTexture::on_vkCmdCopyBufferToImageImpl(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
                                             size_t astcDataSize, VkImage dstImage,
                                             VkImageLayout dstImageLayout, uint32_t regionCount,
-                                            const VkBufferImageCopy* pRegions,
+                                            const T* pRegions,
                                             const VkDecoderContext& context) {
     auto watchdog =
-        WATCHDOG_BUILDER(context.healthMonitor, "AstcTexture::on_vkCmdCopyBufferToImage").build();
+        WATCHDOG_BUILDER(context.healthMonitor, "AstcTexture::on_vkCmdCopyBufferToImageImpl").build();
     auto start_time = std::chrono::steady_clock::now();
     mSuccess = false;
     size_t decompSize = 0;  // How many bytes we need to hold the decompressed data
@@ -180,7 +181,17 @@
 
     // Make a copy of the regions and update the buffer offset of each to reflect the
     // correct location of the decompressed data
-    std::vector<VkBufferImageCopy> decompRegions(pRegions, pRegions + regionCount);
+    std::vector<VkBufferImageCopy> decompRegions(regionCount);
+    for (size_t i = 0; i < regionCount; ++i) {
+        decompRegions[i] = VkBufferImageCopy {
+            pRegions[i].bufferOffset,
+            pRegions[i].bufferRowLength,
+            pRegions[i].bufferImageHeight,
+            pRegions[i].imageSubresource,
+            pRegions[i].imageOffset,
+            pRegions[i].imageExtent
+        };
+    }
     for (auto& decompRegion : decompRegions) {
         const uint32_t mipLevel = decompRegion.imageSubresource.mipLevel;
         const uint32_t width = mipmapSize(mImgSize.width, mipLevel);
@@ -255,5 +266,26 @@
     }
 }
 
+void AstcTexture::on_vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
+                                size_t astcDataSize, VkImage dstImage,
+                                VkImageLayout dstImageLayout, uint32_t regionCount,
+                                const VkBufferImageCopy* pRegions,
+                                const VkDecoderContext& context) {
+    on_vkCmdCopyBufferToImageImpl(commandBuffer, srcAstcData, astcDataSize, dstImage, dstImageLayout, regionCount, pRegions, context);
+}
+
+void AstcTexture::on_vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
+                                size_t astcDataSize, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo,
+                                const VkDecoderContext& context) {
+    on_vkCmdCopyBufferToImageImpl(commandBuffer,
+                                  srcAstcData,
+                                  astcDataSize,
+                                  pCopyBufferToImageInfo->dstImage,
+                                  pCopyBufferToImageInfo->dstImageLayout,
+                                  pCopyBufferToImageInfo->regionCount,
+                                  pCopyBufferToImageInfo->pRegions,
+                                  context);
+}
+
 }  // namespace vk
 }  // namespace gfxstream
diff --git a/host/vulkan/emulated_textures/AstcTexture.h b/host/vulkan/emulated_textures/AstcTexture.h
index 5aa688d..3fead51 100644
--- a/host/vulkan/emulated_textures/AstcTexture.h
+++ b/host/vulkan/emulated_textures/AstcTexture.h
@@ -41,8 +41,19 @@
                                    VkImageLayout dstImageLayout, uint32_t regionCount,
                                    const VkBufferImageCopy* pRegions,
                                    const VkDecoderContext& context);
+    void on_vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
+                                   size_t astcDataSize, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo,
+                                   const VkDecoderContext& context);
 
    private:
+
+    template<typename T>
+    void on_vkCmdCopyBufferToImageImpl(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
+                                   size_t astcDataSize, VkImage dstImage,
+                                   VkImageLayout dstImageLayout, uint32_t regionCount,
+                                   const T* pRegions,
+                                   const VkDecoderContext& context);
+
     uint8_t* createVkBufferAndMapMemory(size_t bufferSize);
     void destroyVkBuffer();
 
diff --git a/host/vulkan/emulated_textures/CompressedImageInfo.cpp b/host/vulkan/emulated_textures/CompressedImageInfo.cpp
index 40e9b7c..6e179b3 100644
--- a/host/vulkan/emulated_textures/CompressedImageInfo.cpp
+++ b/host/vulkan/emulated_textures/CompressedImageInfo.cpp
@@ -513,6 +513,11 @@
                                             dstImageLayout, regionCount, pRegions, context);
 }
 
+void CompressedImageInfo::decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData, size_t astcDataSize,
+                         const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo, const VkDecoderContext& context) {
+    mAstcTexture->on_vkCmdCopyBufferToImage2(commandBuffer, srcAstcData, astcDataSize, pCopyBufferToImageInfo, context);
+}
+
 VkMemoryRequirements CompressedImageInfo::getMemoryRequirements() const {
     return mMemoryRequirements;
 }
@@ -541,6 +546,19 @@
     return region;
 }
 
+VkBufferImageCopy2 CompressedImageInfo::getBufferImageCopy(
+    const VkBufferImageCopy2& origRegion) const {
+    VkBufferImageCopy2 region = origRegion;
+    uint32_t mipLevel = region.imageSubresource.mipLevel;
+    region.imageSubresource.mipLevel = 0;
+    region.bufferRowLength /= mBlock.width;
+    region.bufferImageHeight /= mBlock.height;
+    region.imageOffset.x /= mBlock.width;
+    region.imageOffset.y /= mBlock.height;
+    region.imageExtent = compressedMipmapPortion(region.imageExtent, mipLevel);
+    return region;
+}
+
 // static
 VkImageCopy CompressedImageInfo::getCompressedMipmapsImageCopy(const VkImageCopy& origRegion,
                                                                const CompressedImageInfo& srcImg,
@@ -563,6 +581,27 @@
     return region;
 }
 
+VkImageCopy2 CompressedImageInfo::getCompressedMipmapsImageCopy(const VkImageCopy2& origRegion,
+                                                                const CompressedImageInfo& srcImg,
+                                                                const CompressedImageInfo& dstImg,
+                                                                bool needEmulatedSrc,
+                                                                bool needEmulatedDst) {
+    VkImageCopy2 region = origRegion;
+    if (needEmulatedSrc) {
+        uint32_t mipLevel = region.srcSubresource.mipLevel;
+        region.srcSubresource.mipLevel = 0;
+        region.srcOffset.x /= srcImg.mBlock.width;
+        region.srcOffset.y /= srcImg.mBlock.height;
+        region.extent = srcImg.compressedMipmapPortion(region.extent, mipLevel);
+    }
+    if (needEmulatedDst) {
+        region.dstSubresource.mipLevel = 0;
+        region.dstOffset.x /= dstImg.mBlock.width;
+        region.dstOffset.y /= dstImg.mBlock.height;
+    }
+    return region;
+}
+
 void CompressedImageInfo::destroy(VulkanDispatch* vk) {
     for (const auto& image : mCompressedMipmaps) {
         vk->vkDestroyImage(mDevice, image, nullptr);
diff --git a/host/vulkan/emulated_textures/CompressedImageInfo.h b/host/vulkan/emulated_textures/CompressedImageInfo.h
index 9d75ad1..0f0ddfa 100644
--- a/host/vulkan/emulated_textures/CompressedImageInfo.h
+++ b/host/vulkan/emulated_textures/CompressedImageInfo.h
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "host/vulkan/emulated_textures/AstcTexture.h"
-#include "host/vulkan/emulated_textures/AstcTexture.h"
 #include "host/vulkan/emulated_textures/GpuDecompressionPipeline.h"
 #include "vulkan/cereal/common/goldfish_vk_dispatch.h"
 #include "vulkan/vulkan.h"
@@ -47,6 +46,11 @@
                                                      const CompressedImageInfo& srcImg,
                                                      const CompressedImageInfo& dstImg,
                                                      bool needEmulatedSrc, bool needEmulatedDst);
+    static VkImageCopy2 getCompressedMipmapsImageCopy(const VkImageCopy2& origRegion,
+                                                      const CompressedImageInfo& srcImg,
+                                                      const CompressedImageInfo& dstImg,
+                                                      bool needEmulatedSrc, bool needEmulatedDst);
+
 
     // Constructors
 
@@ -87,6 +91,9 @@
                          VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
                          const VkBufferImageCopy* pRegions, const VkDecoderContext& context);
 
+    void decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData, size_t astcDataSize,
+                         const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo, const VkDecoderContext& context);
+
     VkMemoryRequirements getMemoryRequirements() const;
 
     VkResult bindCompressedMipmapsMemory(VulkanDispatch* vk, VkDeviceMemory memory,
@@ -95,6 +102,7 @@
     // Given a VkBufferImageCopy object for the original image, returns a new
     // VkBufferImageCopy that points to the same location in the compressed mipmap images.
     VkBufferImageCopy getBufferImageCopy(const VkBufferImageCopy& origRegion) const;
+    VkBufferImageCopy2 getBufferImageCopy(const VkBufferImageCopy2& origRegion) const;
 
     // Releases all the resources used by this class. It may no longer be used after calling this.
     void destroy(VulkanDispatch* vk);