diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp
index 91da57b4bec0fa..280539dce36c7c 100644
--- a/pcsx2-gsrunner/Main.cpp
+++ b/pcsx2-gsrunner/Main.cpp
@@ -246,7 +246,7 @@ void Host::BeginPresentFrame()
GSJoinSnapshotThreads();
// queue dumping of this frame
- std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
+ std::string dump_path(fmt::format("{}_frame{:05}.png", s_output_prefix, s_dump_frame_number));
GSQueueSnapshot(dump_path);
}
@@ -443,6 +443,14 @@ static void PrintCommandLineHelp(const char* progname)
std::fprintf(stderr, " -help: Displays this information and exits.\n");
std::fprintf(stderr, " -version: Displays version information and exits.\n");
std::fprintf(stderr, " -dumpdir
: Frame dump directory (will be dumped as filename_frameN.png).\n");
+ std::fprintf(stderr, " -dump [rt|tex|z|f|a|i]: Enabling dunmping of render target, texture, z buffer, frame, "
+ "alphas, and info (context, vertices), respectively, per draw. Generates lots of data.\n");
+ std::fprintf(stderr, " -dumprange N[,L,B]: Start dumping from draw N (base 0), stops after L draws, and only "
+ "those draws that are multiples of B (intersection of -dumprange and -dumrangef used)."
+ "Defaults to N=0,L=-1,B=1 (all draws). Only used if -dump used.\n");
+ std::fprintf(stderr, " -dumprangef NF[,LF,BF]: Start dumping from frame NF (base 0), stops after LF frames, "
+ "and only those frames that are multiples of BF (intersection of -dumprange and -dumrangef used).\n"
+ "Defaults to NF=0,LF=-1,BF=1 (all frames). Only used if -dump is used.\n");
std::fprintf(stderr, " -loop : Loops dump playback N times. Defaults to 1. 0 will loop infinitely.\n");
std::fprintf(stderr, " -renderer : Sets the graphics renderer. Defaults to Auto.\n");
std::fprintf(stderr, " -window: Forces a window to be displayed.\n");
@@ -465,6 +473,7 @@ void GSRunner::InitializeConsole()
bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& params)
{
+ std::string dumpdir; // Save from argument -dumpdir for creating sub-directories
bool no_more_args = false;
for (int i = 1; i < argc; i++)
{
@@ -485,7 +494,7 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
}
else if (CHECK_ARG_PARAM("-dumpdir"))
{
- s_output_prefix = StringUtil::StripWhitespace(argv[++i]);
+ dumpdir = s_output_prefix = StringUtil::StripWhitespace(argv[++i]);
if (s_output_prefix.empty())
{
Console.Error("Invalid dump directory specified.");
@@ -500,6 +509,86 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
continue;
}
+ else if (CHECK_ARG_PARAM("-dump"))
+ {
+ std::string str(argv[++i]);
+
+ s_settings_interface.SetBoolValue("EmuCore/GS", "dump", true);
+
+ if (str.find("rt") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "save", true);
+ if (str.find("f") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "savef", true);
+ if (str.find("tex") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "savet", true);
+ if (str.find("z") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "savez", true);
+ if (str.find("a") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "savea", true);
+ if (str.find("i") != std::string::npos)
+ s_settings_interface.SetBoolValue("EmuCore/GS", "savei", true);
+ continue;
+ }
+ else if (CHECK_ARG_PARAM("-dumprange"))
+ {
+ std::string str(argv[++i]);
+
+ std::vector split = StringUtil::SplitString(str, ',');
+ int start = 0;
+ int num = -1;
+ int by = 1;
+ if (split.size() > 0)
+ {
+ start = StringUtil::FromChars(split[0]).value_or(0);
+ }
+ if (split.size() > 1)
+ {
+ num = StringUtil::FromChars(split[1]).value_or(-1);
+ }
+ if (split.size() > 2)
+ {
+ by = std::max(1, StringUtil::FromChars(split[2]).value_or(1));
+ }
+ s_settings_interface.SetIntValue("EmuCore/GS", "saven", start);
+ s_settings_interface.SetIntValue("EmuCore/GS", "savel", num);
+ s_settings_interface.SetIntValue("EmuCore/GS", "saveb", by);
+ continue;
+ }
+ else if (CHECK_ARG_PARAM("-dumprangef"))
+ {
+ std::string str(argv[++i]);
+
+ std::vector split = StringUtil::SplitString(str, ',');
+ int start = 0;
+ int num = -1;
+ int by = 1;
+ if (split.size() > 0)
+ {
+ start = StringUtil::FromChars(split[0]).value_or(0);
+ }
+ if (split.size() > 1)
+ {
+ num = StringUtil::FromChars(split[1]).value_or(-1);
+ }
+ if (split.size() > 2)
+ {
+ by = std::max(1, StringUtil::FromChars(split[2]).value_or(1));
+ }
+ s_settings_interface.SetIntValue("EmuCore/GS", "savenf", start);
+ s_settings_interface.SetIntValue("EmuCore/GS", "savelf", num);
+ s_settings_interface.SetIntValue("EmuCore/GS", "savebf", by);
+ continue;
+ }
+ else if (CHECK_ARG_PARAM("-dumpdirhw"))
+ {
+ s_settings_interface.SetStringValue("EmuCore/GS", "HWDumpDirectory", argv[++i]);
+ continue;
+ }
+ else if (CHECK_ARG_PARAM("-dumpdirsw"))
+ {
+ s_settings_interface.SetStringValue("EmuCore/GS", "SWDumpDirectory", argv[++i]);
+ continue;
+ }
else if (CHECK_ARG_PARAM("-loop"))
{
s_loop_count = StringUtil::FromChars(argv[++i]).value_or(0);
@@ -543,6 +632,19 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", static_cast(type));
continue;
}
+ else if (CHECK_ARG_PARAM("-swthreads"))
+ {
+ const int swthreads = StringUtil::FromChars(argv[++i]).value_or(0);
+ if (swthreads < 0)
+ {
+ Console.WriteLn("Invalid number of software threads");
+ return false;
+ }
+
+ Console.WriteLn(fmt::format("Setting number of software threads to {}", swthreads));
+ s_settings_interface.SetIntValue("EmuCore/GS", "SWExtraThreads", swthreads);
+ continue;
+ }
else if (CHECK_ARG_PARAM("-renderhacks"))
{
std::string str(argv[++i]);
@@ -643,6 +745,18 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
return false;
}
+ if (s_settings_interface.GetBoolValue("EmuCore/GS", "dump") && !dumpdir.empty())
+ {
+ if (s_settings_interface.GetStringValue("EmuCore/GS", "HWDumpDirectory").empty())
+ s_settings_interface.SetStringValue("EmuCore/GS", "HWDumpDirectory", dumpdir.c_str());
+ if (s_settings_interface.GetStringValue("EmuCore/GS", "SWDumpDirectory").empty())
+ s_settings_interface.SetStringValue("EmuCore/GS", "SWDumpDirectory", dumpdir.c_str());
+
+ // Disable saving frames with SaveSnapshotToMemory()
+ // Instead we save more "raw" snapshots when using -dump.
+ s_output_prefix = "";
+ }
+
// set up the frame dump directory
if (!s_output_prefix.empty())
{
diff --git a/pcsx2/Config.h b/pcsx2/Config.h
index 37e4a7dffcc31e..fa9b97ea2b1e40 100644
--- a/pcsx2/Config.h
+++ b/pcsx2/Config.h
@@ -745,6 +745,8 @@ struct Pcsx2Config
SaveFrame : 1,
SaveTexture : 1,
SaveDepth : 1,
+ SaveAlpha : 1,
+ SaveInfo : 1,
DumpReplaceableTextures : 1,
DumpReplaceableMipmaps : 1,
DumpTexturesWithFMVActive : 1,
@@ -822,6 +824,10 @@ struct Pcsx2Config
int SaveN = 0;
int SaveL = 5000;
+ int SaveB = 1;
+ int SaveNF = 0;
+ int SaveLF = -1;
+ int SaveBF = 1;
s8 ExclusiveFullscreenControl = -1;
GSScreenshotSize ScreenshotSize = GSScreenshotSize::WindowResolution;
@@ -865,6 +871,9 @@ struct Pcsx2Config
bool operator==(const GSOptions& right) const;
bool operator!=(const GSOptions& right) const;
+
+ // Should we dump this draw/frame?
+ bool ShouldDump(int draw, int frame) const;
};
struct SPU2Options
diff --git a/pcsx2/GS/GSDrawingContext.cpp b/pcsx2/GS/GSDrawingContext.cpp
index b5ee2395f594b0..b8aa57ecd04ff4 100644
--- a/pcsx2/GS/GSDrawingContext.cpp
+++ b/pcsx2/GS/GSDrawingContext.cpp
@@ -203,16 +203,15 @@ void GSDrawingContext::Dump(const std::string& filename)
"\tTBW:%u\n"
"\tPSM:0x%x\n"
"\tTW:%u\n"
+ "\tTH:%u\n"
"\tTCC:%u\n"
"\tTFX:%u\n"
"\tCBP:0x%x\n"
"\tCPSM:0x%x\n"
"\tCSM:%u\n"
"\tCSA:%u\n"
- "\tCLD:%u\n"
- "\tTH:%u\n",
- TEX0.TBP0, TEX0.TBW, TEX0.PSM, TEX0.TW, TEX0.TCC, TEX0.TFX, TEX0.CBP, TEX0.CPSM, TEX0.CSM, TEX0.CSA, TEX0.CLD,
- static_cast(TEX0.TH));
+ "\tCLD:%u\n\n",
+ TEX0.TBP0, TEX0.TBW, TEX0.PSM, TEX0.TW, static_cast(TEX0.TH), TEX0.TCC, TEX0.TFX, TEX0.CBP, TEX0.CPSM, TEX0.CSM, TEX0.CSA, TEX0.CLD);
fprintf(fp,
"TEX1\n"
diff --git a/pcsx2/GS/GSDrawingEnvironment.h b/pcsx2/GS/GSDrawingEnvironment.h
index 7d562fa9522b21..762c55d4e98c0c 100644
--- a/pcsx2/GS/GSDrawingEnvironment.h
+++ b/pcsx2/GS/GSDrawingEnvironment.h
@@ -87,7 +87,6 @@ class alignas(32) GSDrawingEnvironment
fprintf(fp, "SCANMSK\n"
"\tMSK:%u\n\n"
- "\n"
, SCANMSK.MSK);
fprintf(fp, "TEXA\n"
diff --git a/pcsx2/GS/GSLocalMemory.cpp b/pcsx2/GS/GSLocalMemory.cpp
index 98f1598cf6753a..c7ca7e313ab926 100644
--- a/pcsx2/GS/GSLocalMemory.cpp
+++ b/pcsx2/GS/GSLocalMemory.cpp
@@ -662,11 +662,7 @@ void GSLocalMemory::SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int
}
}
-#ifdef PCSX2_DEVBUILD
- GSPng::Save(GSPng::RGB_A_PNG, fn, static_cast(bits), w, h, pitch, GSConfig.PNGCompressionLevel, false);
-#else
- GSPng::Save(GSPng::RGB_PNG, fn, static_cast(bits), w, h, pitch, GSConfig.PNGCompressionLevel, false);
-#endif
+ GSPng::Save((IsDevBuild || GSConfig.SaveAlpha) ? GSPng::RGB_A_PNG : GSPng::RGB_PNG, fn, static_cast(bits), w, h, pitch, GSConfig.PNGCompressionLevel, false);
_aligned_free(bits);
}
diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp
index c6642048644831..54000504c14f1d 100644
--- a/pcsx2/GS/GSState.cpp
+++ b/pcsx2/GS/GSState.cpp
@@ -443,7 +443,7 @@ void GSState::DumpVertices(const std::string& filename)
file << std::fixed << std::setprecision(4);
for (u32 i = 0; i < count; ++i)
{
- file << "\t" << "v" << i << ": ";
+ file << "\t" << std::dec << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
const float x = (v.XYZ.X - (int)m_context->XYOFFSET.OFX) / 16.0f;
@@ -461,7 +461,7 @@ void GSState::DumpVertices(const std::string& filename)
file << std::fixed << std::setprecision(6);
for (u32 i = 0; i < count; ++i)
{
- file << "\t" << "v" << i << ": ";
+ file << "\t" << std::dec << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
@@ -479,7 +479,7 @@ void GSState::DumpVertices(const std::string& filename)
file << "TEXTURE COORDS (" << qualifier << ")" << std::endl;;
for (u32 i = 0; i < count; ++i)
{
- file << "\t" << "v" << i << ": ";
+ file << "\t" << "v" << std::dec << i << ": ";
const GSVertex v = buffer[m_index.buff[i]];
// note
@@ -1994,7 +1994,7 @@ void GSState::InitReadFIFO(u8* mem, int len)
// Read the image all in one go.
m_mem.ReadImageX(m_tr.x, m_tr.y, m_tr.buff, m_tr.total, m_env.BITBLTBUF, m_env.TRXPOS, m_env.TRXREG);
- if (GSConfig.DumpGSData && GSConfig.SaveRT && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveRT && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
const std::string s(GetDrawDumpPath(
"%05d_read_%05x_%d_%d_%d_%d_%d_%d.bmp",
@@ -2742,7 +2742,7 @@ int GSState::Defrost(const freezeData* fd)
m_mem.m_clut.Reset();
(PRIM->CTXT == 0) ? ApplyTEX0<0>(m_context->TEX0) : ApplyTEX0<1>(m_context->TEX0);
- g_perfmon.SetFrame(5000);
+ g_perfmon.SetFrame(0);
ResetPCRTC();
diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp
index 190de01fcef5a2..fc068c13a4720f 100644
--- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp
+++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp
@@ -546,9 +546,9 @@ void GSRenderer::EndPresentFrame()
void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
{
- if (GSConfig.DumpGSData && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveInfo && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
- DumpGSPrivRegs(*m_regs, GetDrawDumpPath("vsync_%05d_f%lld_gs_reg.txt", s_n, g_perfmon.GetFrame()));
+ DumpGSPrivRegs(*m_regs, GetDrawDumpPath("%05d_f%05lld_vsync_gs_reg.txt", s_n, g_perfmon.GetFrame()));
}
const int fb_sprite_blits = g_perfmon.GetDisplayFramebufferSpriteBlits();
diff --git a/pcsx2/GS/Renderers/Common/GSTexture.cpp b/pcsx2/GS/Renderers/Common/GSTexture.cpp
index dd2f566c85d6c6..e0212a711011c0 100644
--- a/pcsx2/GS/Renderers/Common/GSTexture.cpp
+++ b/pcsx2/GS/Renderers/Common/GSTexture.cpp
@@ -35,11 +35,8 @@ bool GSTexture::Save(const std::string& fn)
return res;
}
-#ifdef PCSX2_DEVBUILD
- GSPng::Format format = GSPng::RGB_A_PNG;
-#else
- GSPng::Format format = GSPng::RGB_PNG;
-#endif
+ GSPng::Format format = (IsDevBuild || GSConfig.SaveAlpha) ? GSPng::RGB_A_PNG : GSPng::RGB_PNG;
+
switch (m_format)
{
case Format::UNorm8:
diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp
index eb3acb08e49515..217bacd626d4d9 100644
--- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp
+++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp
@@ -179,12 +179,9 @@ GSTexture* GSRendererHW::GetOutput(int i, float& scale, int& y_offset)
}
#ifdef ENABLE_OGL_DEBUG
- if (GSConfig.DumpGSData)
+ if (GSConfig.SaveFrame && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
- if (GSConfig.SaveFrame && s_n >= GSConfig.SaveN)
- {
- t->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, static_cast(TEX0.TBP0), psm_str(TEX0.PSM)));
- }
+ t->Save(GetDrawDumpPath("%05d_f%05lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, static_cast(TEX0.TBP0), psm_str(TEX0.PSM)));
}
#endif
}
@@ -211,8 +208,8 @@ GSTexture* GSRendererHW::GetFeedbackOutput(float& scale)
scale = rt->m_scale;
#ifdef ENABLE_OGL_DEBUG
- if (GSConfig.DumpGSData && GSConfig.SaveFrame && s_n >= GSConfig.SaveN)
- t->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), 3, static_cast(TEX0.TBP0), psm_str(TEX0.PSM)));
+ if (GSConfig.SaveFrame && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
+ t->Save(GetDrawDumpPath("%05d_f%05lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), 3, static_cast(TEX0.TBP0), psm_str(TEX0.PSM)));
#endif
return t;
@@ -1984,7 +1981,7 @@ void GSRendererHW::RoundSpriteOffset()
void GSRendererHW::Draw()
{
- if (GSConfig.DumpGSData && (s_n >= GSConfig.SaveN))
+ if (GSConfig.SaveInfo && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
std::string s;
@@ -3264,15 +3261,15 @@ void GSRendererHW::Draw()
src->m_texture = src->m_from_target->m_texture;
}
- if (GSConfig.DumpGSData)
+ if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
const u64 frame = g_perfmon.GetFrame();
std::string s;
- if (GSConfig.SaveTexture && s_n >= GSConfig.SaveN && src)
+ if (GSConfig.SaveTexture && src)
{
- s = GetDrawDumpPath("%05d_f%lld_itex_%05x_%s_%d%d_%02x_%02x_%02x_%02x.dds",
+ s = GetDrawDumpPath("%05d_f%05lld_itex_%05x_%s_%d%d_%02x_%02x_%02x_%02x.dds",
s_n, frame, static_cast(m_cached_ctx.TEX0.TBP0), psm_str(m_cached_ctx.TEX0.PSM),
static_cast(m_cached_ctx.CLAMP.WMS), static_cast(m_cached_ctx.CLAMP.WMT),
static_cast(m_cached_ctx.CLAMP.MINU), static_cast(m_cached_ctx.CLAMP.MAXU),
@@ -3282,23 +3279,23 @@ void GSRendererHW::Draw()
if (src->m_palette)
{
- s = GetDrawDumpPath("%05d_f%lld_itpx_%05x_%s.dds", s_n, frame, m_cached_ctx.TEX0.CBP, psm_str(m_cached_ctx.TEX0.CPSM));
+ s = GetDrawDumpPath("%05d_f%05lld_itpx_%05x_%s.dds", s_n, frame, m_cached_ctx.TEX0.CBP, psm_str(m_cached_ctx.TEX0.CPSM));
src->m_palette->Save(s);
}
}
- if (rt && GSConfig.SaveRT && s_n >= GSConfig.SaveN)
+ if (rt && GSConfig.SaveRT)
{
- s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, m_cached_ctx.FRAME.Block(), psm_str(m_cached_ctx.FRAME.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rt0_%05x_%s.bmp", s_n, frame, m_cached_ctx.FRAME.Block(), psm_str(m_cached_ctx.FRAME.PSM));
if (rt->m_texture)
rt->m_texture->Save(s);
}
- if (ds && GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
+ if (ds && GSConfig.SaveDepth)
{
- s = GetDrawDumpPath("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, m_cached_ctx.ZBUF.Block(), psm_str(m_cached_ctx.ZBUF.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rz0_%05x_%s.bmp", s_n, frame, m_cached_ctx.ZBUF.Block(), psm_str(m_cached_ctx.ZBUF.PSM));
if (ds->m_texture)
ds->m_texture->Save(s);
@@ -3415,30 +3412,25 @@ void GSRendererHW::Draw()
//
- if (GSConfig.DumpGSData)
+ if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
const u64 frame = g_perfmon.GetFrame();
std::string s;
- if (rt && GSConfig.SaveRT && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveRT)
{
- s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_(%05x)_%s.bmp", s_n, frame, m_cached_ctx.FRAME.Block(), rt->m_TEX0.TBP0, psm_str(m_cached_ctx.FRAME.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rt1_(%05x)_%s.bmp", s_n, frame, m_cached_ctx.FRAME.Block(), psm_str(m_cached_ctx.FRAME.PSM));
rt->m_texture->Save(s);
}
- if (ds && GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveDepth)
{
- s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_(%05x)_%s.bmp", s_n, frame, m_cached_ctx.ZBUF.Block(), ds->m_TEX0.TBP0, psm_str(m_cached_ctx.ZBUF.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rz1_(%05x)_%s.bmp", s_n, frame, m_cached_ctx.ZBUF.Block(), psm_str(m_cached_ctx.ZBUF.PSM));
ds->m_texture->Save(s);
}
-
- if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
- {
- GSConfig.DumpGSData = 0;
- }
}
if (rt)
diff --git a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
index 8910d8af577590..4114b504d3cbbd 100644
--- a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
+++ b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
@@ -179,12 +179,9 @@ GSTexture* GSRendererSW::GetOutput(int i, float& scale, int& y_offset)
m_texture[index]->Update(out_r, m_output, pitch);
- if (GSConfig.DumpGSData)
+ if (GSConfig.SaveFrame && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
- if (GSConfig.SaveFrame && s_n >= GSConfig.SaveN)
- {
- m_texture[index]->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)curFramebuffer.Block(), psm_str(curFramebuffer.PSM)));
- }
+ m_texture[index]->Save(GetDrawDumpPath("%05d_f%05lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)curFramebuffer.Block(), psm_str(curFramebuffer.PSM)));
}
}
@@ -312,22 +309,19 @@ void GSRendererSW::Draw()
{
const GSDrawingContext* context = m_context;
- if (GSConfig.DumpGSData)
+ if (GSConfig.SaveInfo && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
std::string s;
- if (s_n >= GSConfig.SaveN)
- {
- // Dump Register state
- s = GetDrawDumpPath("%05d_context.txt", s_n);
+ // Dump Register state
+ s = GetDrawDumpPath("%05d_context.txt", s_n);
- m_draw_env->Dump(s);
- m_context->Dump(s);
+ m_draw_env->Dump(s);
+ m_context->Dump(s);
- // Dump vertices
- s = GetDrawDumpPath("%05d_vertex.txt", s_n);
- DumpVertices(s);
- }
+ // Dump vertices
+ s = GetDrawDumpPath("%05d_vertex.txt", s_n);
+ DumpVertices(s);
}
auto data = m_vertex_heap.make_shared().cast();
@@ -431,9 +425,7 @@ void GSRendererSW::Draw()
sd->UsePages(fb_pages, m_context->offset.fb.psm(), zb_pages, m_context->offset.zb.psm());
- //
-
- if (GSConfig.DumpGSData)
+ if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
Sync(2);
@@ -444,36 +436,36 @@ void GSRendererSW::Draw()
// It will breaks the few games that really uses 16 bits RT
bool texture_shuffle = ((context->FRAME.PSM & 0x2) && ((context->TEX0.PSM & 3) == 2) && (m_vt.m_primclass == GS_SPRITE_CLASS));
- if (GSConfig.SaveTexture && s_n >= GSConfig.SaveN && PRIM->TME)
+ if (GSConfig.SaveTexture && PRIM->TME)
{
if (texture_shuffle)
{
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
- s = GetDrawDumpPath("%05d_f%lld_itexraw_%05x_32bits.bmp", s_n, frame, (int)m_context->TEX0.TBP0);
+ s = GetDrawDumpPath("%05d_f%05lld_itexraw_%05x_32bits.bmp", s_n, frame, (int)m_context->TEX0.TBP0);
m_mem.SaveBMP(s, m_context->TEX0.TBP0, m_context->TEX0.TBW, 0, 1 << m_context->TEX0.TW, 1 << m_context->TEX0.TH);
}
- s = GetDrawDumpPath("%05d_f%lld_itexraw_%05x_%s.bmp", s_n, frame, (int)m_context->TEX0.TBP0, psm_str(m_context->TEX0.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_itexraw_%05x_%s.bmp", s_n, frame, (int)m_context->TEX0.TBP0, psm_str(m_context->TEX0.PSM));
m_mem.SaveBMP(s, m_context->TEX0.TBP0, m_context->TEX0.TBW, m_context->TEX0.PSM, 1 << m_context->TEX0.TW, 1 << m_context->TEX0.TH);
}
- if (GSConfig.SaveRT && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveRT)
{
if (texture_shuffle)
{
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
- s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
+ s = GetDrawDumpPath("%05d_f%05lld_rt0_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, r.z, r.w);
}
- s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rt0_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, r.z, r.w);
}
- if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveDepth)
{
- s = GetDrawDumpPath("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rz0_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, r.z, r.w);
}
@@ -482,30 +474,25 @@ void GSRendererSW::Draw()
Sync(3);
- if (GSConfig.SaveRT && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveRT)
{
if (texture_shuffle)
{
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
- s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
+ s = GetDrawDumpPath("%05d_f%05lld_rt1_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, r.z, r.w);
}
- s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rt1_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, r.z, r.w);
}
- if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
+ if (GSConfig.SaveDepth)
{
- s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rz1_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, r.z, r.w);
}
-
- if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
- {
- GSConfig.DumpGSData = 0;
- }
}
else
{
@@ -585,14 +572,14 @@ void GSRendererSW::Sync(int reason)
if (GSConfig.SaveRT)
{
- s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_rt1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
}
if (GSConfig.SaveDepth)
{
- s = GetDrawDumpPath("%05d_f%lld_zb1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_zb1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
}
@@ -1546,30 +1533,25 @@ void GSRendererSW::SharedData::UpdateSource()
}
}
- // TODO
-
- if (GSConfig.DumpGSData)
+ if (GSConfig.SaveTexture && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
const u64 frame = g_perfmon.GetFrame();
std::string s;
- if (GSConfig.SaveTexture && g_gs_renderer->s_n >= GSConfig.SaveN)
+ for (size_t i = 0; m_tex[i].t; i++)
{
- for (size_t i = 0; m_tex[i].t; i++)
- {
- const GIFRegTEX0& TEX0 = g_gs_renderer->GetTex0Layer(i);
+ const GIFRegTEX0& TEX0 = g_gs_renderer->GetTex0Layer(i);
- s = GetDrawDumpPath("%05d_f%lld_itex%d_%05x_%s.bmp", g_gs_renderer->s_n, frame, i, TEX0.TBP0, psm_str(TEX0.PSM));
+ s = GetDrawDumpPath("%05d_f%05lld_itex%d_%05x_%s.bmp", g_gs_renderer->s_n, frame, i, TEX0.TBP0, psm_str(TEX0.PSM));
- m_tex[i].t->Save(s);
- }
+ m_tex[i].t->Save(s);
+ }
- if (global.clut)
- {
- s = GetDrawDumpPath("%05d_f%lld_itexp_%05x_%s.bmp", g_gs_renderer->s_n, frame, (int)g_gs_renderer->m_context->TEX0.CBP, psm_str(g_gs_renderer->m_context->TEX0.CPSM));
- GSPng::Save(IsDevBuild ? GSPng::RGB_A_PNG : GSPng::RGB_PNG, s, reinterpret_cast(global.clut), 256, 1, sizeof(u32) * 256, GSConfig.PNGCompressionLevel, false);
- }
+ if (global.clut)
+ {
+ s = GetDrawDumpPath("%05d_f%05lld_itexp_%05x_%s.bmp", g_gs_renderer->s_n, frame, (int)g_gs_renderer->m_context->TEX0.CBP, psm_str(g_gs_renderer->m_context->TEX0.CPSM));
+ GSPng::Save((IsDevBuild || GSConfig.SaveAlpha) ? GSPng::RGB_A_PNG : GSPng::RGB_PNG, s, reinterpret_cast(global.clut), 256, 1, sizeof(u32) * 256, GSConfig.PNGCompressionLevel, false);
}
}
}
diff --git a/pcsx2/GS/Renderers/SW/GSTextureCacheSW.cpp b/pcsx2/GS/Renderers/SW/GSTextureCacheSW.cpp
index 7079f06fd983d8..2f8d53fecc7da5 100644
--- a/pcsx2/GS/Renderers/SW/GSTextureCacheSW.cpp
+++ b/pcsx2/GS/Renderers/SW/GSTextureCacheSW.cpp
@@ -317,7 +317,7 @@ bool GSTextureCacheSW::Texture::Save(const std::string& fn) const
const u32 w = 1 << m_TEX0.TW;
const u32 h = 1 << m_TEX0.TH;
- constexpr GSPng::Format format = IsDevBuild ? GSPng::RGB_A_PNG : GSPng::RGB_PNG;
+ const GSPng::Format format = (IsDevBuild || GSConfig.SaveAlpha) ? GSPng::RGB_A_PNG : GSPng::RGB_PNG;
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[m_TEX0.PSM];
const u8* RESTRICT src = (u8*)m_buff;
const u32 src_pitch = 1u << (m_tw + (psm.pal == 0 ? 2 : 0));
diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp
index ae9fcb2605df28..52212a0da70af9 100644
--- a/pcsx2/Pcsx2Config.cpp
+++ b/pcsx2/Pcsx2Config.cpp
@@ -840,6 +840,10 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(PNGCompressionLevel) &&
OpEqu(SaveN) &&
OpEqu(SaveL) &&
+ OpEqu(SaveB) &&
+ OpEqu(SaveNF) &&
+ OpEqu(SaveLF) &&
+ OpEqu(SaveBF) &&
OpEqu(ExclusiveFullscreenControl) &&
OpEqu(ScreenshotSize) &&
@@ -966,6 +970,8 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBoolEx(SaveFrame, "savef");
SettingsWrapBitBoolEx(SaveTexture, "savet");
SettingsWrapBitBoolEx(SaveDepth, "savez");
+ SettingsWrapBitBoolEx(SaveAlpha, "savea");
+ SettingsWrapBitBoolEx(SaveInfo, "savei");
SettingsWrapBitBool(DumpReplaceableTextures);
SettingsWrapBitBool(DumpReplaceableMipmaps);
SettingsWrapBitBool(DumpTexturesWithFMVActive);
@@ -1026,6 +1032,10 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitfieldEx(PNGCompressionLevel, "png_compression_level");
SettingsWrapBitfieldEx(SaveN, "saven");
SettingsWrapBitfieldEx(SaveL, "savel");
+ SettingsWrapBitfieldEx(SaveB, "saveb");
+ SettingsWrapBitfieldEx(SaveNF, "savenf");
+ SettingsWrapBitfieldEx(SaveLF, "savelf");
+ SettingsWrapBitfieldEx(SaveBF, "savebf");
SettingsWrapEntryEx(CaptureContainer, "CaptureContainer");
SettingsWrapEntryEx(VideoCaptureCodec, "VideoCaptureCodec");
@@ -1110,6 +1120,13 @@ bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
return (Renderer != GSRendererType::Null && Renderer != GSRendererType::SW);
}
+bool Pcsx2Config::GSOptions::ShouldDump(int draw, int frame) const
+{
+ return DumpGSData &&
+ (SaveN <= draw) && ((SaveL < 0) || (draw < SaveN + SaveL)) && (draw % SaveB == 0) &&
+ (SaveNF <= frame) && ((SaveLF < 0) || (frame < SaveNF + SaveLF)) && (frame % SaveBF == 0);
+}
+
static constexpr const std::array s_spu2_sync_mode_names = {
"Disabled",
"TimeStretch",