From deed78921c234b8c7ef2f1f4d4ef2f4cd943d4ac Mon Sep 17 00:00:00 2001 From: saschawillems Date: Sat, 17 Dec 2016 00:13:55 +0100 Subject: [PATCH] Added screenshot taking example --- CMakeLists.txt | 1 + data/shaders/screenshot/mesh.frag | 23 ++ data/shaders/screenshot/mesh.frag.spv | Bin 0 -> 1720 bytes data/shaders/screenshot/mesh.vert | 39 ++ data/shaders/screenshot/mesh.vert.spv | Bin 0 -> 2616 bytes screenshot/screenshot.cpp | 554 ++++++++++++++++++++++++++ screenshot/screenshot.vcxproj | 81 ++++ screenshot/screenshot.vcxproj.filters | 53 +++ vulkanExamples.sln | 6 + 9 files changed, 757 insertions(+) create mode 100644 data/shaders/screenshot/mesh.frag create mode 100644 data/shaders/screenshot/mesh.frag.spv create mode 100644 data/shaders/screenshot/mesh.vert create mode 100644 data/shaders/screenshot/mesh.vert.spv create mode 100644 screenshot/screenshot.cpp create mode 100644 screenshot/screenshot.vcxproj create mode 100644 screenshot/screenshot.vcxproj.filters diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a4400ed..52aed192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ set(EXAMPLES radialblur raytracing scenerendering + screenshot shadowmapping shadowmappingomni skeletalanimation diff --git a/data/shaders/screenshot/mesh.frag b/data/shaders/screenshot/mesh.frag new file mode 100644 index 00000000..90463067 --- /dev/null +++ b/data/shaders/screenshot/mesh.frag @@ -0,0 +1,23 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec3 inViewVec; +layout (location = 3) in vec3 inLightVec; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + vec3 V = normalize(inViewVec); + vec3 R = reflect(-L, N); + vec3 ambient = vec3(0.1); + vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0); + vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.75); + outFragColor = vec4((ambient + diffuse) * inColor.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/data/shaders/screenshot/mesh.frag.spv b/data/shaders/screenshot/mesh.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..50714dbef1374557a2e0551bc43672bb0d3a3c77 GIT binary patch literal 1720 zcmZ9MNmCP16osE;kV%;ooQs3!WHg6!6|d)fzfoL?>POwZu-3Ov%C?u-nI($%qVS_P`}k>byWXC4mvsis z{V0sPvSr;h;e+V#u-6HNb8b$!a~#%sji4=6Oy2LN)$6`)2lbb&Ml0d7eqT}Cr^?Dj z@8shLX18E&0ZeaTb_!TzR^Sl50_y(NAry!vYcRwqk&Rq$1-rqO*)59GJ?+qX7v41$r zPYnGSz9*fzt=@g<_`qAedFfUUJN3-=Ah9tYe!OYxg@44nVSMOiPA8nzE=p$~hF7H1 z@0VnjRq6P^o6nkb-t*VQ=c#o3;LYcmbZS|ib?NxQzfJb~Tsk~I<4V%unANzAgyXw; ZqYd+&ZAys8A3NVQ^YPtb`>hOXl0WERZpr`v literal 0 HcmV?d00001 diff --git a/data/shaders/screenshot/mesh.vert b/data/shaders/screenshot/mesh.vert new file mode 100644 index 00000000..aa7975ef --- /dev/null +++ b/data/shaders/screenshot/mesh.vert @@ -0,0 +1,39 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec4 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec3 inColor; + +layout (set = 0, binding = 0) uniform UBO +{ + mat4 projection; + mat4 view; + mat4 model; +} ubo; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec3 outViewVec; +layout (location = 3) out vec3 outLightVec; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outNormal = inNormal; + outColor = inColor; + gl_Position = ubo.projection * ubo.view * ubo.model * inPos; + + vec4 pos = ubo.view * ubo.model * vec4(inPos.xyz, 1.0); + outNormal = mat3(ubo.model) * inNormal; + + vec3 lightPos = vec3(1.0f, -1.0f, 1.0f); + outLightVec = lightPos.xyz - pos.xyz; + outViewVec = -pos.xyz; +} diff --git a/data/shaders/screenshot/mesh.vert.spv b/data/shaders/screenshot/mesh.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..a8beb7b6dd833b31607fc04f171da7daa2e270cd GIT binary patch literal 2616 zcmZ9MYjYG;5Qc|rHlipOk&8Fn5bt1&7!XAu3Cg0bWCOBZzHBYY)H1r+N$qabZ&Kwi z&_86AzsfIG`8+ez)R?pN=5)V(Ieog%q%pVjRFWLxcR2YWDc6Z)4n0YZBn!pf?%eMz zPiH&JtCv^wIGQvHM}3YZ&lU1Ke|HDjSmkMO1ZX)5PJ%bV7U+Qnzd825NidIhyVGC4 zx6z-bdxObfmiDKQ20Q7bpFjLD-JVVV?}I5D5BsCRc(^|prv24RD|>_OpOPk->NPZB ztRvr_-OVSvgAuZvb!FoxRKZy%ez)>bK0$<=FY3<5CDo)4C*X&pem9-;(#b6SRlkLb zSJswKvssppiEk8lmRCN*{$OLX@K<*2O>%FiJAAY7v7bL@=`Y%YvroJEPFmLANM1zW zf0!3F+S6>zvFx66k$n%%d)`GJWy43a(p+%%pL=$sd+Bz;IS;rV$4VvbVj@X$4&J41 z&(y6?-G0?|XRL0mcgSk5T*kTD)FxNj=Zm|_H-Y}qlO;)3o#SyP#|y5Evxa({x^ZhD z;^Yp-d3HTc-MGgf;^ZREnypLDJKOj3z!mypsTR7ksQ1iXt2coD_BncVlC0)k#HwBS zG|~cl3!K5|$ziGKs=r6FhfVnPI`YB}BS@hHF<|=cS^Og&E##waxV9z~6-JZ*7f2op< zi8EeBuAjMB`RM;fW%FF~sXHV6wZFq|{c*<&$T55K+ly6@B#&=;hfk5T8x!{H$S(7J z&8}U9eG{4Zzb=wZz6;DL?`~br)wg2a(0i4Bg%|7n?VC%Ve_(va`dYK!yt&n_!S7!E zI=cI_z;|$iXy1!^*p4Cl2DJG$)Yr*wuU`iC-#O}U-(Lap;p2PO$2o-0Ysm6{lE;`9 zFit+=&LbPQ2qJC?Sw7-kM{WUmeVo%&amyeOHPak{W8{)n2@&U%1s4*ksU{}s8&_bqZg-*@P8!WH>`tM2kWoId92v8(@! H*H!QzK>egp literal 0 HcmV?d00001 diff --git a/screenshot/screenshot.cpp b/screenshot/screenshot.cpp new file mode 100644 index 00000000..9bdb95fa --- /dev/null +++ b/screenshot/screenshot.cpp @@ -0,0 +1,554 @@ +/* +* 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 +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include "vulkanexamplebase.h" + +#define VERTEX_BUFFER_BIND_ID 0 +#define ENABLE_VALIDATION false + +// Vertex layout for this example +std::vector vertexLayout = +{ + vkMeshLoader::VERTEX_LAYOUT_POSITION, + vkMeshLoader::VERTEX_LAYOUT_NORMAL, + vkMeshLoader::VERTEX_LAYOUT_COLOR +}; + +class VulkanExample : public VulkanExampleBase +{ +public: + struct { + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + } vertices; + + struct { + vkMeshLoader::MeshBuffer object; + } meshes; + + vk::Buffer uniformBuffer; + + 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); + + vkMeshLoader::freeMeshBufferResources(device, &meshes.object); + + uniformBuffer.destroy(); + } + + void loadAssets() + { + loadMesh(getAssetPath() + "models/chinesedragon.dae", &meshes.object, vertexLayout, 0.1f); + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); + 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); + + VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0); + 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 }; + vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.object.vertices.buf, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.object.indices.buf, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(drawCmdBuffers[i], meshes.object.indexCount, 1, 0, 0, 0); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void prepareVertices() + { + // Binding description + vertices.bindingDescriptions.resize(1); + vertices.bindingDescriptions[0] = + vkTools::initializers::vertexInputBindingDescription( + VERTEX_BUFFER_BIND_ID, + vkMeshLoader::vertexSize(vertexLayout), + VK_VERTEX_INPUT_RATE_VERTEX); + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions.resize(4); + // Location 0 : Position + vertices.attributeDescriptions[0] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 0, + VK_FORMAT_R32G32B32_SFLOAT, + 0); + // Location 1 : Normal + vertices.attributeDescriptions[1] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 1, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 3); + // Location 2 : Texture coordinates + vertices.attributeDescriptions[2] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 2, + VK_FORMAT_R32G32_SFLOAT, + sizeof(float) * 6); + // Location 3 : Color + vertices.attributeDescriptions[3] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 3, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 8); + + vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); + vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size(); + vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); + vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size(); + vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); + } + + void setupDescriptorPool() + { + // Example uses one ubo and one image sampler + std::vector poolSizes = { + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vkTools::initializers::descriptorPoolCreateInfo( + poolSizes.size(), + poolSizes.data(), + 2); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupDescriptorSetLayout() + { + std::vector setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // Binding 0 : Vertex shader uniform buffer + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vkTools::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + setLayoutBindings.size()); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = + vkTools::initializers::pipelineLayoutCreateInfo( + &descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + } + + void setupDescriptorSet() + { + VkDescriptorSetAllocateInfo allocInfo = + vkTools::initializers::descriptorSetAllocateInfo( + descriptorPool, + &descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + + std::vector writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), // Binding 0 : Vertex shader uniform buffer + }; + + vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vkTools::initializers::pipelineInputAssemblyStateCreateInfo( + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + 0, + VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vkTools::initializers::pipelineRasterizationStateCreateInfo( + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vkTools::initializers::pipelineColorBlendAttachmentState( + 0xf, + VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vkTools::initializers::pipelineColorBlendStateCreateInfo( + 1, + &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vkTools::initializers::pipelineDepthStencilStateCreateInfo( + VK_TRUE, + VK_TRUE, + VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vkTools::initializers::pipelineMultisampleStateCreateInfo( + VK_SAMPLE_COUNT_1_BIT, + 0); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vkTools::initializers::pipelineDynamicStateCreateInfo( + dynamicStateEnables.data(), + dynamicStateEnables.size(), + 0); + + // Spherical environment rendering pipeline + // Load shaders + std::array shaderStages; + 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); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vkTools::initializers::pipelineCreateInfo( + pipelineLayout, + renderPass, + 0); + + pipelineCreateInfo.pVertexInputState = &vertices.inputState; + 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(); + + 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; + uboVS.model = glm::mat4(); + + 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; + vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps); + + // Check if the device supports blitting to linear images + if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { + std::cerr << "Error: Device does not support blitting to linear tiled images!" << std::endl; + return; + } + + // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) + if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { + std::cerr << "Error: Device does not support blitting from optimal tiled images!" << std::endl; + return; + } + + // Source for the blit is the last rendered swapchain image + VkImage srcImage = swapChain.images[currentBuffer]; + + // Create the linear tiled destination image to copy to and to read the memory from + VkImageCreateInfo imgCreateInfo(vkTools::initializers::imageCreateInfo()); + imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; + // Note that vkCmdBlitImage will also do format conversions if the swapchain color format would differ + 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; + VkMemoryAllocateInfo memAllocInfo(vkTools::initializers::memoryAllocateInfo()); + 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); + + // Transition destination image to transfer destination layout + vkTools::setImageLayout( + copyCmd, + dstImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + // Transition swapchain image from present to transfer source layout + vkTools::setImageLayout( + copyCmd, + srcImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + // 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); + + // Transition destination image to general layout, which is the required layout for mapping the image memory later on + vkTools::setImageLayout( + copyCmd, + dstImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + // Transition back the swap chain image after the blit is done + vkTools::setImageLayout( + copyCmd, + srcImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + 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"; + + // 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++) + { + file.write((char*)row, 3); + 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(); + prepareVertices(); + 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() \ No newline at end of file diff --git a/screenshot/screenshot.vcxproj b/screenshot/screenshot.vcxproj new file mode 100644 index 00000000..cafadc36 --- /dev/null +++ b/screenshot/screenshot.vcxproj @@ -0,0 +1,81 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + {AD1DAD4D-A753-4A78-88E2-B4DCE15D482F} + screenshot + 8.1 + + + + v140 + + + v140 + + + + + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_DEBUG;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX + MultiThreadedDebugDLL + Disabled + true + /FS %(AdditionalOptions) + + + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + true + %(AdditionalLibraryDirectories) + Console + + + + + WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + + + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/screenshot/screenshot.vcxproj.filters b/screenshot/screenshot.vcxproj.filters new file mode 100644 index 00000000..03d127fd --- /dev/null +++ b/screenshot/screenshot.vcxproj.filters @@ -0,0 +1,53 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {ea2b3ea0-f498-4b9a-a493-df913acc5e32} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/vulkanExamples.sln b/vulkanExamples.sln index 8fe4ddcb..2afb7793 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -128,6 +128,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution CMakeLists.txt = CMakeLists.txt EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "screenshot", "screenshot\screenshot.vcxproj", "{AD1DAD4D-A753-4A78-88E2-B4DCE15D482F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -310,6 +312,10 @@ Global {6B4BC372-5897-40FB-91D4-421C2817F656}.Debug|x64.Build.0 = Debug|x64 {6B4BC372-5897-40FB-91D4-421C2817F656}.Release|x64.ActiveCfg = Release|x64 {6B4BC372-5897-40FB-91D4-421C2817F656}.Release|x64.Build.0 = Release|x64 + {AD1DAD4D-A753-4A78-88E2-B4DCE15D482F}.Debug|x64.ActiveCfg = Debug|x64 + {AD1DAD4D-A753-4A78-88E2-B4DCE15D482F}.Debug|x64.Build.0 = Debug|x64 + {AD1DAD4D-A753-4A78-88E2-B4DCE15D482F}.Release|x64.ActiveCfg = Release|x64 + {AD1DAD4D-A753-4A78-88E2-B4DCE15D482F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE