2016-12-17 00:13:55 +01:00
/*
* Vulkan Example - Taking screenshots
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <vector>
# define GLM_FORCE_RADIANS
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
# include <glm/glm.hpp>
# include <glm/gtc/matrix_transform.hpp>
# include <vulkan/vulkan.h>
# include "vulkanexamplebase.h"
2017-02-11 14:18:24 +01:00
# include "VulkanModel.hpp"
2016-12-17 00:13:55 +01:00
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public :
2017-02-11 14:18:24 +01:00
// Vertex layout for the models
vks : : VertexLayout vertexLayout = vks : : VertexLayout ( {
vks : : VERTEX_COMPONENT_POSITION ,
vks : : VERTEX_COMPONENT_NORMAL ,
vks : : VERTEX_COMPONENT_COLOR ,
} ) ;
2016-12-17 00:13:55 +01:00
struct {
2017-02-11 14:18:24 +01:00
vks : : Model object ;
} models ;
2016-12-17 00:13:55 +01:00
2017-02-12 10:44:51 +01:00
vks : : Buffer uniformBuffer ;
2016-12-17 00:13:55 +01:00
struct {
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : mat4 view ;
int32_t texIndex = 0 ;
} uboVS ;
VkPipelineLayout pipelineLayout ;
VkPipeline pipeline ;
VkDescriptorSetLayout descriptorSetLayout ;
VkDescriptorSet descriptorSet ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
title = " Vulkan Example - Screenshot " ;
enableTextOverlay = true ;
camera . type = Camera : : CameraType : : lookat ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 512.0f ) ;
camera . setRotation ( glm : : vec3 ( - 25.0f , 23.75f , 0.0f ) ) ;
camera . setTranslation ( glm : : vec3 ( 0.0f , 0.0f , - 2.0f ) ) ;
}
~ VulkanExample ( )
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
vkDestroyPipeline ( device , pipeline , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2017-02-11 14:18:24 +01:00
models . object . destroy ( ) ;
2016-12-17 00:13:55 +01:00
uniformBuffer . destroy ( ) ;
}
void loadAssets ( )
{
2017-02-11 14:18:24 +01:00
models . object . loadFromFile ( getAssetPath ( ) + " models/chinesedragon.dae " , vertexLayout , 0.1f , vulkanDevice , queue ) ;
2016-12-17 00:13:55 +01:00
}
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-12-17 00:13:55 +01:00
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-12-17 00:13:55 +01:00
renderPassBeginInfo . renderPass = renderPass ;
renderPassBeginInfo . renderArea . offset . x = 0 ;
renderPassBeginInfo . renderArea . offset . y = 0 ;
renderPassBeginInfo . renderArea . extent . width = width ;
renderPassBeginInfo . renderArea . extent . height = height ;
renderPassBeginInfo . clearValueCount = 2 ;
renderPassBeginInfo . pClearValues = clearValues ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
// Set target frame buffer
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2017-02-12 11:12:42 +01:00
VkViewport viewport = vks : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-12-17 00:13:55 +01:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2017-02-12 11:12:42 +01:00
VkRect2D scissor = vks : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-12-17 00:13:55 +01:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
VkDeviceSize offsets [ 1 ] = { 0 } ;
2017-02-11 14:18:24 +01:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & models . object . vertices . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , models . object . indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2016-12-17 00:13:55 +01:00
2017-02-11 14:18:24 +01:00
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , models . object . indexCount , 1 , 0 , 0 , 0 ) ;
2016-12-17 00:13:55 +01:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void setupDescriptorPool ( )
{
// Example uses one ubo and one image sampler
std : : vector < VkDescriptorPoolSize > poolSizes = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) ,
2016-12-17 00:13:55 +01:00
} ;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolCreateInfo (
2016-12-17 00:13:55 +01:00
poolSizes . size ( ) ,
poolSizes . data ( ) ,
2 ) ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , VK_SHADER_STAGE_VERTEX_BIT , 0 ) , // Binding 0 : Vertex shader uniform buffer
2016-12-17 00:13:55 +01:00
} ;
VkDescriptorSetLayoutCreateInfo descriptorLayout =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutCreateInfo (
2016-12-17 00:13:55 +01:00
setLayoutBindings . data ( ) ,
setLayoutBindings . size ( ) ) ;
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineLayoutCreateInfo (
2016-12-17 00:13:55 +01:00
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
VkDescriptorSetAllocateInfo allocInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetAllocateInfo (
2016-12-17 00:13:55 +01:00
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
std : : vector < VkWriteDescriptorSet > writeDescriptorSets = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 0 , & uniformBuffer . descriptor ) , // Binding 0 : Vertex shader uniform buffer
2016-12-17 00:13:55 +01:00
} ;
vkUpdateDescriptorSets ( device , writeDescriptorSets . size ( ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
}
void preparePipelines ( )
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineInputAssemblyStateCreateInfo (
2016-12-17 00:13:55 +01:00
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ,
0 ,
VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineRasterizationStateCreateInfo (
2016-12-17 00:13:55 +01:00
VK_POLYGON_MODE_FILL ,
VK_CULL_MODE_BACK_BIT ,
VK_FRONT_FACE_CLOCKWISE ,
0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendAttachmentState (
2016-12-17 00:13:55 +01:00
0xf ,
VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendStateCreateInfo (
2016-12-17 00:13:55 +01:00
1 ,
& blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDepthStencilStateCreateInfo (
2016-12-17 00:13:55 +01:00
VK_TRUE ,
VK_TRUE ,
VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
2016-12-17 00:13:55 +01:00
VkPipelineMultisampleStateCreateInfo multisampleState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineMultisampleStateCreateInfo (
2016-12-17 00:13:55 +01:00
VK_SAMPLE_COUNT_1_BIT ,
0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR
} ;
VkPipelineDynamicStateCreateInfo dynamicState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDynamicStateCreateInfo (
2016-12-17 00:13:55 +01:00
dynamicStateEnables . data ( ) ,
dynamicStateEnables . size ( ) ,
0 ) ;
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineCreateInfo (
2016-12-17 00:13:55 +01:00
pipelineLayout ,
renderPass ,
0 ) ;
2017-02-23 19:14:53 +01:00
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
2016-12-17 00:13:55 +01:00
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
pipelineCreateInfo . stageCount = shaderStages . size ( ) ;
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2017-02-23 19:14:53 +01:00
// Vertex bindings and attributes
// Binding description
std : : vector < VkVertexInputBindingDescription > vertexInputBindings = {
vks : : initializers : : vertexInputBindingDescription ( 0 , vertexLayout . stride ( ) , VK_VERTEX_INPUT_RATE_VERTEX ) ,
} ;
// Attribute descriptions
std : : vector < VkVertexInputAttributeDescription > vertexInputAttributes = {
vks : : initializers : : vertexInputAttributeDescription ( 0 , 0 , VK_FORMAT_R32G32B32_SFLOAT , 0 ) , // Position
vks : : initializers : : vertexInputAttributeDescription ( 0 , 1 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 3 ) , // Normal
2017-02-23 19:59:58 +01:00
vks : : initializers : : vertexInputAttributeDescription ( 0 , 2 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 6 ) , // Color
2017-02-23 19:14:53 +01:00
} ;
VkPipelineVertexInputStateCreateInfo vertexInputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
vertexInputState . vertexBindingDescriptionCount = static_cast < uint32_t > ( vertexInputBindings . size ( ) ) ;
vertexInputState . pVertexBindingDescriptions = vertexInputBindings . data ( ) ;
vertexInputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertexInputAttributes . size ( ) ) ;
vertexInputState . pVertexAttributeDescriptions = vertexInputAttributes . data ( ) ;
pipelineCreateInfo . pVertexInputState = & vertexInputState ;
// Mesh rendering pipeline
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/screenshot/mesh.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/screenshot/mesh.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2016-12-17 00:13:55 +01:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipeline ) ) ;
}
void prepareUniformBuffers ( )
{
// Vertex shader uniform buffer block
vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& uniformBuffer ,
sizeof ( uboVS ) ) ;
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
uboVS . projection = camera . matrices . perspective ;
uboVS . view = camera . matrices . view ;
2017-09-24 18:17:07 +02:00
uboVS . model = glm : : mat4 ( 1.0f ) ;
2016-12-17 00:13:55 +01:00
VK_CHECK_RESULT ( uniformBuffer . map ( ) ) ;
uniformBuffer . copyTo ( & uboVS , sizeof ( uboVS ) ) ;
uniformBuffer . unmap ( ) ;
}
// Take a screenshot for the curretn swapchain image
// This is done using a blit from the swapchain image to a linear image whose memory content is then saved as a ppm image
// Getting the image date directly from a swapchain image wouldn't work as they're usually stored in an implementation dependant optimal tiling format
// Note: This requires the swapchain images to be created with the VK_IMAGE_USAGE_TRANSFER_SRC_BIT flag (see VulkanSwapChain::create)
void saveScreenshot ( const char * filename )
{
// Get format properties for the swapchain color format
VkFormatProperties formatProps ;
2017-02-23 20:04:07 +01:00
bool supportsBlit = true ;
2017-02-23 19:14:53 +01:00
// Check blit support for source and destination
2016-12-17 00:13:55 +01:00
// Check if the device supports blitting from optimal images (the swapchain images are in optimal format)
2017-02-23 19:14:53 +01:00
vkGetPhysicalDeviceFormatProperties ( physicalDevice , swapChain . colorFormat , & formatProps ) ;
if ( ! ( formatProps . optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT ) ) {
std : : cerr < < " Device does not support blitting from optimal tiled images, using copy instead of blit! " < < std : : endl ;
supportsBlit = false ;
2016-12-17 00:13:55 +01:00
}
2017-02-23 19:14:53 +01:00
// Check if the device supports blitting to linear images
vkGetPhysicalDeviceFormatProperties ( physicalDevice , VK_FORMAT_R8G8B8A8_UNORM , & formatProps ) ;
if ( ! ( formatProps . linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ) ) {
std : : cerr < < " Device does not support blitting to linear tiled images, using copy instead of blit! " < < std : : endl ;
supportsBlit = false ;
}
// Source for the copy is the last rendered swapchain image
2016-12-17 00:13:55 +01:00
VkImage srcImage = swapChain . images [ currentBuffer ] ;
// Create the linear tiled destination image to copy to and to read the memory from
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imgCreateInfo ( vks : : initializers : : imageCreateInfo ( ) ) ;
2016-12-17 00:13:55 +01:00
imgCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
2017-02-23 19:14:53 +01:00
// Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ
2016-12-17 00:13:55 +01:00
imgCreateInfo . format = VK_FORMAT_R8G8B8A8_UNORM ;
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
VkImage dstImage ;
VK_CHECK_RESULT ( vkCreateImage ( device , & imgCreateInfo , nullptr , & dstImage ) ) ;
// Create memory to back up the image
VkMemoryRequirements memRequirements ;
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo ( vks : : initializers : : memoryAllocateInfo ( ) ) ;
2016-12-17 00:13:55 +01:00
VkDeviceMemory dstImageMemory ;
vkGetImageMemoryRequirements ( device , dstImage , & 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 ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & dstImageMemory ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device , dstImage , dstImageMemory , 0 ) ) ;
// Do the actual blit from the swapchain image to our host visible destination image
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
2017-02-23 19:59:58 +01:00
VkImageMemoryBarrier imageMemoryBarrier = vks : : initializers : : imageMemoryBarrier ( ) ;
2016-12-17 00:13:55 +01:00
// Transition destination image to transfer destination layout
2017-02-23 19:59:58 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-12-17 00:13:55 +01:00
copyCmd ,
dstImage ,
2017-02-23 19:59:58 +01:00
0 ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
2016-12-17 00:13:55 +01:00
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2017-02-23 19:59:58 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VkImageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ) ;
2016-12-17 00:13:55 +01:00
// Transition swapchain image from present to transfer source layout
2017-02-23 19:59:58 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-12-17 00:13:55 +01:00
copyCmd ,
srcImage ,
2017-02-23 19:59:58 +01:00
VK_ACCESS_MEMORY_READ_BIT ,
VK_ACCESS_TRANSFER_READ_BIT ,
2016-12-17 00:13:55 +01:00
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2017-02-23 19:59:58 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VkImageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ) ;
2016-12-17 00:13:55 +01:00
2017-02-23 19:14:53 +01:00
// If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)
if ( supportsBlit )
{
// Define the region to blit (we will blit the whole swapchain image)
VkOffset3D blitSize ;
blitSize . x = width ;
blitSize . y = height ;
blitSize . z = 1 ;
VkImageBlit imageBlitRegion { } ;
imageBlitRegion . srcSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
imageBlitRegion . srcSubresource . layerCount = 1 ;
imageBlitRegion . srcOffsets [ 1 ] = blitSize ;
imageBlitRegion . dstSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
imageBlitRegion . dstSubresource . layerCount = 1 ;
imageBlitRegion . dstOffsets [ 1 ] = blitSize ;
// Issue the blit command
vkCmdBlitImage (
copyCmd ,
srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
1 ,
& imageBlitRegion ,
VK_FILTER_NEAREST ) ;
}
else
{
// Otherwise use image copy (requires us to manually flip components)
VkImageCopy imageCopyRegion { } ;
imageCopyRegion . srcSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
imageCopyRegion . srcSubresource . layerCount = 1 ;
imageCopyRegion . dstSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_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 ,
dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
1 ,
& imageCopyRegion ) ;
}
2016-12-17 00:13:55 +01:00
// Transition destination image to general layout, which is the required layout for mapping the image memory later on
2017-02-23 19:59:58 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-12-17 00:13:55 +01:00
copyCmd ,
dstImage ,
2017-02-23 19:59:58 +01:00
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_MEMORY_READ_BIT ,
2016-12-17 00:13:55 +01:00
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_GENERAL ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2017-02-23 19:59:58 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VkImageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ) ;
2016-12-17 00:13:55 +01:00
// Transition back the swap chain image after the blit is done
2017-02-23 19:59:58 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-12-17 00:13:55 +01:00
copyCmd ,
srcImage ,
2017-02-23 19:59:58 +01:00
VK_ACCESS_TRANSFER_READ_BIT ,
VK_ACCESS_MEMORY_READ_BIT ,
2016-12-17 00:13:55 +01:00
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2017-02-23 19:59:58 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VkImageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ) ;
2016-12-17 00:13:55 +01:00
vulkanDevice - > flushCommandBuffer ( copyCmd , queue ) ;
// Get layout of the image (including row pitch)
VkImageSubresource subResource { } ;
subResource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
VkSubresourceLayout subResourceLayout ;
vkGetImageSubresourceLayout ( device , dstImage , & subResource , & subResourceLayout ) ;
// Map image memory so we can start copying from it
const char * data ;
vkMapMemory ( device , dstImageMemory , 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 " ;
2017-02-23 19:14:53 +01:00
// 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
2017-02-23 20:04:07 +01:00
bool colorSwizzle = false ;
2017-02-23 19:14:53 +01:00
// Check if source is BGR
// Note: Not complete, only contains most common and basic BGR surface formats for demonstation purposes
if ( ! supportsBlit )
{
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 ( ) ) ;
}
2016-12-17 00:13:55 +01:00
// 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 + + )
{
2017-02-23 19:14:53 +01:00
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 ) ;
}
2016-12-17 00:13:55 +01:00
row + + ;
}
data + = subResourceLayout . rowPitch ;
}
file . close ( ) ;
std : : cout < < " Screenshot saved to disk " < < std : : endl ;
// Clean up resources
vkUnmapMemory ( device , dstImageMemory ) ;
vkFreeMemory ( device , dstImageMemory , nullptr ) ;
vkDestroyImage ( device , dstImage , nullptr ) ;
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
loadAssets ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
virtual void keyPressed ( uint32_t keyCode )
{
switch ( keyCode )
{
case KEY_F2 :
case GAMEPAD_BUTTON_A :
saveScreenshot ( " screenshot.ppm " ) ;
break ;
}
}
virtual void getOverlayText ( VulkanTextOverlay * textOverlay )
{
# if defined(__ANDROID__)
textOverlay - > addText ( " \" Button A \" to save screenshot " , 5.0f , 85.0f , VulkanTextOverlay : : alignLeft ) ;
# else
textOverlay - > addText ( " \" F2 \" to save screenshot " , 5.0f , 85.0f , VulkanTextOverlay : : alignLeft ) ;
# endif
}
} ;
VULKAN_EXAMPLE_MAIN ( )