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;
}