mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
VideoCommon: Support dumping frames to images
This is mainly for potential Android fifoci usage, and thus is not exposed anywhere in the UI. To enable, set DumpFramesAsImages under Settings in GFX.ini.
This commit is contained in:
parent
b6f9bdf16b
commit
6d0b9b816f
@ -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)
|
|
||||||
{
|
{
|
||||||
avi_dump_started = false;
|
// No additional cleanup is needed when dumping to images.
|
||||||
AVIDump::Stop();
|
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++;
|
||||||
}
|
}
|
||||||
|
@ -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
Block a user