Merge pull request #4455 from stenzek/png-frame-dumping
VideoCommon: Support dumping frames to images
This commit is contained in:
commit
681294586b
|
@ -22,6 +22,8 @@
|
||||||
#include "Common/Event.h"
|
#include "Common/Event.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/Profiler.h"
|
#include "Common/Profiler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
|
@ -696,8 +698,18 @@ void Renderer::FinishFrameData()
|
||||||
void Renderer::RunFrameDumps()
|
void Renderer::RunFrameDumps()
|
||||||
{
|
{
|
||||||
Common::SetCurrentThreadName("FrameDumping");
|
Common::SetCurrentThreadName("FrameDumping");
|
||||||
bool avi_dump_started = false;
|
bool dump_to_avi = !g_ActiveConfig.bDumpFramesAsImages;
|
||||||
std::vector<u8> data;
|
bool frame_dump_started = false;
|
||||||
|
|
||||||
|
// If Dolphin was compiled without libav, we only support dumping to images.
|
||||||
|
#if !defined(HAVE_LIBAV) && !defined(_WIN32)
|
||||||
|
if (dump_to_avi)
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "AVI frame dump requested, but Dolphin was compiled without libav. "
|
||||||
|
"Frame dump will be saved as images instead.");
|
||||||
|
dump_to_avi = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -727,33 +739,103 @@ void Renderer::RunFrameDumps()
|
||||||
s_screenshotCompleted.Set();
|
s_screenshotCompleted.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
|
||||||
if (SConfig::GetInstance().m_DumpFrames)
|
if (SConfig::GetInstance().m_DumpFrames)
|
||||||
{
|
{
|
||||||
if (!avi_dump_started)
|
if (!frame_dump_started)
|
||||||
{
|
{
|
||||||
if (AVIDump::Start(config.width, config.height))
|
if (dump_to_avi)
|
||||||
{
|
frame_dump_started = StartFrameDumpToAVI(config);
|
||||||
avi_dump_started = true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
frame_dump_started = StartFrameDumpToImage(config);
|
||||||
|
|
||||||
|
// Stop frame dumping if we fail to start.
|
||||||
|
if (!frame_dump_started)
|
||||||
SConfig::GetInstance().m_DumpFrames = false;
|
SConfig::GetInstance().m_DumpFrames = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
AVIDump::AddFrame(config.data, config.width, config.height, config.stride, config.state);
|
// If we failed to start frame dumping, don't write a frame.
|
||||||
|
if (frame_dump_started)
|
||||||
|
{
|
||||||
|
if (dump_to_avi)
|
||||||
|
DumpFrameToAVI(config);
|
||||||
|
else
|
||||||
|
DumpFrameToImage(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
m_frame_dump_done.Set();
|
m_frame_dump_done.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
if (frame_dump_started)
|
||||||
if (avi_dump_started)
|
{
|
||||||
|
// No additional cleanup is needed when dumping to images.
|
||||||
|
if (dump_to_avi)
|
||||||
|
StopFrameDumpToAVI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
||||||
|
|
||||||
|
bool Renderer::StartFrameDumpToAVI(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
return AVIDump::Start(config.width, config.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::DumpFrameToAVI(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
AVIDump::AddFrame(config.data, config.width, config.height, config.stride, config.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::StopFrameDumpToAVI()
|
||||||
{
|
{
|
||||||
avi_dump_started = false;
|
|
||||||
AVIDump::Stop();
|
AVIDump::Stop();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool Renderer::StartFrameDumpToAVI(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::DumpFrameToAVI(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::StopFrameDumpToAVI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(HAVE_LIBAV) || defined(WIN32)
|
||||||
|
|
||||||
|
std::string Renderer::GetFrameDumpNextImageFileName() const
|
||||||
|
{
|
||||||
|
return StringFromFormat("%sframedump_%u.png", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(),
|
||||||
|
m_frame_dump_image_counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::StartFrameDumpToImage(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
m_frame_dump_image_counter = 1;
|
||||||
|
if (!SConfig::GetInstance().m_DumpFramesSilent)
|
||||||
|
{
|
||||||
|
// Only check for the presence of the first image to confirm overwriting.
|
||||||
|
// A previous run will always have at least one image, and it's safe to assume that if the user
|
||||||
|
// has allowed the first image to be overwritten, this will apply any remaining images as well.
|
||||||
|
std::string filename = GetFrameDumpNextImageFileName();
|
||||||
|
if (File::Exists(filename))
|
||||||
|
{
|
||||||
|
if (!AskYesNoT("Frame dump image(s) '%s' already exists. Overwrite?", filename.c_str()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::DumpFrameToImage(const FrameDumpConfig& config)
|
||||||
|
{
|
||||||
|
std::string filename = GetFrameDumpNextImageFileName();
|
||||||
|
TextureToPng(config.data, config.stride, filename, config.width, config.height, false);
|
||||||
|
m_frame_dump_image_counter++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,6 +197,7 @@ private:
|
||||||
Common::Event m_frame_dump_start;
|
Common::Event m_frame_dump_start;
|
||||||
Common::Event m_frame_dump_done;
|
Common::Event m_frame_dump_done;
|
||||||
Common::Flag m_frame_dump_thread_running;
|
Common::Flag m_frame_dump_thread_running;
|
||||||
|
u32 m_frame_dump_image_counter = 0;
|
||||||
bool m_frame_dump_frame_running = false;
|
bool m_frame_dump_frame_running = false;
|
||||||
struct FrameDumpConfig
|
struct FrameDumpConfig
|
||||||
{
|
{
|
||||||
|
@ -207,6 +208,14 @@ private:
|
||||||
bool upside_down;
|
bool upside_down;
|
||||||
AVIDump::Frame state;
|
AVIDump::Frame state;
|
||||||
} m_frame_dump_config;
|
} m_frame_dump_config;
|
||||||
|
|
||||||
|
// NOTE: The methods below are called on the framedumping thread.
|
||||||
|
bool StartFrameDumpToAVI(const FrameDumpConfig& config);
|
||||||
|
void DumpFrameToAVI(const FrameDumpConfig& config);
|
||||||
|
void StopFrameDumpToAVI();
|
||||||
|
std::string GetFrameDumpNextImageFileName() const;
|
||||||
|
bool StartFrameDumpToImage(const FrameDumpConfig& config);
|
||||||
|
void DumpFrameToImage(const FrameDumpConfig& config);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<Renderer> g_renderer;
|
extern std::unique_ptr<Renderer> g_renderer;
|
||||||
|
|
|
@ -71,6 +71,7 @@ void VideoConfig::Load(const std::string& ini_file)
|
||||||
settings->Get("ConvertHiresTextures", &bConvertHiresTextures, 0);
|
settings->Get("ConvertHiresTextures", &bConvertHiresTextures, 0);
|
||||||
settings->Get("CacheHiresTextures", &bCacheHiresTextures, 0);
|
settings->Get("CacheHiresTextures", &bCacheHiresTextures, 0);
|
||||||
settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
|
settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
|
||||||
|
settings->Get("DumpFramesAsImages", &bDumpFramesAsImages, 0);
|
||||||
settings->Get("FreeLook", &bFreeLook, 0);
|
settings->Get("FreeLook", &bFreeLook, 0);
|
||||||
settings->Get("UseFFV1", &bUseFFV1, 0);
|
settings->Get("UseFFV1", &bUseFFV1, 0);
|
||||||
settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
|
settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
|
||||||
|
@ -287,6 +288,7 @@ void VideoConfig::Save(const std::string& ini_file)
|
||||||
settings->Set("ConvertHiresTextures", bConvertHiresTextures);
|
settings->Set("ConvertHiresTextures", bConvertHiresTextures);
|
||||||
settings->Set("CacheHiresTextures", bCacheHiresTextures);
|
settings->Set("CacheHiresTextures", bCacheHiresTextures);
|
||||||
settings->Set("DumpEFBTarget", bDumpEFBTarget);
|
settings->Set("DumpEFBTarget", bDumpEFBTarget);
|
||||||
|
settings->Set("DumpFramesAsImages", bDumpFramesAsImages);
|
||||||
settings->Set("FreeLook", bFreeLook);
|
settings->Set("FreeLook", bFreeLook);
|
||||||
settings->Set("UseFFV1", bUseFFV1);
|
settings->Set("UseFFV1", bUseFFV1);
|
||||||
settings->Set("EnablePixelLighting", bEnablePixelLighting);
|
settings->Set("EnablePixelLighting", bEnablePixelLighting);
|
||||||
|
|
|
@ -99,6 +99,7 @@ struct VideoConfig final
|
||||||
bool bConvertHiresTextures;
|
bool bConvertHiresTextures;
|
||||||
bool bCacheHiresTextures;
|
bool bCacheHiresTextures;
|
||||||
bool bDumpEFBTarget;
|
bool bDumpEFBTarget;
|
||||||
|
bool bDumpFramesAsImages;
|
||||||
bool bUseFFV1;
|
bool bUseFFV1;
|
||||||
bool bFreeLook;
|
bool bFreeLook;
|
||||||
bool bBorderlessFullscreen;
|
bool bBorderlessFullscreen;
|
||||||
|
|
Loading…
Reference in New Issue