From 9aed27cdcfa7f24c0b8aa33e66c95b099629d258 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 3 Nov 2016 22:50:31 +1100 Subject: [PATCH] Vulkan: Combine frame dumping and present into one command buffer. Small optimization that should make things slightly more efficient when frame dumping is enabled. --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 43 +++++++++++++------ Source/Core/VideoBackends/Vulkan/Renderer.h | 5 +++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 78772e7e15..16099a5890 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -491,15 +491,15 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height StateTracker::GetInstance()->OnEndFrame(); // Draw to the screenshot buffer if needed. - if (IsFrameDumping() && - DrawScreenshot(rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height)) - { - DumpFrameData(reinterpret_cast(m_screenshot_readback_texture->GetMapPointer()), - static_cast(m_screenshot_render_texture->GetWidth()), - static_cast(m_screenshot_render_texture->GetHeight()), - static_cast(m_screenshot_readback_texture->GetRowStride()), ticks); - FinishFrameData(); - } + // We don't actually copy it to the frame dump here, instead we submit the present to the screen + // and the readback in one command buffer, wait for it, and then dump the frame. This allows us + // to render to the screen and dump the frame in one command buffer instead of two. + VkFence frame_dump_fence = g_command_buffer_mgr->GetCurrentCommandBufferFence(); + bool dump_this_frame = IsFrameDumping() && DrawScreenshot(rc, xfb_addr, xfb_sources, xfb_count, + fb_width, fb_stride, fb_height); + + // If we're dumping frames, don't bother waking the worker thread, since we have to wait anyway. + bool submit_on_background_thread = !dump_this_frame; // Ensure the worker thread is not still submitting a previous command buffer. // In other words, the last frame has been submitted (otherwise the next call would @@ -516,13 +516,23 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height // the available semaphore to be signaled before executing the buffer. This final submission // can happen off-thread in the background while we're preparing the next frame. g_command_buffer_mgr->SubmitCommandBuffer( - true, m_image_available_semaphore, m_rendering_finished_semaphore, + submit_on_background_thread, m_image_available_semaphore, m_rendering_finished_semaphore, m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex()); } else { // No swap chain, just execute command buffer. - g_command_buffer_mgr->SubmitCommandBuffer(true); + g_command_buffer_mgr->SubmitCommandBuffer(submit_on_background_thread); + } + + // If we're dumping frames, wait for the GPU to complete these commands, and then copy the image. + // NOTE: This call must come immediately after submitting the command buffer. Placing any other + // function calls between the submit and wait could cause another command buffer to be submitted, + // making frame_dump_fence refer to an incorrect fence. + if (dump_this_frame) + { + g_command_buffer_mgr->WaitForFence(frame_dump_fence); + DumpScreenshot(ticks); } // NOTE: It is important that no rendering calls are made to the EFB between submitting the @@ -732,11 +742,18 @@ bool Renderer::DrawScreenshot(const EFBRectangle& rc, u32 xfb_addr, g_command_buffer_mgr->GetCurrentCommandBuffer(), m_screenshot_render_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width, height, 0, 0); - // Wait for the command buffer to complete. - g_command_buffer_mgr->ExecuteCommandBuffer(false, true); return true; } +void Renderer::DumpScreenshot(u64 ticks) +{ + DumpFrameData(reinterpret_cast(m_screenshot_readback_texture->GetMapPointer()), + static_cast(m_screenshot_render_texture->GetWidth()), + static_cast(m_screenshot_render_texture->GetHeight()), + static_cast(m_screenshot_readback_texture->GetRowStride()), ticks); + FinishFrameData(); +} + void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, const TargetRectangle& src_rect, const Texture2D* src_tex, bool linear_filter) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index d8dbb29595..2d51e837b8 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -109,6 +109,11 @@ private: bool DrawScreenshot(const EFBRectangle& rc, u32 xfb_addr, const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height); + // Copies the screenshot readback texture to the frame dumping buffer. + // NOTE: This assumes that DrawScreenshot has been called prior, and the fence associated + // with the command buffer where the readback buffer was populated has been reached. + void DumpScreenshot(u64 ticks); + // Copies/scales an image to the currently-bound framebuffer. void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, const TargetRectangle& src_rect, const Texture2D* src_tex, bool linear_filter);