// Copyright 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "vulkan/VkDecoderSnapshotUtils.h" #include "VkCommonOperations.h" namespace gfxstream { namespace vk { namespace { uint32_t GetMemoryType(const PhysicalDeviceInfo& physicalDevice, const VkMemoryRequirements& memoryRequirements, VkMemoryPropertyFlags memoryProperties) { const auto& props = physicalDevice.memoryPropertiesHelper->getHostMemoryProperties(); for (uint32_t i = 0; i < props.memoryTypeCount; i++) { if (!(memoryRequirements.memoryTypeBits & (1 << i))) { continue; } if ((props.memoryTypes[i].propertyFlags & memoryProperties) != memoryProperties) { continue; } return i; } GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Cannot find memory type for snapshot save " << __func__ << " (" << __FILE__ << ":" << __LINE__ << ")"; } uint32_t bytes_per_pixel(VkFormat format) { switch (format) { case VK_FORMAT_R8_UNORM: case VK_FORMAT_R8_SNORM: case VK_FORMAT_R8_USCALED: case VK_FORMAT_R8_SSCALED: case VK_FORMAT_R8_UINT: case VK_FORMAT_R8_SINT: case VK_FORMAT_R8_SRGB: case VK_FORMAT_S8_UINT: return 1; case VK_FORMAT_R8G8_UNORM: case VK_FORMAT_R8G8_SNORM: case VK_FORMAT_R8G8_USCALED: case VK_FORMAT_R8G8_SSCALED: case VK_FORMAT_R8G8_UINT: case VK_FORMAT_R8G8_SINT: case VK_FORMAT_R8G8_SRGB: return 2; case VK_FORMAT_R8G8B8_UNORM: case VK_FORMAT_R8G8B8_SNORM: case VK_FORMAT_R8G8B8_USCALED: case VK_FORMAT_R8G8B8_SSCALED: case VK_FORMAT_R8G8B8_UINT: case VK_FORMAT_R8G8B8_SINT: case VK_FORMAT_R8G8B8_SRGB: case VK_FORMAT_B8G8R8_UNORM: case VK_FORMAT_B8G8R8_SNORM: case VK_FORMAT_B8G8R8_USCALED: case VK_FORMAT_B8G8R8_SSCALED: case VK_FORMAT_B8G8R8_UINT: case VK_FORMAT_B8G8R8_SINT: case VK_FORMAT_B8G8R8_SRGB: case VK_FORMAT_D16_UNORM_S8_UINT: return 3; case VK_FORMAT_R8G8B8A8_UNORM: case VK_FORMAT_R8G8B8A8_SNORM: case VK_FORMAT_R8G8B8A8_USCALED: case VK_FORMAT_R8G8B8A8_SSCALED: case VK_FORMAT_R8G8B8A8_UINT: case VK_FORMAT_R8G8B8A8_SINT: case VK_FORMAT_R8G8B8A8_SRGB: case VK_FORMAT_B8G8R8A8_UNORM: case VK_FORMAT_B8G8R8A8_SNORM: case VK_FORMAT_B8G8R8A8_USCALED: case VK_FORMAT_B8G8R8A8_SSCALED: case VK_FORMAT_B8G8R8A8_UINT: case VK_FORMAT_B8G8R8A8_SINT: case VK_FORMAT_B8G8R8A8_SRGB: case VK_FORMAT_A8B8G8R8_UNORM_PACK32: case VK_FORMAT_A8B8G8R8_SNORM_PACK32: case VK_FORMAT_A8B8G8R8_USCALED_PACK32: case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: case VK_FORMAT_A8B8G8R8_UINT_PACK32: case VK_FORMAT_A8B8G8R8_SINT_PACK32: case VK_FORMAT_A8B8G8R8_SRGB_PACK32: case VK_FORMAT_D24_UNORM_S8_UINT: return 4; default: GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Unsupported VkFormat on snapshot save " << format << " " << __func__ << " (" << __FILE__ << ":" << __LINE__ << ")"; } } VkExtent3D getMipmapExtent(VkExtent3D baseExtent, uint32_t mipLevel) { return VkExtent3D{ .width = (baseExtent.width + (1 << mipLevel) - 1) >> mipLevel, .height = (baseExtent.height + (1 << mipLevel) - 1) >> mipLevel, .depth = baseExtent.depth, }; } } // namespace #define _RUN_AND_CHECK(command) \ { \ if (command) \ GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) \ << "Vulkan snapshot save failed at " << __func__ << " (" << __FILE__ << ":" \ << __LINE__ << ")"; \ } void saveImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image, const ImageInfo* imageInfo) { if (imageInfo->layout == VK_IMAGE_LAYOUT_UNDEFINED) { return; } // TODO(b/333936705): snapshot multi-sample images if (imageInfo->imageCreateInfoShallow.samples != VK_SAMPLE_COUNT_1_BIT) { return; } VulkanDispatch* dispatch = stateBlock->deviceDispatch; const VkImageCreateInfo& imageCreateInfo = imageInfo->imageCreateInfoShallow; VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = stateBlock->commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer commandBuffer; _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo, &commandBuffer) != VK_SUCCESS); VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; VkFence fence; _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence)); VkBufferCreateInfo bufferCreateInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = static_cast( imageCreateInfo.extent.width * imageCreateInfo.extent.height * imageCreateInfo.extent.depth * bytes_per_pixel(imageCreateInfo.format)), .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; VkBuffer readbackBuffer; _RUN_AND_CHECK( dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &readbackBuffer)); VkMemoryRequirements readbackBufferMemoryRequirements{}; dispatch->vkGetBufferMemoryRequirements(stateBlock->device, readbackBuffer, &readbackBufferMemoryRequirements); const auto readbackBufferMemoryType = GetMemoryType(*stateBlock->physicalDeviceInfo, readbackBufferMemoryRequirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // Staging memory // TODO(b/323064243): reuse staging memory VkMemoryAllocateInfo readbackBufferMemoryAllocateInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = readbackBufferMemoryRequirements.size, .memoryTypeIndex = readbackBufferMemoryType, }; VkDeviceMemory readbackMemory; _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &readbackBufferMemoryAllocateInfo, nullptr, &readbackMemory)); _RUN_AND_CHECK( dispatch->vkBindBufferMemory(stateBlock->device, readbackBuffer, readbackMemory, 0)); void* mapped = nullptr; _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, readbackMemory, 0, VK_WHOLE_SIZE, VkMemoryMapFlags{}, &mapped)); for (uint32_t mipLevel = 0; mipLevel < imageInfo->imageCreateInfoShallow.mipLevels; mipLevel++) { for (uint32_t arrayLayer = 0; arrayLayer < imageInfo->imageCreateInfoShallow.arrayLayers; arrayLayer++) { // TODO(b/323064243): reuse command buffers VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Failed to start command buffer on snapshot save"; } // TODO(b/323059453): separate stencil and depth images properly VkExtent3D mipmapExtent = getMipmapExtent(imageCreateInfo.extent, mipLevel); VkImageAspectFlags aspects = imageCreateInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; VkImageLayout layoutBeforeSave = imageInfo->layout; VkImageMemoryBarrier imgMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = static_cast(~VK_ACCESS_NONE_KHR), .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = layoutBeforeSave, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = VkImageSubresourceRange{.aspectMask = aspects, .baseMipLevel = mipLevel, .levelCount = 1, .baseArrayLayer = arrayLayer, .layerCount = 1}}; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imgMemoryBarrier); VkBufferImageCopy region{ .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, .imageSubresource = VkImageSubresourceLayers{.aspectMask = aspects, .mipLevel = mipLevel, .baseArrayLayer = arrayLayer, .layerCount = 1}, .imageOffset = VkOffset3D{ .x = 0, .y = 0, .z = 0, }, .imageExtent = mipmapExtent, }; dispatch->vkCmdCopyImageToBuffer(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuffer, 1, ®ion); // Cannot really translate it back to VK_IMAGE_LAYOUT_PREINITIALIZED if (layoutBeforeSave != VK_IMAGE_LAYOUT_PREINITIALIZED) { imgMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; imgMemoryBarrier.newLayout = layoutBeforeSave; imgMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; imgMemoryBarrier.dstAccessMask = ~VK_ACCESS_NONE_KHR; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imgMemoryBarrier); } _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer)); // Execute the command to copy image VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, }; _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence)); _RUN_AND_CHECK( dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L)); _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence)); size_t bytes = mipmapExtent.width * mipmapExtent.height * mipmapExtent.depth * bytes_per_pixel(imageCreateInfo.format); stream->putBe64(bytes); stream->write(mapped, bytes); } } dispatch->vkDestroyFence(stateBlock->device, fence, nullptr); dispatch->vkUnmapMemory(stateBlock->device, readbackMemory); dispatch->vkDestroyBuffer(stateBlock->device, readbackBuffer, nullptr); dispatch->vkFreeMemory(stateBlock->device, readbackMemory, nullptr); dispatch->vkFreeCommandBuffers(stateBlock->device, stateBlock->commandPool, 1, &commandBuffer); } void loadImageContent(android::base::Stream* stream, StateBlock* stateBlock, VkImage image, const ImageInfo* imageInfo) { if (imageInfo->layout == VK_IMAGE_LAYOUT_UNDEFINED) { return; } VulkanDispatch* dispatch = stateBlock->deviceDispatch; const VkImageCreateInfo& imageCreateInfo = imageInfo->imageCreateInfoShallow; VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = stateBlock->commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer commandBuffer; _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo, &commandBuffer) != VK_SUCCESS); VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; VkFence fence; _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence)); if (imageInfo->imageCreateInfoShallow.samples != VK_SAMPLE_COUNT_1_BIT) { // Set the layout and quit // TODO: resolve and save image content VkImageAspectFlags aspects = imageCreateInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; VkImageMemoryBarrier imgMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = static_cast(~VK_ACCESS_NONE_KHR), .dstAccessMask = static_cast(~VK_ACCESS_NONE_KHR), .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = imageInfo->layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = VkImageSubresourceRange{.aspectMask = aspects, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS}}; VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; _RUN_AND_CHECK(dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS); dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imgMemoryBarrier); _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer)); // Execute the command to copy image VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, }; _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence)); _RUN_AND_CHECK( dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L)); dispatch->vkDestroyFence(stateBlock->device, fence, nullptr); dispatch->vkFreeCommandBuffers(stateBlock->device, stateBlock->commandPool, 1, &commandBuffer); return; } VkBufferCreateInfo bufferCreateInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = static_cast( imageCreateInfo.extent.width * imageCreateInfo.extent.height * imageCreateInfo.extent.depth * bytes_per_pixel(imageCreateInfo.format)), .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; VkBuffer stagingBuffer; _RUN_AND_CHECK( dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &stagingBuffer)); VkMemoryRequirements stagingBufferMemoryRequirements{}; dispatch->vkGetBufferMemoryRequirements(stateBlock->device, stagingBuffer, &stagingBufferMemoryRequirements); const auto stagingBufferMemoryType = GetMemoryType(*stateBlock->physicalDeviceInfo, stagingBufferMemoryRequirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // Staging memory // TODO(b/323064243): reuse staging memory VkMemoryAllocateInfo stagingBufferMemoryAllocateInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = stagingBufferMemoryRequirements.size, .memoryTypeIndex = stagingBufferMemoryType, }; VkDeviceMemory stagingMemory; _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &stagingBufferMemoryAllocateInfo, nullptr, &stagingMemory)); _RUN_AND_CHECK( dispatch->vkBindBufferMemory(stateBlock->device, stagingBuffer, stagingMemory, 0)); void* mapped = nullptr; _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, stagingMemory, 0, VK_WHOLE_SIZE, VkMemoryMapFlags{}, &mapped)); for (uint32_t mipLevel = 0; mipLevel < imageInfo->imageCreateInfoShallow.mipLevels; mipLevel++) { for (uint32_t arrayLayer = 0; arrayLayer < imageInfo->imageCreateInfoShallow.arrayLayers; arrayLayer++) { // TODO(b/323064243): reuse command buffers VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Failed to start command buffer on snapshot save"; } VkExtent3D mipmapExtent = getMipmapExtent(imageCreateInfo.extent, mipLevel); size_t bytes = stream->getBe64(); stream->read(mapped, bytes); VkImageAspectFlags aspects = imageCreateInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; VkImageMemoryBarrier imgMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = static_cast(~VK_ACCESS_NONE_KHR), .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = VkImageSubresourceRange{.aspectMask = aspects, .baseMipLevel = mipLevel, .levelCount = 1, .baseArrayLayer = arrayLayer, .layerCount = 1}}; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imgMemoryBarrier); VkBufferImageCopy region{ .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, .imageSubresource = VkImageSubresourceLayers{.aspectMask = aspects, .mipLevel = mipLevel, .baseArrayLayer = arrayLayer, .layerCount = 1}, .imageOffset = VkOffset3D{ .x = 0, .y = 0, .z = 0, }, .imageExtent = mipmapExtent, }; dispatch->vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); // Cannot really translate it back to VK_IMAGE_LAYOUT_PREINITIALIZED if (imageInfo->layout != VK_IMAGE_LAYOUT_PREINITIALIZED) { imgMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imgMemoryBarrier.newLayout = imageInfo->layout; imgMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imgMemoryBarrier.dstAccessMask = ~VK_ACCESS_NONE_KHR; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imgMemoryBarrier); } _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer)); // Execute the command to copy image VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, }; _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence)); _RUN_AND_CHECK( dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L)); _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence)); } } dispatch->vkDestroyFence(stateBlock->device, fence, nullptr); dispatch->vkUnmapMemory(stateBlock->device, stagingMemory); dispatch->vkDestroyBuffer(stateBlock->device, stagingBuffer, nullptr); dispatch->vkFreeMemory(stateBlock->device, stagingMemory, nullptr); dispatch->vkFreeCommandBuffers(stateBlock->device, stateBlock->commandPool, 1, &commandBuffer); } void saveBufferContent(android::base::Stream* stream, StateBlock* stateBlock, VkBuffer buffer, const BufferInfo* bufferInfo) { VkBufferUsageFlags requiredUsages = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; if ((bufferInfo->usage & requiredUsages) != requiredUsages) { return; } VulkanDispatch* dispatch = stateBlock->deviceDispatch; VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = stateBlock->commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer commandBuffer; _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo, &commandBuffer) != VK_SUCCESS); VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; VkFence fence; _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence)); VkBufferCreateInfo bufferCreateInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = static_cast(bufferInfo->size), .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; VkBuffer readbackBuffer; _RUN_AND_CHECK( dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &readbackBuffer)); VkMemoryRequirements readbackBufferMemoryRequirements{}; dispatch->vkGetBufferMemoryRequirements(stateBlock->device, readbackBuffer, &readbackBufferMemoryRequirements); const auto readbackBufferMemoryType = GetMemoryType(*stateBlock->physicalDeviceInfo, readbackBufferMemoryRequirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // Staging memory // TODO(b/323064243): reuse staging memory VkMemoryAllocateInfo readbackBufferMemoryAllocateInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = readbackBufferMemoryRequirements.size, .memoryTypeIndex = readbackBufferMemoryType, }; VkDeviceMemory readbackMemory; _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &readbackBufferMemoryAllocateInfo, nullptr, &readbackMemory)); _RUN_AND_CHECK( dispatch->vkBindBufferMemory(stateBlock->device, readbackBuffer, readbackMemory, 0)); void* mapped = nullptr; _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, readbackMemory, 0, VK_WHOLE_SIZE, VkMemoryMapFlags{}, &mapped)); VkBufferCopy bufferCopy = { .srcOffset = 0, .dstOffset = 0, .size = bufferInfo->size, }; VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Failed to start command buffer on snapshot save"; } dispatch->vkCmdCopyBuffer(commandBuffer, buffer, readbackBuffer, 1, &bufferCopy); VkBufferMemoryBarrier barrier{.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_HOST_READ_BIT, .srcQueueFamilyIndex = 0xFFFFFFFF, .dstQueueFamilyIndex = 0xFFFFFFFF, .buffer = readbackBuffer, .offset = 0, .size = bufferInfo->size}; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr); // Execute the command to copy buffer VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, }; _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer)); _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence)); _RUN_AND_CHECK(dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L)); _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence)); stream->putBe64(bufferInfo->size); stream->write(mapped, bufferInfo->size); dispatch->vkDestroyFence(stateBlock->device, fence, nullptr); dispatch->vkUnmapMemory(stateBlock->device, readbackMemory); dispatch->vkDestroyBuffer(stateBlock->device, readbackBuffer, nullptr); dispatch->vkFreeMemory(stateBlock->device, readbackMemory, nullptr); dispatch->vkFreeCommandBuffers(stateBlock->device, stateBlock->commandPool, 1, &commandBuffer); } void loadBufferContent(android::base::Stream* stream, StateBlock* stateBlock, VkBuffer buffer, const BufferInfo* bufferInfo) { VkBufferUsageFlags requiredUsages = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; if ((bufferInfo->usage & requiredUsages) != requiredUsages) { return; } VulkanDispatch* dispatch = stateBlock->deviceDispatch; VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = stateBlock->commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer commandBuffer; _RUN_AND_CHECK(dispatch->vkAllocateCommandBuffers(stateBlock->device, &allocInfo, &commandBuffer) != VK_SUCCESS); VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; VkFence fence; _RUN_AND_CHECK(dispatch->vkCreateFence(stateBlock->device, &fenceCreateInfo, nullptr, &fence)); VkBufferCreateInfo bufferCreateInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = static_cast(bufferInfo->size), .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; VkBuffer stagingBuffer; _RUN_AND_CHECK( dispatch->vkCreateBuffer(stateBlock->device, &bufferCreateInfo, nullptr, &stagingBuffer)); VkMemoryRequirements stagingBufferMemoryRequirements{}; dispatch->vkGetBufferMemoryRequirements(stateBlock->device, stagingBuffer, &stagingBufferMemoryRequirements); const auto stagingBufferMemoryType = GetMemoryType(*stateBlock->physicalDeviceInfo, stagingBufferMemoryRequirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // Staging memory // TODO(b/323064243): reuse staging memory VkMemoryAllocateInfo stagingBufferMemoryAllocateInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = stagingBufferMemoryRequirements.size, .memoryTypeIndex = stagingBufferMemoryType, }; VkDeviceMemory stagingMemory; _RUN_AND_CHECK(dispatch->vkAllocateMemory(stateBlock->device, &stagingBufferMemoryAllocateInfo, nullptr, &stagingMemory)); _RUN_AND_CHECK( dispatch->vkBindBufferMemory(stateBlock->device, stagingBuffer, stagingMemory, 0)); void* mapped = nullptr; _RUN_AND_CHECK(dispatch->vkMapMemory(stateBlock->device, stagingMemory, 0, VK_WHOLE_SIZE, VkMemoryMapFlags{}, &mapped)); size_t bufferSize = stream->getBe64(); assert(bufferSize == bufferInfo->size); stream->read(mapped, bufferInfo->size); VkBufferCopy bufferCopy = { .srcOffset = 0, .dstOffset = 0, .size = bufferInfo->size, }; VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; if (dispatch->vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) << "Failed to start command buffer on snapshot save"; } dispatch->vkCmdCopyBuffer(commandBuffer, stagingBuffer, buffer, 1, &bufferCopy); VkBufferMemoryBarrier barrier{.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = static_cast(~VK_ACCESS_NONE_KHR), .srcQueueFamilyIndex = 0xFFFFFFFF, .dstQueueFamilyIndex = 0xFFFFFFFF, .buffer = buffer, .offset = 0, .size = bufferInfo->size}; dispatch->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr); // Execute the command to copy buffer VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, }; _RUN_AND_CHECK(dispatch->vkEndCommandBuffer(commandBuffer)); _RUN_AND_CHECK(dispatch->vkQueueSubmit(stateBlock->queue, 1, &submitInfo, fence)); _RUN_AND_CHECK(dispatch->vkWaitForFences(stateBlock->device, 1, &fence, VK_TRUE, 3000000000L)); _RUN_AND_CHECK(dispatch->vkResetFences(stateBlock->device, 1, &fence)); dispatch->vkDestroyFence(stateBlock->device, fence, nullptr); dispatch->vkUnmapMemory(stateBlock->device, stagingMemory); dispatch->vkDestroyBuffer(stateBlock->device, stagingBuffer, nullptr); dispatch->vkFreeMemory(stateBlock->device, stagingMemory, nullptr); dispatch->vkFreeCommandBuffers(stateBlock->device, stateBlock->commandPool, 1, &commandBuffer); } } // namespace vk } // namespace gfxstream