From 49a9c33bd75df2b8cebd58eab10f140c79a8f65c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 30 Sep 2017 16:25:36 +1000 Subject: VideoCommon: Move abstract texture creation function to Renderer --- Source/Core/VideoCommon/RenderBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon/RenderBase.cpp') diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 4a46475344..6f3e23923c 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -731,7 +731,7 @@ void Renderer::UpdateFrameDumpTexture() config.width = target_width; config.height = target_height; config.rendertarget = true; - m_dump_texture = g_texture_cache->CreateTexture(config); + m_dump_texture = CreateTexture(config); } m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height}); -- cgit v1.2.3 From 56afebeb44f1ace990d9383e9b840fdc27610029 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 21:51:42 +1000 Subject: AbstractTexture: Seperate CopyRectangleFromTexture to two methods ScaleRectangleFromTexture, which does a draw, and CopyRectangleFromTexture, which where possible, does a bit-for-bit copy. --- Source/Core/VideoCommon/RenderBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon/RenderBase.cpp') diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 6f3e23923c..00e34cff71 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -733,8 +733,8 @@ void Renderer::UpdateFrameDumpTexture() config.rendertarget = true; m_dump_texture = CreateTexture(config); } - m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, - EFBRectangle{0, 0, target_width, target_height}); + m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, 0, 0, + EFBRectangle{0, 0, target_width, target_height}, 0, 0); } void Renderer::ShutdownFrameDumping() -- cgit v1.2.3 From 6577365851967d03caf1b70f440e1eee76198ebb Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 19 Nov 2017 17:33:53 +1000 Subject: VideoCommon: Re-implement asynchronous frame dumping This was lost as a result of hybrid XFB, now it is back, and ~10% faster in very brief testing. --- Source/Core/VideoCommon/RenderBase.cpp | 133 ++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 42 deletions(-) (limited to 'Source/Core/VideoCommon/RenderBase.cpp') diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 00e34cff71..d20e23ac46 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -43,6 +43,7 @@ #include "Core/Movie.h" #include "VideoCommon/AVIDump.h" +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" @@ -99,15 +100,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) Renderer::~Renderer() = default; -void Renderer::ExitFramedumping() -{ - ShutdownFrameDumping(); - if (m_frame_dump_thread.joinable()) - m_frame_dump_thread.join(); - - m_dump_texture.reset(); -} - void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, float Gamma) { @@ -629,14 +621,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total; } - if (IsFrameDumping() && m_last_xfb_texture) - { - FinishFrameData(); - } - else - { - ShutdownFrameDumping(); - } + // Ensure the last frame was written to the dump. + // This is required even if frame dumping has stopped, since the frame dump is one frame + // behind the renderer. + FlushFrameDump(); bool update_frame_count = false; if (xfbAddr && fbWidth && fbStride && fbHeight) @@ -662,10 +650,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_fps_counter.Update(); update_frame_count = true; + if (IsFrameDumping()) - { - DoDumpFrame(); - } + DumpCurrentFrame(); } // Update our last xfb values @@ -695,20 +682,16 @@ bool Renderer::IsFrameDumping() return false; } -void Renderer::DoDumpFrame() +void Renderer::DumpCurrentFrame() { - UpdateFrameDumpTexture(); + // Scale/render to frame dump texture. + RenderFrameDump(); - auto result = m_dump_texture->Map(); - if (result.has_value()) - { - auto raw_data = result.value(); - DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride, - AVIDump::FetchState(m_last_xfb_ticks)); - } + // Queue a readback for the next frame. + QueueFrameDumpReadback(); } -void Renderer::UpdateFrameDumpTexture() +void Renderer::RenderFrameDump() { int target_width, target_height; if (!g_ActiveConfig.bInternalResolutionFrameDumps) @@ -723,33 +706,99 @@ void Renderer::UpdateFrameDumpTexture() m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height); } - if (m_dump_texture == nullptr || - m_dump_texture->GetConfig().width != static_cast(target_width) || - m_dump_texture->GetConfig().height != static_cast(target_height)) + // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used). + // Or, resize texture if it isn't large enough to accommodate the current frame. + if (!m_frame_dump_render_texture || + m_frame_dump_render_texture->GetConfig().width != static_cast(target_width) || + m_frame_dump_render_texture->GetConfig().height == static_cast(target_height)) + { + // Recreate texture objects. Release before creating so we don't temporarily use twice the RAM. + TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true); + m_frame_dump_render_texture.reset(); + m_frame_dump_render_texture = CreateTexture(config); + _assert_(m_frame_dump_render_texture); + } + + // Scaling is likely to occur here, but if possible, do a bit-for-bit copy. + if (m_last_xfb_region.GetWidth() != target_width || + m_last_xfb_region.GetHeight() != target_height) + { + m_frame_dump_render_texture->ScaleRectangleFromTexture( + m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height}); + } + else + { + m_frame_dump_render_texture->CopyRectangleFromTexture( + m_last_xfb_texture, m_last_xfb_region, 0, 0, + EFBRectangle{0, 0, target_width, target_height}, 0, 0); + } +} + +void Renderer::QueueFrameDumpReadback() +{ + // Index 0 was just sent to AVI dump. Swap with the second texture. + if (m_frame_dump_readback_textures[0]) + std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]); + + std::unique_ptr& rbtex = m_frame_dump_readback_textures[0]; + if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig()) { - TextureConfig config; - config.width = target_width; - config.height = target_height; - config.rendertarget = true; - m_dump_texture = CreateTexture(config); + rbtex = CreateStagingTexture(StagingTextureType::Readback, + m_frame_dump_render_texture->GetConfig()); } - m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region, 0, 0, - EFBRectangle{0, 0, target_width, target_height}, 0, 0); + + m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks); + m_last_frame_exported = true; + rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0); +} + +void Renderer::FlushFrameDump() +{ + if (!m_last_frame_exported) + return; + + // Ensure the previously-queued frame was encoded. + FinishFrameData(); + + // Queue encoding of the last frame dumped. + std::unique_ptr& rbtex = m_frame_dump_readback_textures[0]; + rbtex->Flush(); + if (rbtex->Map()) + { + DumpFrameData(reinterpret_cast(rbtex->GetMappedPointer()), rbtex->GetConfig().width, + rbtex->GetConfig().height, static_cast(rbtex->GetMappedStride()), + m_last_frame_state); + rbtex->Unmap(); + } + + m_last_frame_exported = false; + + // Shutdown frame dumping if it is no longer active. + if (!IsFrameDumping()) + ShutdownFrameDumping(); } void Renderer::ShutdownFrameDumping() { + // Ensure the last queued readback has been sent to the encoder. + FlushFrameDump(); + if (!m_frame_dump_thread_running.IsSet()) return; + // Ensure previous frame has been encoded. FinishFrameData(); + + // Wake thread up, and wait for it to exit. m_frame_dump_thread_running.Clear(); m_frame_dump_start.Set(); + if (m_frame_dump_thread.joinable()) + m_frame_dump_thread.join(); } void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state) { - m_frame_dump_config = FrameDumpConfig{m_last_xfb_texture, data, w, h, stride, state}; + m_frame_dump_config = FrameDumpConfig{data, w, h, stride, state}; if (!m_frame_dump_thread_running.IsSet()) { @@ -759,6 +808,7 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVI m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this); } + // Wake worker thread up. m_frame_dump_start.Set(); m_frame_dump_frame_running = true; } @@ -770,7 +820,6 @@ void Renderer::FinishFrameData() m_frame_dump_done.Wait(); m_frame_dump_frame_running = false; - m_frame_dump_config.texture->Unmap(); } void Renderer::RunFrameDumps() -- cgit v1.2.3