From 625cc11ea237d4d2905c607a00817ab4ba6ed22d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 13 Mar 2019 14:10:13 +0100 Subject: [PATCH] - capture screenshot support --- .../vulkan/renderer/vk_postprocess.cpp | 8 +- .../vulkan/renderer/vk_postprocess.h | 2 +- .../vulkan/system/vk_framebuffer.cpp | 100 +++++++++++++++--- src/rendering/vulkan/system/vk_framebuffer.h | 3 + 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/rendering/vulkan/renderer/vk_postprocess.cpp b/src/rendering/vulkan/renderer/vk_postprocess.cpp index 3f372fb66..a0089afe0 100644 --- a/src/rendering/vulkan/renderer/vk_postprocess.cpp +++ b/src/rendering/vulkan/renderer/vk_postprocess.cpp @@ -119,7 +119,7 @@ void VkPostprocess::BlitSceneToTexture() imageTransition1.execute(fb->GetDrawCommands()); } -void VkPostprocess::BlitCurrentToImage(VulkanImage *dstimage, VkImageLayout *dstlayout) +void VkPostprocess::BlitCurrentToImage(VulkanImage *dstimage, VkImageLayout *dstlayout, VkImageLayout finallayout) { auto fb = GetVulkanFrameBuffer(); @@ -134,7 +134,7 @@ void VkPostprocess::BlitCurrentToImage(VulkanImage *dstimage, VkImageLayout *dst VkPPImageTransition imageTransition0; imageTransition0.addImage(srcimage, srclayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, false); imageTransition0.addImage(dstimage, dstlayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true); - imageTransition0.execute(fb->GetDrawCommands()); + imageTransition0.execute(cmdbuffer); VkImageBlit blit = {}; blit.srcOffsets[0] = { 0, 0, 0 }; @@ -155,8 +155,8 @@ void VkPostprocess::BlitCurrentToImage(VulkanImage *dstimage, VkImageLayout *dst 1, &blit, VK_FILTER_NEAREST); VkPPImageTransition imageTransition1; - imageTransition1.addImage(dstimage, dstlayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false); - imageTransition1.execute(fb->GetDrawCommands()); + imageTransition1.addImage(dstimage, dstlayout, finallayout, false); + imageTransition1.execute(cmdbuffer); } void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool clearBorders) diff --git a/src/rendering/vulkan/renderer/vk_postprocess.h b/src/rendering/vulkan/renderer/vk_postprocess.h index 5cc32247a..23a408b05 100644 --- a/src/rendering/vulkan/renderer/vk_postprocess.h +++ b/src/rendering/vulkan/renderer/vk_postprocess.h @@ -61,7 +61,7 @@ public: void ClearTonemapPalette(); void BlitSceneToTexture(); - void BlitCurrentToImage(VulkanImage *image, VkImageLayout *layout); + void BlitCurrentToImage(VulkanImage *image, VkImageLayout *layout, VkImageLayout finallayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); void DrawPresentTexture(const IntRect &box, bool applyGamma, bool clearBorders); private: diff --git a/src/rendering/vulkan/system/vk_framebuffer.cpp b/src/rendering/vulkan/system/vk_framebuffer.cpp index 4a5a30ed4..b101dd81b 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/rendering/vulkan/system/vk_framebuffer.cpp @@ -166,6 +166,26 @@ void VulkanFrameBuffer::Update() mPostprocess->DrawPresentTexture(mOutputLetterbox, true, true); + SubmitCommands(true); + + Flush3D.Unclock(); + + Finish.Reset(); + Finish.Clock(); + device->PresentFrame(); + device->WaitPresent(); + + mDrawCommands.reset(); + mUploadCommands.reset(); + mFrameDeleteList.clear(); + + Finish.Unclock(); + + Super::Update(); +} + +void VulkanFrameBuffer::SubmitCommands(bool finish) +{ mDrawCommands->end(); if (mUploadCommands) @@ -186,7 +206,7 @@ void VulkanFrameBuffer::Update() // Wait for upload commands to finish, then submit render commands VkSemaphore waitSemaphores[] = { mUploadSemaphore->semaphore, device->imageAvailableSemaphore->semaphore }; VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = 2; + submitInfo.waitSemaphoreCount = finish ? 2 : 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; @@ -204,7 +224,7 @@ void VulkanFrameBuffer::Update() VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 1; + submitInfo.waitSemaphoreCount = finish ? 1 : 0; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; @@ -215,21 +235,6 @@ void VulkanFrameBuffer::Update() if (result < VK_SUCCESS) I_FatalError("Failed to submit command buffer! Error %d\n", result); } - - Flush3D.Unclock(); - - Finish.Reset(); - Finish.Clock(); - device->PresentFrame(); - device->WaitPresent(); - - mDrawCommands.reset(); - mUploadCommands.reset(); - mFrameDeleteList.clear(); - - Finish.Unclock(); - - Super::Update(); } void VulkanFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height) @@ -611,6 +616,67 @@ FTexture *VulkanFrameBuffer::WipeEndScreen() return tex; } +TArray VulkanFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) +{ + int w = SCREENWIDTH; + int h = SCREENHEIGHT; + + // Convert from rgba16f to rgba8 using the GPU: + ImageBuilder imgbuilder; + imgbuilder.setFormat(VK_FORMAT_R8G8B8A8_UNORM); + imgbuilder.setUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + imgbuilder.setSize(w, h); + auto image = imgbuilder.create(device); + VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; + GetPostprocess()->BlitCurrentToImage(image.get(), &layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // Staging buffer for download + BufferBuilder bufbuilder; + bufbuilder.setSize(w * h * 4); + bufbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU); + auto staging = bufbuilder.create(device); + + // Copy from image to buffer + VkBufferImageCopy region = {}; + region.imageExtent.width = w; + region.imageExtent.height = h; + region.imageExtent.depth = 1; + region.imageSubresource.layerCount = 1; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + GetDrawCommands()->copyImageToBuffer(image->image, layout, staging->buffer, 1, ®ion); + + // Submit command buffers and wait for device to finish the work + SubmitCommands(false); + vkWaitForFences(device->device, 1, &device->renderFinishedFence->fence, VK_TRUE, std::numeric_limits::max()); + vkResetFences(device->device, 1, &device->renderFinishedFence->fence); + mDrawCommands.reset(); + mUploadCommands.reset(); + mFrameDeleteList.clear(); + + // Map and convert from rgba8 to rgb8 + TArray ScreenshotBuffer(w * h * 3, true); + uint8_t *pixels = (uint8_t*)staging->Map(0, w * h * 4); + int dindex = 0; + for (int y = 0; y < h; y++) + { + int sindex = (h - y - 1) * w * 4; + for (int x = 0; x < w; x++) + { + ScreenshotBuffer[dindex] = pixels[sindex]; + ScreenshotBuffer[dindex + 1] = pixels[sindex + 1]; + ScreenshotBuffer[dindex + 2] = pixels[sindex + 2]; + dindex += 3; + sindex += 4; + } + } + staging->Unmap(); + + pitch = w * 3; + color_type = SS_RGB; + gamma = 2.2f; + return ScreenshotBuffer; +} + void VulkanFrameBuffer::BeginFrame() { SetViewportRects(nullptr); diff --git a/src/rendering/vulkan/system/vk_framebuffer.h b/src/rendering/vulkan/system/vk_framebuffer.h index 5fb474166..7babdfdd0 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.h +++ b/src/rendering/vulkan/system/vk_framebuffer.h @@ -74,6 +74,8 @@ public: FTexture *WipeStartScreen() override; FTexture *WipeEndScreen() override; + TArray GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) override; + void SetVSync(bool vsync); void Draw2D() override; @@ -84,6 +86,7 @@ private: void DrawScene(HWDrawInfo *di, int drawmode); void PrintStartupLog(); void CreateFanToTrisIndexBuffer(); + void SubmitCommands(bool finish); std::unique_ptr mShaderManager; std::unique_ptr mSamplerManager;