blob: 7d75cfe775252471566d159d7620c32e9d636a95 [file] [log] [blame]
// 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:
case VK_FORMAT_D16_UNORM:
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_A2R10G10B10_UNORM_PACK32:
case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_R16G16_SFLOAT:
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
case VK_FORMAT_X8_D24_UNORM_PACK32:
return 4;
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
return 8;
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
return 16;
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 >> mipLevel,
.height = baseExtent.height >> 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<VkDeviceSize>(
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<VkAccessFlags>(~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, &region);
// 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<VkAccessFlags>(~VK_ACCESS_NONE_KHR),
.dstAccessMask = static_cast<VkAccessFlags>(~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<VkDeviceSize>(
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<VkAccessFlags>(~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, &region);
// 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<VkDeviceSize>(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<VkDeviceSize>(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<VkAccessFlags>(~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