Skip to content

Instantly share code, notes, and snippets.

@mdomrach
Created August 14, 2017 13:47
Show Gist options
  • Save mdomrach/d743dc1b7a80fe32250662e626218bc7 to your computer and use it in GitHub Desktop.
Save mdomrach/d743dc1b7a80fe32250662e626218bc7 to your computer and use it in GitHub Desktop.
ScreenshotDepth
void FVulkanScreenGrab::ScreenshotDepth(FVulkanDevice vulkanDevice, FVulkanSwapChain swapChain, VkCommandPool commandPool, VkQueue queue, VkImage srcImage, const char* filename)
{
// Get format properties for the swapchain color format
VkFormatProperties formatProps;
auto depthFormat = FVulkanCalculator::FindDepthFormat(vulkanDevice.physicalDevice);
// Check if the device supports blitting from optimal images (the swapchain images are in optimal format)
vkGetPhysicalDeviceFormatProperties(vulkanDevice.physicalDevice, depthFormat, &formatProps);
if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR))
{
std::cerr << "Device does not support depth images can be used as a source image for copy commands!" << std::endl;
return;
}
VkImage depthImage;
VkDeviceMemory depthImageMemory;
int width = swapChain.extent.width;
int height = swapChain.extent.height;
// Create the linear tiled destination image to copy to and to read the memory from
VkImageCreateInfo imgCreateInfo = FVulkanInitializers::ImageCreateInfo();// (vks::initializers::imageCreateInfo());
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imgCreateInfo.format = FVulkanCalculator::FindDepthFormat(vulkanDevice.physicalDevice);
imgCreateInfo.extent.width = width;
imgCreateInfo.extent.height = height;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// Create the image
vkCreateImage(vulkanDevice.logicalDevice, &imgCreateInfo, nullptr, &depthImage);
// Create memory to back up the image
VkMemoryRequirements memRequirements;
VkMemoryAllocateInfo memAllocInfo = FVulkanInitializers::MemoryAllocateInfo();
vkGetImageMemoryRequirements(vulkanDevice.logicalDevice, depthImage, &memRequirements);
memAllocInfo.allocationSize = memRequirements.size;
// Memory must be host visible to copy from
memAllocInfo.memoryTypeIndex = vulkanDevice.GetMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(vulkanDevice.logicalDevice, &memAllocInfo, nullptr, &depthImageMemory);
vkBindImageMemory(vulkanDevice.logicalDevice, depthImage, depthImageMemory, 0);
// Do the actual blit from the swapchain image to our host visible destination image
VkCommandBuffer copyCmd = FVulkanCommandBufferCalculator::BeginSingleTimeCommands(vulkanDevice.logicalDevice, commandPool);
VkImageMemoryBarrier imageMemoryBarrier = FVulkanInitializers::ImageMemoryBarrier();
// Transition destination image to transfer destination layout
InsertImageMemoryBarrier(
copyCmd,
depthImage,
0,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 });
// Transition depth image from present to transfer source layout
InsertImageMemoryBarrier(
copyCmd,
srcImage,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
//VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 });
// Use image copy (requires us to manually flip components)
VkImageCopy imageCopyRegion{};
imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
imageCopyRegion.srcSubresource.layerCount = 1;
imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
imageCopyRegion.dstSubresource.layerCount = 1;
imageCopyRegion.extent.width = width;
imageCopyRegion.extent.height = height;
imageCopyRegion.extent.depth = 1;
// Issue the copy command
vkCmdCopyImage(
copyCmd,
srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
depthImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&imageCopyRegion);
// Transition destination image to general layout, which is the required layout for mapping the image memory later on
InsertImageMemoryBarrier(
copyCmd,
depthImage,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_MEMORY_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 });
// Transition back the depth image after the copy is done
InsertImageMemoryBarrier(
copyCmd,
srcImage,
VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 });
FVulkanCommandBufferCalculator::EndSingleTimeCommands(copyCmd, vulkanDevice.logicalDevice, queue, commandPool);
// Get layout of the image (including row pitch)
VkImageSubresource subResource{};
subResource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
VkSubresourceLayout subResourceLayout;
vkGetImageSubresourceLayout(vulkanDevice.logicalDevice, depthImage, &subResource, &subResourceLayout);
// Map image memory so we can start copying from it
const char* data;
vkMapMemory(vulkanDevice.logicalDevice, depthImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data);
data += subResourceLayout.offset;
std::ofstream file(filename, std::ios::out | std::ios::binary);
// ppm header
file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n";
// If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components
bool colorSwizzle = false;
// Check if source is BGR
// Note: Not complete, only contains most common and basic BGR surface formats for demonstation purposes
std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM };
colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end());
// ppm binary pixel data
for (uint32_t y = 0; y < height; y++)
{
unsigned int *row = (unsigned int*)data;
for (uint32_t x = 0; x < width; x++)
{
if (colorSwizzle)
{
file.write((char*)row + 2, 1);
file.write((char*)row + 1, 1);
file.write((char*)row, 1);
}
else
{
file.write((char*)row, 3);
}
row++;
}
data += subResourceLayout.rowPitch;
}
file.close();
std::cout << "Depth Screenshot saved to disk" << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment