diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index e3d3bea2d5..b1fae3b98c 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -767,6 +767,9 @@ Renderer::Renderer() Renderer::~Renderer() { + FlushFrameDump(); + if (m_frame_dumping_pbo[0]) + glDeleteBuffers(2, m_frame_dumping_pbo.data()); } void Renderer::Shutdown() @@ -1447,19 +1450,9 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - if (IsFrameDumping()) - { - std::vector image(flipped_trc.GetWidth() * flipped_trc.GetHeight() * 4); + FlushFrameDump(); + DumpFrame(flipped_trc, ticks); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(flipped_trc.left, flipped_trc.bottom, flipped_trc.GetWidth(), - flipped_trc.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, image.data()); - - AVIDump::Frame state = AVIDump::FetchState(ticks); - DumpFrameData(image.data(), flipped_trc.GetWidth(), flipped_trc.GetHeight(), - flipped_trc.GetWidth() * 4, state, true); - FinishFrameData(); - } // Finish up the current frame, print some stats SetWindowSize(fbStride, fbHeight); @@ -1588,6 +1581,62 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, ClearEFBCache(); } +void Renderer::FlushFrameDump() +{ + if (!m_last_frame_exported) + return; + + FinishFrameData(); + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]); + m_frame_pbo_is_mapped[0] = true; + void* data = glMapBufferRange( + GL_PIXEL_PACK_BUFFER, 0, m_last_frame_width[0] * m_last_frame_height[0] * 4, GL_MAP_READ_BIT); + DumpFrameData(reinterpret_cast(data), m_last_frame_width[0], m_last_frame_height[0], + m_last_frame_width[0] * 4, m_last_frame_state, true); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + m_last_frame_exported = false; +} + +void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks) +{ + if (!IsFrameDumping()) + return; + + if (!m_frame_dumping_pbo[0]) + { + glGenBuffers(2, m_frame_dumping_pbo.data()); + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]); + } + else + { + std::swap(m_frame_dumping_pbo[0], m_frame_dumping_pbo[1]); + std::swap(m_frame_pbo_is_mapped[0], m_frame_pbo_is_mapped[1]); + std::swap(m_last_frame_width[0], m_last_frame_width[1]); + std::swap(m_last_frame_height[0], m_last_frame_height[1]); + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]); + if (m_frame_pbo_is_mapped[0]) + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + m_frame_pbo_is_mapped[0] = false; + } + + if (flipped_trc.GetWidth() != m_last_frame_width[0] || + flipped_trc.GetHeight() != m_last_frame_height[0]) + { + m_last_frame_width[0] = flipped_trc.GetWidth(); + m_last_frame_height[0] = flipped_trc.GetHeight(); + glBufferData(GL_PIXEL_PACK_BUFFER, m_last_frame_width[0] * m_last_frame_height[0] * 4, nullptr, + GL_STREAM_READ); + } + + m_last_frame_state = AVIDump::FetchState(ticks); + m_last_frame_exported = true; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(flipped_trc.left, flipped_trc.bottom, m_last_frame_width[0], m_last_frame_height[0], + GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +} + // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing void Renderer::ResetAPIState() { diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index a74948065c..cfa9a3ddd4 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "VideoCommon/RenderBase.h" @@ -111,5 +112,16 @@ private: void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, int src_height); + + void FlushFrameDump(); + void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks); + + // avi dumping state to delay one frame + std::array m_frame_dumping_pbo = {}; + std::array m_frame_pbo_is_mapped = {}; + std::array m_last_frame_width = {}; + std::array m_last_frame_height = {}; + bool m_last_frame_exported = false; + AVIDump::Frame m_last_frame_state; }; }