Read/Update ColorBuffers from VK when using guest ANGLE ... to avoid the need for any GL operations. Bug: b/229145718 Test: `launch_cvd --gpu_mode=gfxstream` Test: `launch_cvd --gpu_mode=gfxstream` with WIP ANGLE Change-Id: I8fec6a702aac9b0047318887fc7dfcf3f20c2e3c
diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp index 2af304b..cfc1c42 100644 --- a/stream-servers/vulkan/VkCommonOperations.cpp +++ b/stream-servers/vulkan/VkCommonOperations.cpp
@@ -26,6 +26,7 @@ #include <unordered_set> #include "FrameBuffer.h" +#include "VkFormatUtils.h" #include "VulkanDispatch.h" #include "base/Lock.h" #include "base/Lookup.h" @@ -1849,332 +1850,407 @@ return res; } -bool updateColorBufferFromVkImage(uint32_t colorBufferHandle) { - if (!sVkEmulation || !sVkEmulation->live) return false; - - auto vk = sVkEmulation->dvk; - - AutoLock lock(sVkEmulationLock); - - auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); - - if (!infoPtr) { - // Color buffer not found; this is usually OK. +bool colorBufferNeedsTransferBetweenGlAndVk(const VkEmulation::ColorBufferInfo& colorBufferInfo) { + // GL is not used. + if (colorBufferInfo.vulkanMode == VkEmulation::VulkanMode::VulkanOnly) { return false; } - if (!infoPtr->image) { - fprintf(stderr, "%s: error: ColorBuffer 0x%x has no VkImage\n", __func__, - colorBufferHandle); - return false; - } - - if (infoPtr->glExported || (infoPtr->vulkanMode == VkEmulation::VulkanMode::VulkanOnly) || - infoPtr->frameworkFormat != FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE) { - // No sync needed if exported to GL or in Vulkan-only mode + // YUV formats require extra conversions. + if (colorBufferInfo.frameworkFormat != FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE) { return true; } - // Record our synchronization commands. - VkCommandBufferBeginInfo beginInfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - 0, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - nullptr /* no inheritance info */, - }; - - vk->vkBeginCommandBuffer(sVkEmulation->commandBuffer, &beginInfo); - - // From the spec: If an application does not need the contents of a resource - // to remain valid when transferring from one queue family to another, then - // the ownership transfer should be skipped. - - // We definitely need to transition the image to - // VK_TRANSFER_SRC_OPTIMAL and back. - - VkImageMemoryBarrier presentToTransferSrc = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - 0, - 0, - VK_ACCESS_HOST_READ_BIT, - infoPtr->currentLayout, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - infoPtr->image, - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - }, - }; - - vk->vkCmdPipelineBarrier(sVkEmulation->commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, - &presentToTransferSrc); - - infoPtr->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - // Copy to staging buffer - uint32_t bpp = 4; /* format always rgba8...not */ - switch (infoPtr->imageCreateInfoShallow.format) { - case VK_FORMAT_R5G6B5_UNORM_PACK16: - bpp = 2; - break; - case VK_FORMAT_R8G8B8_UNORM: - bpp = 3; - break; - default: - case VK_FORMAT_R8G8B8A8_UNORM: - bpp = 4; - break; + // GL and VK are sharing the same underlying memory. + if (colorBufferInfo.glExported) { + return false; } - VkBufferImageCopy region = { - 0 /* buffer offset */, - infoPtr->imageCreateInfoShallow.extent.width, - infoPtr->imageCreateInfoShallow.extent.height, - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 0, - 1, - }, - {0, 0, 0}, - infoPtr->imageCreateInfoShallow.extent, - }; - - vk->vkCmdCopyImageToBuffer(sVkEmulation->commandBuffer, infoPtr->image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, sVkEmulation->staging.buffer, - 1, ®ion); - - vk->vkEndCommandBuffer(sVkEmulation->commandBuffer); - - VkSubmitInfo submitInfo = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, 0, nullptr, nullptr, 1, - &sVkEmulation->commandBuffer, 0, nullptr, - }; - - { - android::base::AutoLock lock(*sVkEmulation->queueLock); - vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, sVkEmulation->commandBufferFence); - } - - static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL; - - vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence, VK_TRUE, - ANB_MAX_WAIT_NS); - vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence); - - VkMappedMemoryRange toInvalidate = { - VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - 0, - sVkEmulation->staging.memory.memory, - 0, - VK_WHOLE_SIZE, - }; - - vk->vkInvalidateMappedMemoryRanges(sVkEmulation->device, 1, &toInvalidate); - - const std::size_t copiedSize = infoPtr->imageCreateInfoShallow.extent.width * - infoPtr->imageCreateInfoShallow.extent.height * bpp; - - FrameBuffer::getFB()->replaceColorBufferContents( - colorBufferHandle, sVkEmulation->staging.memory.mappedPtr, copiedSize); return true; } -bool updateVkImageFromColorBuffer(uint32_t colorBufferHandle) { - if (!sVkEmulation || !sVkEmulation->live) return false; +bool readColorBufferToGl(uint32_t colorBufferHandle) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); + return false; + } auto vk = sVkEmulation->dvk; AutoLock lock(sVkEmulationLock); - auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); - - if (!infoPtr) { - // Color buffer not found; this is usually OK. + auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + if (!colorBufferInfo) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, not found.", colorBufferHandle); return false; } - if (infoPtr->frameworkFormat == FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE && - (infoPtr->glExported || infoPtr->vulkanMode == VkEmulation::VulkanMode::VulkanOnly)) { - // No sync needed if exported to GL or in Vulkan-only mode + if (!colorBufferNeedsTransferBetweenGlAndVk(*colorBufferInfo)) { return true; } - size_t cbNumBytes = 0; - bool readRes = - FrameBuffer::getFB()->readColorBufferContents(colorBufferHandle, &cbNumBytes, nullptr); - if (!readRes) { - fprintf(stderr, "%s: Failed to read color buffer 0x%x\n", __func__, colorBufferHandle); + VkDeviceSize bytesNeeded = 0; + bool result = getFormatTransferInfo(colorBufferInfo->imageCreateInfoShallow.format, + colorBufferInfo->imageCreateInfoShallow.extent.width, + colorBufferInfo->imageCreateInfoShallow.extent.height, + &bytesNeeded, nullptr); + if (!result) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, failed to get read size.", + colorBufferHandle); return false; } - if (cbNumBytes > sVkEmulation->staging.memory.size) { - fprintf(stderr, - "%s: Not enough space to read to staging buffer. " - "Wanted: 0x%llx Have: 0x%llx\n", - __func__, (unsigned long long)cbNumBytes, - (unsigned long long)(sVkEmulation->staging.memory.size)); + std::vector<uint8_t> bytes(bytesNeeded); + + result = readColorBufferToBytes( + colorBufferHandle, 0, 0, colorBufferInfo->imageCreateInfoShallow.extent.width, + colorBufferInfo->imageCreateInfoShallow.extent.height, bytes.data()); + if (!result) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, failed to get read size.", + colorBufferHandle); return false; } - readRes = FrameBuffer::getFB()->readColorBufferContents(colorBufferHandle, &cbNumBytes, - sVkEmulation->staging.memory.mappedPtr); + return FrameBuffer::getFB()->replaceColorBufferContents(colorBufferHandle, bytes.data(), + bytes.size()); +} - if (!readRes) { - fprintf(stderr, "%s: Failed to read color buffer 0x%x (at glReadPixels)\n", __func__, - colorBufferHandle); +bool readColorBufferToBytes(uint32_t colorBufferHandle, uint32_t x, uint32_t y, uint32_t w, + uint32_t h, void* outPixels) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); return false; } + auto vk = sVkEmulation->dvk; + + AutoLock lock(sVkEmulationLock); + return readColorBufferToBytesLocked(colorBufferHandle, x, y, w, h, outPixels); +} + +bool readColorBufferToBytesLocked(uint32_t colorBufferHandle, uint32_t x, uint32_t y, uint32_t w, + uint32_t h, void* outPixels) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); + return false; + } + + auto vk = sVkEmulation->dvk; + + auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + if (!colorBufferInfo) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, not found.", colorBufferHandle); + return false; + } + + if (!colorBufferInfo->image) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, no VkImage.", colorBufferHandle); + return false; + } + + if (x != 0 || y != 0 || w != colorBufferInfo->imageCreateInfoShallow.extent.width || + h != colorBufferInfo->imageCreateInfoShallow.extent.height) { + VK_COMMON_ERROR("Failed to read from ColorBuffer:%d, unhandled subrect.", + colorBufferHandle); + return false; + } + + std::size_t bufferCopySize = 0; + std::vector<VkBufferImageCopy> bufferImageCopies; + if (!getFormatTransferInfo(colorBufferInfo->imageCreateInfoShallow.format, + colorBufferInfo->imageCreateInfoShallow.extent.width, + colorBufferInfo->imageCreateInfoShallow.extent.height, + &bufferCopySize, &bufferImageCopies)) { + VK_COMMON_ERROR("Failed to read ColorBuffer:%d, unable to get transfer info.", + colorBufferHandle); + return false; + } + + // Avoid transitioning from VK_IMAGE_LAYOUT_UNDEFINED. Unfortunetly, Android does not + // yet have a mechanism for sharing the expected VkImageLayout. However, the Vulkan + // spec's image layout transition sections says "If the old layout is + // VK_IMAGE_LAYOUT_UNDEFINED, the contents of that range may be discarded." Some + // Vulkan drivers have been observed to actually perform the discard which leads to + // ColorBuffer-s being unintentionally cleared. See go/ahb-vkimagelayout for a more + // thorough write up. + if (colorBufferInfo->currentLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + colorBufferInfo->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } + // Record our synchronization commands. - VkCommandBufferBeginInfo beginInfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - 0, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - nullptr /* no inheritance info */, + const VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; - vk->vkBeginCommandBuffer(sVkEmulation->commandBuffer, &beginInfo); + VkCommandBuffer commandBuffer = sVkEmulation->commandBuffer; - // From the spec: If an application does not need the contents of a resource - // to remain valid when transferring from one queue family to another, then - // the ownership transfer should be skipped. + VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo)); - // We definitely need to transition the image to - // VK_TRANSFER_SRC_OPTIMAL and back. - - VkImageMemoryBarrier presentToTransferSrc = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - 0, - 0, - VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - infoPtr->currentLayout, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - infoPtr->image, - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - }, + const VkImageMemoryBarrier toTransferSrcImageBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_HOST_READ_BIT, + .oldLayout = colorBufferInfo->currentLayout, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = colorBufferInfo->image, + .subresourceRange = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, }; - infoPtr->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - vk->vkCmdPipelineBarrier(sVkEmulation->commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, - &presentToTransferSrc); + &toTransferSrcImageBarrier); - // Copy to staging buffer - std::vector<VkBufferImageCopy> regions; - if (infoPtr->frameworkFormat == FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE) { - regions.push_back({ - 0 /* buffer offset */, - infoPtr->imageCreateInfoShallow.extent.width, - infoPtr->imageCreateInfoShallow.extent.height, - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 0, - 1, - }, - {0, 0, 0}, - infoPtr->imageCreateInfoShallow.extent, - }); - } else { - // YUV formats - bool swapUV = infoPtr->frameworkFormat == FRAMEWORK_FORMAT_YV12; - VkExtent3D subplaneExtent = {infoPtr->imageCreateInfoShallow.extent.width / 2, - infoPtr->imageCreateInfoShallow.extent.height / 2, 1}; - regions.push_back({ - 0 /* buffer offset */, - infoPtr->imageCreateInfoShallow.extent.width, - infoPtr->imageCreateInfoShallow.extent.height, - { - VK_IMAGE_ASPECT_PLANE_0_BIT, - 0, - 0, - 1, - }, - {0, 0, 0}, - infoPtr->imageCreateInfoShallow.extent, - }); - regions.push_back({ - infoPtr->imageCreateInfoShallow.extent.width * - infoPtr->imageCreateInfoShallow.extent.height /* buffer offset */, - subplaneExtent.width, - subplaneExtent.height, - { - (VkImageAspectFlags)(swapUV ? VK_IMAGE_ASPECT_PLANE_2_BIT - : VK_IMAGE_ASPECT_PLANE_1_BIT), - 0, - 0, - 1, - }, - {0, 0, 0}, - subplaneExtent, - }); - if (infoPtr->frameworkFormat == FRAMEWORK_FORMAT_YUV_420_888 || - infoPtr->frameworkFormat == FRAMEWORK_FORMAT_YV12) { - regions.push_back({ - infoPtr->imageCreateInfoShallow.extent.width * - infoPtr->imageCreateInfoShallow.extent.height + - subplaneExtent.width * subplaneExtent.height, - subplaneExtent.width, - subplaneExtent.height, - { - (VkImageAspectFlags)(swapUV ? VK_IMAGE_ASPECT_PLANE_1_BIT - : VK_IMAGE_ASPECT_PLANE_2_BIT), - 0, - 0, - 1, - }, - {0, 0, 0}, - subplaneExtent, - }); - } - } + colorBufferInfo->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - vk->vkCmdCopyBufferToImage(sVkEmulation->commandBuffer, sVkEmulation->staging.buffer, - infoPtr->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions.size(), - regions.data()); + vk->vkCmdCopyImageToBuffer(commandBuffer, colorBufferInfo->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, sVkEmulation->staging.buffer, + bufferImageCopies.size(), bufferImageCopies.data()); - vk->vkEndCommandBuffer(sVkEmulation->commandBuffer); + VK_CHECK(vk->vkEndCommandBuffer(commandBuffer)); - VkSubmitInfo submitInfo = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, 0, nullptr, nullptr, 1, - &sVkEmulation->commandBuffer, 0, nullptr, + const VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &commandBuffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, }; { android::base::AutoLock lock(*sVkEmulation->queueLock); - vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, sVkEmulation->commandBufferFence); + VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, + sVkEmulation->commandBufferFence)); } static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL; - vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence, VK_TRUE, - ANB_MAX_WAIT_NS); - vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence); + VK_CHECK(vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence, + VK_TRUE, ANB_MAX_WAIT_NS)); - VkMappedMemoryRange toInvalidate = { - VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - 0, - sVkEmulation->staging.memory.memory, - 0, - VK_WHOLE_SIZE, + VK_CHECK(vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence)); + + const VkMappedMemoryRange toInvalidate = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .pNext = nullptr, + .memory = sVkEmulation->staging.memory.memory, + .offset = 0, + .size = VK_WHOLE_SIZE, }; - vk->vkInvalidateMappedMemoryRanges(sVkEmulation->device, 1, &toInvalidate); + VK_CHECK(vk->vkInvalidateMappedMemoryRanges(sVkEmulation->device, 1, &toInvalidate)); + + const auto* stagingBufferPtr = sVkEmulation->staging.memory.mappedPtr; + std::memcpy(outPixels, stagingBufferPtr, bufferCopySize); + + return true; +} + +bool updateColorBufferFromGl(uint32_t colorBufferHandle) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); + return false; + } + + AutoLock lock(sVkEmulationLock); + + auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + if (!colorBufferInfo) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, not found.", colorBufferHandle); + return false; + } + + if (!colorBufferNeedsTransferBetweenGlAndVk(*colorBufferInfo)) { + return true; + } + + size_t bytesNeeded = 0; + bool result = + FrameBuffer::getFB()->readColorBufferContents(colorBufferHandle, &bytesNeeded, nullptr); + if (!result) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, failed to get read contents size.", + colorBufferHandle); + return false; + } + + std::vector<uint8_t> bytes(bytesNeeded); + result = FrameBuffer::getFB()->readColorBufferContents(colorBufferHandle, &bytesNeeded, + bytes.data()); + if (!result) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, failed to read contents.", + colorBufferHandle); + return false; + } + + return updateColorBufferFromBytesLocked( + colorBufferHandle, 0, 0, colorBufferInfo->imageCreateInfoShallow.extent.width, + colorBufferInfo->imageCreateInfoShallow.extent.height, bytes.data()); +} + +bool updateColorBufferFromBytes(uint32_t colorBufferHandle, uint32_t x, uint32_t y, uint32_t w, + uint32_t h, const void* pixels) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); + return false; + } + + auto vk = sVkEmulation->dvk; + AutoLock lock(sVkEmulationLock); + return updateColorBufferFromBytesLocked(colorBufferHandle, x, y, w, h, pixels); +} + +bool updateColorBufferFromBytesLocked(uint32_t colorBufferHandle, uint32_t x, uint32_t y, + uint32_t w, uint32_t h, const void* pixels) { + if (!sVkEmulation || !sVkEmulation->live) { + VK_COMMON_ERROR("VkEmulation not available."); + return false; + } + + auto vk = sVkEmulation->dvk; + + auto colorBufferInfo = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + if (!colorBufferInfo) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, not found.", colorBufferHandle); + return false; + } + + if (!colorBufferInfo->image) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, no VkImage.", colorBufferHandle); + return false; + } + + if (x != 0 || y != 0 || w != colorBufferInfo->imageCreateInfoShallow.extent.width || + h != colorBufferInfo->imageCreateInfoShallow.extent.height) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, unhandled subrect.", colorBufferHandle); + return false; + } + + std::size_t bufferCopySize = 0; + std::vector<VkBufferImageCopy> bufferImageCopies; + if (!getFormatTransferInfo(colorBufferInfo->imageCreateInfoShallow.format, + colorBufferInfo->imageCreateInfoShallow.extent.width, + colorBufferInfo->imageCreateInfoShallow.extent.height, + &bufferCopySize, &bufferImageCopies)) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, unable to get transfer info.", + colorBufferHandle); + return false; + } + + const VkDeviceSize stagingBufferSize = sVkEmulation->staging.size; + if (bufferCopySize > stagingBufferSize) { + VK_COMMON_ERROR("Failed to update ColorBuffer:%d, transfer size %" PRIu64 + " too large for staging buffer size:%" PRIu64 ".", + colorBufferHandle, bufferCopySize, stagingBufferSize); + return false; + } + + auto* stagingBufferPtr = sVkEmulation->staging.memory.mappedPtr; + std::memcpy(stagingBufferPtr, pixels, bufferCopySize); + + // Avoid transitioning from VK_IMAGE_LAYOUT_UNDEFINED. Unfortunetly, Android does not + // yet have a mechanism for sharing the expected VkImageLayout. However, the Vulkan + // spec's image layout transition sections says "If the old layout is + // VK_IMAGE_LAYOUT_UNDEFINED, the contents of that range may be discarded." Some + // Vulkan drivers have been observed to actually perform the discard which leads to + // ColorBuffer-s being unintentionally cleared. See go/ahb-vkimagelayout for a more + // thorough write up. + if (colorBufferInfo->currentLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + colorBufferInfo->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + + // Record our synchronization commands. + const VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + VkCommandBuffer commandBuffer = sVkEmulation->commandBuffer; + + VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo)); + + const VkImageMemoryBarrier toTransferDstImageBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = colorBufferInfo->currentLayout, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = colorBufferInfo->image, + .subresourceRange = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, + &toTransferDstImageBarrier); + + colorBufferInfo->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + // Copy to staging buffer + vk->vkCmdCopyBufferToImage(commandBuffer, sVkEmulation->staging.buffer, colorBufferInfo->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, bufferImageCopies.size(), + bufferImageCopies.data()); + + VK_CHECK(vk->vkEndCommandBuffer(commandBuffer)); + + const VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &commandBuffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + + { + android::base::AutoLock lock(*sVkEmulation->queueLock); + VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo, + sVkEmulation->commandBufferFence)); + } + + static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL; + + VK_CHECK(vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence, + VK_TRUE, ANB_MAX_WAIT_NS)); + + VK_CHECK(vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence)); + + const VkMappedMemoryRange toInvalidate = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .pNext = nullptr, + .memory = sVkEmulation->staging.memory.memory, + .offset = 0, + .size = VK_WHOLE_SIZE, + }; + VK_CHECK(vk->vkInvalidateMappedMemoryRanges(sVkEmulation->device, 1, &toInvalidate)); + return true; }