Created
August 14, 2017 13:47
-
-
Save mdomrach/d743dc1b7a80fe32250662e626218bc7 to your computer and use it in GitHub Desktop.
ScreenshotDepth
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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