Archived
1
Fork 0

Add screenshot function

This commit is contained in:
Joshua Goins 2018-10-24 21:13:55 -04:00
parent fef5254904
commit 1691f59ac7
3 changed files with 211 additions and 1 deletions

View file

@ -48,6 +48,8 @@ public:
RenderTarget* createSurfaceRenderTarget(VkSurfaceKHR surface, RenderTarget* oldRenderTarget = nullptr);
void destroyRenderTarget(RenderTarget* target);
void takeScreenshot(RenderTarget* target);
VkShaderModule createShader(const char* path);
uint32_t findMemoryType(const uint32_t typeFilter, const VkMemoryPropertyFlags properties);

View file

@ -168,6 +168,10 @@ int main(int, char*[]) {
target = renderer->createSurfaceRenderTarget(surface, target);
}
if(event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_F12) {
renderer->takeScreenshot(target);
}
}
renderer->render(world, target);

View file

@ -151,7 +151,7 @@ RenderTarget* Renderer::createSurfaceRenderTarget(VkSurfaceKHR surface, RenderTa
swapchainCreateInfo.imageFormat = surfaceFormats[chosenFormat].format;
swapchainCreateInfo.imageExtent = surfaceCapabilities.currentExtent;
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
swapchainCreateInfo.queueFamilyIndexCount = 1;
swapchainCreateInfo.pQueueFamilyIndices = &queueIndices.presentation;
swapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform;
@ -403,6 +403,210 @@ void Renderer::destroyRenderTarget(RenderTarget* target) {
delete target;
}
void Renderer::takeScreenshot(RenderTarget* target) {
VkImageCreateInfo imageCreateInfo = {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageCreateInfo.extent.width = target->extent.width;
imageCreateInfo.extent.height = target->extent.height;
imageCreateInfo.extent.depth = 1;
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkImage image = nullptr;
vkCreateImage(device_, &imageCreateInfo, nullptr, &image);
VkMemoryRequirements memoryRequirements = {};
vkGetImageMemoryRequirements(device_, image, &memoryRequirements);
VkMemoryAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VkDeviceMemory imageMemory = nullptr;
vkAllocateMemory(device_, &allocateInfo, nullptr, &imageMemory);
vkBindImageMemory(device_, image, imageMemory, 0);
VkCommandBufferAllocateInfo bufferAllocateInfo = {};
bufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
bufferAllocateInfo.commandPool = commandPool_;
bufferAllocateInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer = nullptr;
vkAllocateCommandBuffers(device_, &bufferAllocateInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
// change screenshot image to transfer dst layout
{
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
const VkImage srcImage = target->swapchainImages[0]; // FIXME: use previous image
// change offscreen image to transfer src layout
{
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imageMemoryBarrier.image = srcImage;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
VkImageCopy imageCopy = {};
imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopy.srcSubresource.layerCount = 1;
imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopy.dstSubresource.layerCount = 1;
imageCopy.extent.width = target->extent.width;
imageCopy.extent.height = target->extent.height;
imageCopy.extent.depth = 1;
// Issue the copy command
vkCmdCopyImage(
commandBuffer,
srcImage,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&imageCopy);
// change screenshot image from transfer dst to general (for mapping memory)
{
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
// change offscreen image layout back to normal
{
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.image = srcImage;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkFence fence = nullptr;
vkCreateFence(device_, &fenceCreateInfo, nullptr, &fence);
vkQueueSubmit(graphicsQueue_, 1, &submitInfo, fence);
vkWaitForFences(device_, 1, &fence, true, -1);
vkDestroyFence(device_, fence, nullptr);
vkFreeCommandBuffers(device_, commandPool_, 1, &commandBuffer);
VkImageSubresource subResource = {};
subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkSubresourceLayout subResourceLayout = {};
vkGetImageSubresourceLayout(device_, image, &subResource, &subResourceLayout);
const char* data = nullptr;
vkMapMemory(device_, imageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data);
data += subResourceLayout.offset;
std::ofstream file("screenshot.ppm", std::ios::out | std::ios::binary);
file << "P6\n" << target->extent.width << "\n" << target->extent.height << "\n" << 255 << "\n";
for(uint32_t y = 0; y < target->extent.height; y++) {
const unsigned int* row = reinterpret_cast<const unsigned int*>(data);
for(uint32_t x = 0; x < target->extent.width; x++) {
file.write(reinterpret_cast<const char*>(row), 3);
row++;
}
data += subResourceLayout.rowPitch;
}
file.close();
vkUnmapMemory(device_, imageMemory);
vkFreeMemory(device_, imageMemory, nullptr);
vkDestroyImage(device_, image, nullptr);
}
VkShaderModule Renderer::createShader(const char* path) {
std::ifstream file(path, std::ios::ate | std::ios::binary);