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/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)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
#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_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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -99,6 +99,7 @@ struct VideoConfig final
|
|||
bool bConvertHiresTextures;
|
||||
bool bCacheHiresTextures;
|
||||
bool bDumpEFBTarget;
|
||||
bool bDumpFramesAsImages;
|
||||
bool bUseFFV1;
|
||||
bool bFreeLook;
|
||||
bool bBorderlessFullscreen;
|
||||
|
|
Loading…
Reference in New Issue