Merge pull request #4345 from degasus/framedump_threaded
Renderer: Threaded frame dumping.
This commit is contained in:
commit
5378b6ab85
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: Refactor this. Right now it's needed for the implace flipping of the image.
|
m_frame_dump_config = FrameDumpConfig{data, w, h, stride, swap_upside_down, state};
|
||||||
m_frame_data.assign(data, data + stride * h);
|
|
||||||
if (swap_upside_down)
|
|
||||||
FlipImageData(m_frame_data.data(), w, h, 4);
|
|
||||||
|
|
||||||
// Save screenshot
|
if (!m_frame_dump_thread_running.IsSet())
|
||||||
if (s_bScreenshot)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
if (m_frame_dump_thread.joinable())
|
||||||
|
m_frame_dump_thread.join();
|
||||||
if (TextureToPng(m_frame_data.data(), stride, s_sScreenshotName, w, h, false))
|
m_frame_dump_thread_running.Set();
|
||||||
OSD::AddMessage("Screenshot saved to " + s_sScreenshotName);
|
m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this);
|
||||||
|
|
||||||
// Reset settings
|
|
||||||
s_sScreenshotName.clear();
|
|
||||||
s_bScreenshot = false;
|
|
||||||
s_screenshotCompleted.Set();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
m_frame_dump_start.Set();
|
||||||
if (SConfig::GetInstance().m_DumpFrames)
|
m_frame_dump_frame_running = true;
|
||||||
{
|
|
||||||
if (!m_last_frame_dumped)
|
|
||||||
{
|
|
||||||
m_AVI_dumping = AVIDump::Start(w, h);
|
|
||||||
}
|
|
||||||
if (m_AVI_dumping)
|
|
||||||
{
|
|
||||||
AVIDump::AddFrame(m_frame_data.data(), w, h, stride, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_last_frame_dumped = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::FinishFrameData()
|
void Renderer::FinishFrameData()
|
||||||
{
|
{
|
||||||
|
if (!m_frame_dump_frame_running)
|
||||||
|
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.
|
||||||
|
data.assign(config.data, config.data + config.stride * config.height);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
if (s_screenshot.TestAndClear())
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
||||||
|
|
||||||
|
if (TextureToPng(data.data(), config.stride, s_sScreenshotName, config.width, config.height,
|
||||||
|
false))
|
||||||
|
OSD::AddMessage("Screenshot saved to " + s_sScreenshotName);
|
||||||
|
|
||||||
|
// Reset settings
|
||||||
|
s_sScreenshotName.clear();
|
||||||
|
s_screenshotCompleted.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
||||||
|
if (SConfig::GetInstance().m_DumpFrames)
|
||||||
|
{
|
||||||
|
if (!avi_dump_started)
|
||||||
|
{
|
||||||
|
if (AVIDump::Start(config.width, config.height))
|
||||||
|
{
|
||||||
|
avi_dump_started = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SConfig::GetInstance().m_DumpFrames = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AVIDump::AddFrame(data.data(), config.width, config.height, config.stride, config.state);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue