diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index d70d9ff712..a081d6e883 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -393,7 +393,7 @@ static void CpuThread() s_is_started = false; if (!_CoreParameter.bCPUThread) - g_video_backend->Video_Cleanup(); + g_video_backend->Video_CleanupShared(); if (_CoreParameter.bFastmem) EMM::UninstallExceptionHandler(); @@ -445,7 +445,7 @@ static void FifoPlayerThread() } if (!_CoreParameter.bCPUThread) - g_video_backend->Video_Cleanup(); + g_video_backend->Video_CleanupShared(); } // Initialize and create emulation thread @@ -654,7 +654,7 @@ static void EmuThread(std::unique_ptr boot) INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (core_parameter.bCPUThread) - g_video_backend->Video_Cleanup(); + g_video_backend->Video_CleanupShared(); // If we shut down normally, the stop message does not need to be triggered. stop_message_guard.Dismiss(); diff --git a/Source/Core/VideoCommon/MainBase.cpp b/Source/Core/VideoCommon/MainBase.cpp index 38d37113e2..280fed683d 100644 --- a/Source/Core/VideoCommon/MainBase.cpp +++ b/Source/Core/VideoCommon/MainBase.cpp @@ -45,6 +45,15 @@ void VideoBackendBase::Video_ExitLoop() s_FifoShuttingDown.Set(); } +void VideoBackendBase::Video_CleanupShared() +{ + // First stop any framedumping, which might need to dump the last xfb frame. This process + // can require additional graphics sub-systems so it needs to be done first + g_renderer->ExitFramedumping(); + + Video_Cleanup(); +} + // Run from the CPU thread (from VideoInterface.cpp) void VideoBackendBase::Video_BeginField(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, u64 ticks) diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index ba909a22b3..e63282d527 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -97,11 +97,15 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; } -Renderer::~Renderer() +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, @@ -625,20 +629,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total; } - // The FinishFrameData call here is necessary even after frame dumping is stopped. - // If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered. - FinishFrameData(); if (IsFrameDumping() && m_last_xfb_texture) { - UpdateFrameDumpTexture(horizontal_scale); - - 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(ticks)); - } + FinishFrameData(); + } + else + { + ShutdownFrameDumping(); } bool update_frame_count = false; @@ -653,12 +650,18 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const { m_last_xfb_texture = xfb_entry->texture.get(); m_last_xfb_id = xfb_entry->id; + m_last_xfb_ticks = ticks; + m_last_xfb_horizontal_scale = horizontal_scale; // TODO: merge more generic parts into VideoCommon g_renderer->SwapImpl(xfb_entry->texture.get(), rc, ticks, xfb_entry->gamma); m_fps_counter.Update(); update_frame_count = true; + if (IsFrameDumping()) + { + DoDumpFrame(); + } } // Update our last xfb values @@ -685,10 +688,22 @@ bool Renderer::IsFrameDumping() if (SConfig::GetInstance().m_DumpFrames) return true; - ShutdownFrameDumping(); return false; } +void Renderer::DoDumpFrame() +{ + UpdateFrameDumpTexture(m_last_xfb_horizontal_scale); + + 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)); + } +} + void Renderer::UpdateFrameDumpTexture(float horizontal_scale) { int target_width, target_height; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index ef2560825c..6698f2f66f 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -142,6 +142,8 @@ public: virtual void ChangeSurface(void* new_surface_handle) {} bool UseVertexDepthRange() const; + void ExitFramedumping(); + protected: std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); @@ -179,6 +181,7 @@ protected: u32 m_last_host_config_bits = 0; private: + void DoDumpFrame(); void RunFrameDumps(); void ShutdownFrameDumping(); std::tuple CalculateOutputDimensions(int width, int height); @@ -208,8 +211,10 @@ private: AVIDump::Frame state; } m_frame_dump_config; - AbstractTexture* m_last_xfb_texture; - u64 m_last_xfb_id = 0; + AbstractTexture* m_last_xfb_texture = nullptr; + u64 m_last_xfb_id = std::numeric_limits::max(); + u64 m_last_xfb_ticks = 0; + float m_last_xfb_horizontal_scale = 0.0f; std::unique_ptr m_dump_texture; diff --git a/Source/Core/VideoCommon/VideoBackendBase.h b/Source/Core/VideoCommon/VideoBackendBase.h index 16d25c7eab..b7d6e1faa8 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.h +++ b/Source/Core/VideoCommon/VideoBackendBase.h @@ -47,7 +47,9 @@ public: virtual void Video_Prepare() = 0; void Video_ExitLoop(); - virtual void Video_Cleanup() = 0; // called from gl/d3d thread + + void Video_CleanupShared(); // called from gl/d3d thread + virtual void Video_Cleanup() = 0; void Video_BeginField(u32, u32, u32, u32, u64);