Merge pull request #4345 from degasus/framedump_threaded

Renderer: Threaded frame dumping.
This commit is contained in:
Markus Wick 2016-11-08 11:01:40 +01:00 committed by GitHub
commit 5378b6ab85
3 changed files with 121 additions and 58 deletions

View File

@ -260,6 +260,7 @@ void AVIDump::Stop()
CloseFile(); CloseFile();
s_file_index = 0; s_file_index = 0;
NOTICE_LOG(VIDEO, "Stopping frame dump"); NOTICE_LOG(VIDEO, "Stopping frame dump");
OSD::AddMessage("Stopped dumping frames");
} }
void AVIDump::CloseFile() void AVIDump::CloseFile()

View File

@ -24,6 +24,7 @@
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Common/Profiler.h" #include "Common/Profiler.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
@ -61,8 +62,7 @@ std::mutex Renderer::s_criticalScreenshot;
std::string Renderer::s_sScreenshotName; std::string Renderer::s_sScreenshotName;
Common::Event Renderer::s_screenshotCompleted; Common::Event Renderer::s_screenshotCompleted;
Common::Flag Renderer::s_screenshot;
volatile bool Renderer::s_bScreenshot;
// The framebuffer size // The framebuffer size
int Renderer::s_target_width; int Renderer::s_target_width;
@ -118,14 +118,9 @@ Renderer::~Renderer()
efb_scale_numeratorX = efb_scale_numeratorY = efb_scale_denominatorX = efb_scale_denominatorY = 1; efb_scale_numeratorX = efb_scale_numeratorY = efb_scale_denominatorX = efb_scale_denominatorY = 1;
#if defined(HAVE_LIBAV) || defined(_WIN32) ShutdownFrameDumping();
// Stop frame dumping if it was left enabled at shutdown time. if (m_frame_dump_thread.joinable())
if (m_AVI_dumping) m_frame_dump_thread.join();
{
AVIDump::Stop();
m_AVI_dumping = false;
}
#endif
} }
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
@ -307,7 +302,7 @@ void Renderer::SetScreenshot(const std::string& filename)
{ {
std::lock_guard<std::mutex> lk(s_criticalScreenshot); std::lock_guard<std::mutex> lk(s_criticalScreenshot);
s_sScreenshotName = filename; s_sScreenshotName = filename;
s_bScreenshot = true; s_screenshot.Set();
} }
// Create On-Screen-Messages // Create On-Screen-Messages
@ -648,69 +643,120 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
bool Renderer::IsFrameDumping() bool Renderer::IsFrameDumping()
{ {
if (s_bScreenshot) if (s_screenshot.IsSet())
return true; return true;
#if defined(HAVE_LIBAV) || defined(_WIN32) #if defined(HAVE_LIBAV) || defined(_WIN32)
if (SConfig::GetInstance().m_DumpFrames) if (SConfig::GetInstance().m_DumpFrames)
return true; return true;
if (m_last_frame_dumped && m_AVI_dumping)
{
AVIDump::Stop();
std::vector<u8>().swap(m_frame_data);
m_AVI_dumping = false;
OSD::AddMessage("Stop dumping frames");
}
m_last_frame_dumped = false;
#endif #endif
ShutdownFrameDumping();
return false; return false;
} }
void Renderer::ShutdownFrameDumping()
{
if (!m_frame_dump_thread_running.IsSet())
return;
FinishFrameData();
m_frame_dump_thread_running.Clear();
m_frame_dump_start.Set();
}
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state, void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state,
bool swap_upside_down) bool swap_upside_down)
{ {
if (w == 0 || h == 0) FinishFrameData();
m_frame_dump_config = FrameDumpConfig{data, w, h, stride, swap_upside_down, state};
if (!m_frame_dump_thread_running.IsSet())
{
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
m_frame_dump_thread_running.Set();
m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this);
}
m_frame_dump_start.Set();
m_frame_dump_frame_running = true;
}
void Renderer::FinishFrameData()
{
if (!m_frame_dump_frame_running)
return; return;
m_frame_dump_done.Wait();
m_frame_dump_frame_running = false;
}
void Renderer::RunFrameDumps()
{
Common::SetCurrentThreadName("FrameDumping");
bool avi_dump_started = false;
std::vector<u8> data;
while (true)
{
m_frame_dump_start.Wait();
if (!m_frame_dump_thread_running.IsSet())
break;
const auto config = m_frame_dump_config;
// TODO: Refactor this. Right now it's needed for the implace flipping of the image. // TODO: Refactor this. Right now it's needed for the implace flipping of the image.
m_frame_data.assign(data, data + stride * h); data.assign(config.data, config.data + config.stride * config.height);
if (swap_upside_down)
FlipImageData(m_frame_data.data(), w, h, 4); // As we've done a copy now, there is no need to block the GPU thread any longer.
m_frame_dump_done.Set();
if (config.upside_down)
FlipImageData(data.data(), config.width, config.height, 4);
// Save screenshot // Save screenshot
if (s_bScreenshot) if (s_screenshot.TestAndClear())
{ {
std::lock_guard<std::mutex> lk(s_criticalScreenshot); std::lock_guard<std::mutex> lk(s_criticalScreenshot);
if (TextureToPng(m_frame_data.data(), stride, s_sScreenshotName, w, h, false)) if (TextureToPng(data.data(), config.stride, s_sScreenshotName, config.width, config.height,
false))
OSD::AddMessage("Screenshot saved to " + s_sScreenshotName); OSD::AddMessage("Screenshot saved to " + s_sScreenshotName);
// Reset settings // Reset settings
s_sScreenshotName.clear(); s_sScreenshotName.clear();
s_bScreenshot = false;
s_screenshotCompleted.Set(); s_screenshotCompleted.Set();
} }
#if defined(HAVE_LIBAV) || defined(_WIN32) #if defined(HAVE_LIBAV) || defined(_WIN32)
if (SConfig::GetInstance().m_DumpFrames) if (SConfig::GetInstance().m_DumpFrames)
{ {
if (!m_last_frame_dumped) if (!avi_dump_started)
{ {
m_AVI_dumping = AVIDump::Start(w, h); if (AVIDump::Start(config.width, config.height))
{
avi_dump_started = true;
} }
if (m_AVI_dumping) else
{ {
AVIDump::AddFrame(m_frame_data.data(), w, h, stride, state); SConfig::GetInstance().m_DumpFrames = false;
}
} }
m_last_frame_dumped = true; AVIDump::AddFrame(data.data(), config.width, config.height, config.stride, config.state);
} }
#endif #endif
} }
void Renderer::FinishFrameData() #if defined(HAVE_LIBAV) || defined(_WIN32)
{ if (avi_dump_started)
{
avi_dump_started = false;
AVIDump::Stop();
}
#endif
} }
void Renderer::FlipImageData(u8* data, int w, int h, int pixel_width) void Renderer::FlipImageData(u8* data, int w, int h, int pixel_width)

View File

@ -14,9 +14,11 @@
#pragma once #pragma once
#include <condition_variable>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread>
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -151,7 +153,7 @@ protected:
bool swap_upside_down = false); bool swap_upside_down = false);
void FinishFrameData(); void FinishFrameData();
static volatile bool s_bScreenshot; static Common::Flag s_screenshot;
static std::mutex s_criticalScreenshot; static std::mutex s_criticalScreenshot;
static std::string s_sScreenshotName; static std::string s_sScreenshotName;
@ -181,16 +183,30 @@ protected:
static void* s_new_surface_handle; static void* s_new_surface_handle;
private: private:
void RunFrameDumps();
void ShutdownFrameDumping();
static PEControl::PixelFormat prev_efb_format; static PEControl::PixelFormat prev_efb_format;
static unsigned int efb_scale_numeratorX; static unsigned int efb_scale_numeratorX;
static unsigned int efb_scale_numeratorY; static unsigned int efb_scale_numeratorY;
static unsigned int efb_scale_denominatorX; static unsigned int efb_scale_denominatorX;
static unsigned int efb_scale_denominatorY; static unsigned int efb_scale_denominatorY;
// framedumping // frame dumping
std::vector<u8> m_frame_data; std::thread m_frame_dump_thread;
bool m_AVI_dumping = false; Common::Event m_frame_dump_start;
bool m_last_frame_dumped = false; Common::Event m_frame_dump_done;
Common::Flag m_frame_dump_thread_running;
bool m_frame_dump_frame_running = false;
struct FrameDumpConfig
{
const u8* data;
int width;
int height;
int stride;
bool upside_down;
AVIDump::Frame state;
} m_frame_dump_config;
}; };
extern std::unique_ptr<Renderer> g_renderer; extern std::unique_ptr<Renderer> g_renderer;