diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 91da57b4bec0fa..745c47b4c29e35 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); @@ -643,6 +732,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",