Merge pull request #4455 from stenzek/png-frame-dumping

VideoCommon: Support dumping frames to images
This commit is contained in:
Stenzek 2016-11-28 20:03:50 +10:00 committed by GitHub
commit 681294586b
4 changed files with 111 additions and 17 deletions

View File

@ -22,6 +22,8 @@
#include "Common/Event.h"
#include "Common/FileUtil.h"
#include "Common/Flag.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Profiler.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
@ -696,8 +698,18 @@ void Renderer::FinishFrameData()
void Renderer::RunFrameDumps()
{
Common::SetCurrentThreadName("FrameDumping");
bool avi_dump_started = false;
std::vector<u8> data;
bool dump_to_avi = !g_ActiveConfig.bDumpFramesAsImages;
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)
{
@ -727,33 +739,103 @@ void Renderer::RunFrameDumps()
s_screenshotCompleted.Set();
}
#if defined(HAVE_LIBAV) || defined(_WIN32)
if (SConfig::GetInstance().m_DumpFrames)
{
if (!avi_dump_started)
if (!frame_dump_started)
{
if (AVIDump::Start(config.width, config.height))
{
avi_dump_started = true;
}
if (dump_to_avi)
frame_dump_started = StartFrameDumpToAVI(config);
else
{
frame_dump_started = StartFrameDumpToImage(config);
// Stop frame dumping if we fail to start.
if (!frame_dump_started)
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();
}
#if defined(HAVE_LIBAV) || defined(_WIN32)
if (avi_dump_started)
if (frame_dump_started)
{
avi_dump_started = false;
AVIDump::Stop();
// No additional cleanup is needed when dumping to images.
if (dump_to_avi)
StopFrameDumpToAVI();
}
#endif
}
#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()
{
AVIDump::Stop();
}
#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++;
}

View File

@ -197,6 +197,7 @@ private:
Common::Event m_frame_dump_start;
Common::Event m_frame_dump_done;
Common::Flag m_frame_dump_thread_running;
u32 m_frame_dump_image_counter = 0;
bool m_frame_dump_frame_running = false;
struct FrameDumpConfig
{
@ -207,6 +208,14 @@ private:
bool upside_down;
AVIDump::Frame state;
} 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;

View File

@ -71,6 +71,7 @@ void VideoConfig::Load(const std::string& ini_file)
settings->Get("ConvertHiresTextures", &bConvertHiresTextures, 0);
settings->Get("CacheHiresTextures", &bCacheHiresTextures, 0);
settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
settings->Get("DumpFramesAsImages", &bDumpFramesAsImages, 0);
settings->Get("FreeLook", &bFreeLook, 0);
settings->Get("UseFFV1", &bUseFFV1, 0);
settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
@ -287,6 +288,7 @@ void VideoConfig::Save(const std::string& ini_file)
settings->Set("ConvertHiresTextures", bConvertHiresTextures);
settings->Set("CacheHiresTextures", bCacheHiresTextures);
settings->Set("DumpEFBTarget", bDumpEFBTarget);
settings->Set("DumpFramesAsImages", bDumpFramesAsImages);
settings->Set("FreeLook", bFreeLook);
settings->Set("UseFFV1", bUseFFV1);
settings->Set("EnablePixelLighting", bEnablePixelLighting);

View File

@ -99,6 +99,7 @@ struct VideoConfig final
bool bConvertHiresTextures;
bool bCacheHiresTextures;
bool bDumpEFBTarget;
bool bDumpFramesAsImages;
bool bUseFFV1;
bool bFreeLook;
bool bBorderlessFullscreen;