Add screenshot function
This commit is contained in:
parent
fef5254904
commit
1691f59ac7
3 changed files with 211 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
206
src/renderer.cpp
206
src/renderer.cpp
|
@ -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);
|
||||
|
||||
|
|
Reference in a new issue