GS: Allow dumping draw/frames stats.

This commit is contained in:
TJnotJT 2025-10-04 09:41:51 -04:00 committed by lightningterror
parent 6ab02e76f1
commit be1af0cd0f
12 changed files with 124 additions and 3 deletions

View File

@ -446,8 +446,8 @@ 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 <dir>: Frame dump directory (will be dumped as filename_frameN.png).\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i|tr]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices, transfers (list)), transfers (images), respectively, per draw. Generates lots of data.\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i|tr|ds|fs]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices, list of transfers), transfers images, draw stats, frame stats, 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 -dumprangef used)."
"Defaults to 0,-1,1 (all draws). Only used if -dump used.\n");
@ -533,6 +533,10 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveInfo", true);
if (str.find("tr") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveTransferImages", true);
if (str.find("ds") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveDrawStats", true);
if (str.find("fs") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveFrameStats", true);
continue;
}
else if (CHECK_ARG_PARAM("-dumprange"))

View File

@ -71,7 +71,21 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="4" column="0">
<widget class="QCheckBox" name="saveDrawStats">
<property name="text">
<string>Save Draw Stats</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="saveFrameStats">
<property name="text">
<string>Save Frame Stats</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="saveTransferImages">
<property name="text">
<string>Save Transfer Image Data</string>

View File

@ -110,6 +110,8 @@ DebugSettingsWidget::DebugSettingsWidget(SettingsWindow* settings_dialog, QWidge
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveAlpha, "EmuCore/GS", "SaveAlpha", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveInfo, "EmuCore/GS", "SaveInfo", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveTransferImages, "EmuCore/GS", "SaveTransferImages", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveDrawStats, "EmuCore/GS", "SaveDrawStats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveFrameStats, "EmuCore/GS", "SaveFrameStats", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveDrawStart, "EmuCore/GS", "SaveDrawStart", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveDrawCount, "EmuCore/GS", "SaveDrawCount", 5000);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveFrameStart, "EmuCore/GS", "SaveFrameStart", 0);
@ -215,6 +217,8 @@ void DebugSettingsWidget::onDrawDumpingChanged()
m_gs.saveAlpha->setEnabled(enabled);
m_gs.saveInfo->setEnabled(enabled);
m_gs.saveTransferImages->setEnabled(enabled);
m_gs.saveDrawStats->setEnabled(enabled);
m_gs.saveFrameStats->setEnabled(enabled);
m_gs.saveDrawStart->setEnabled(enabled);
m_gs.saveDrawCount->setEnabled(enabled);
m_gs.saveFrameStart->setEnabled(enabled);

View File

@ -778,6 +778,8 @@ struct Pcsx2Config
SaveAlpha : 1,
SaveInfo : 1,
SaveTransferImages : 1,
SaveDrawStats : 1,
SaveFrameStats : 1,
DumpReplaceableTextures : 1,
DumpReplaceableMipmaps : 1,
DumpTexturesWithFMVActive : 1,

View File

@ -3,8 +3,10 @@
#include "GSPerfMon.h"
#include "GS.h"
#include "GSUtil.h"
#include <cstring>
#include <inttypes.h>
GSPerfMon g_perfmon;
@ -41,3 +43,28 @@ void GSPerfMon::Update()
memset(m_counters, 0, sizeof(m_counters));
}
GSPerfMon GSPerfMon::operator-(const GSPerfMon& other)
{
GSPerfMon diff;
for (std::size_t i = 0; i < std::size(diff.m_counters); i++)
{
diff.m_counters[i] = m_counters[i] - other.m_counters[i];
}
return diff;
}
void GSPerfMon::Dump(const std::string& filename, bool hw)
{
FILE* fp = fopen(filename.c_str(), "w");
if (!fp)
return;
std::size_t last = hw ? CounterLastHW : CounterLastSW;
for (std::size_t i = 0; i < last; i++)
{
fprintf(fp, "%s: %" PRIu64 "\n", GSUtil::GetPerfMonCounterName(static_cast<counter_t>(i), hw), static_cast<u64>(m_counters[i]));
}
fclose(fp);
}

View File

@ -6,6 +6,7 @@
#include "common/Pcsx2Defs.h"
#include <ctime>
#include <string>
class GSPerfMon
{
@ -27,6 +28,9 @@ public:
// Reused counters for HW.
TextureCopies = Fillrate,
TextureUploads = SyncPoint,
CounterLastHW = CounterLast,
CounterLastSW = SyncPoint + 1
};
protected:
@ -58,6 +62,10 @@ public:
m_disp_fb_sprite_blits = 0;
return blits;
}
GSPerfMon operator-(const GSPerfMon& other);
void Dump(const std::string& filename, bool hw);
};
extern GSPerfMon g_perfmon;

View File

@ -202,6 +202,9 @@ void GSState::Reset(bool hardware_reset)
m_backed_up_ctx = -1;
memcpy(&m_prev_env, &m_env, sizeof(m_prev_env));
m_perfmon_draw.Reset();
m_perfmon_frame.Reset();
}
template<bool auto_flush>
@ -2125,6 +2128,16 @@ void GSState::FlushPrim()
g_perfmon.Put(GSPerfMon::Draw, 1);
g_perfmon.Put(GSPerfMon::Prim, m_index.tail / GSUtil::GetVertexCount(PRIM->PRIM));
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
if (GSConfig.SaveDrawStats)
{
m_perfmon_draw = g_perfmon - m_perfmon_draw;
m_perfmon_draw.Dump(GetDrawDumpPath("%05d_draw_stats.txt", s_n), GSIsHardwareRenderer());
m_perfmon_draw = g_perfmon;
}
}
m_index.tail = 0;
m_vertex.head = 0;

View File

@ -4,6 +4,7 @@
#pragma once
#include "GS/GS.h"
#include "GS/GSPerfMon.h"
#include "GS/GSLocalMemory.h"
#include "GS/GSDrawingContext.h"
#include "GS/GSDrawingEnvironment.h"
@ -264,6 +265,9 @@ public:
static int s_last_transfer_draw_n;
static int s_transfer_n;
GSPerfMon m_perfmon_frame; // Track stat across a frame.
GSPerfMon m_perfmon_draw; // Track stat across a draw.
static constexpr u32 STATE_VERSION = 9;
enum REG_DIRTY

View File

@ -176,6 +176,40 @@ const char* GSUtil::GetACName(u32 ac)
return (ac < std::size(names)) ? names[ac] : "";
}
const char* GSUtil::GetPerfMonCounterName(GSPerfMon::counter_t counter, bool hw)
{
if (hw)
{
static constexpr const char* names_hw[GSPerfMon::CounterLastHW] = {
"Prim",
"Draw",
"DrawCalls",
"Readbacks",
"Swizzle",
"Unswizzle",
"TextureCopies",
"TextureUploads",
"Barriers",
"RenderPasses"
};
return counter < std::size(names_hw) ? names_hw[counter] : "";
}
else
{
static constexpr const char* names_sw[GSPerfMon::CounterLastSW] = {
"Prim",
"Draw",
"DrawCalls",
"Readbacks",
"Swizzle",
"Unswizzle",
"Fillrate",
"SyncPoint"
};
return counter < std::size(names_sw) ? names_sw[counter] : "";
}
}
const u32* GSUtil::HasSharedBitsPtr(u32 dpsm)
{
return s_maps.SharedBitsField[dpsm];

View File

@ -5,6 +5,7 @@
#include "GS.h"
#include "GSRegs.h"
#include "GSPerfMon.h"
class GSUtil
{
@ -25,6 +26,7 @@ public:
static const char* GetTFXName(u32 tfx);
static const char* GetTCCName(u32 tcc);
static const char* GetACName(u32 ac);
static const char* GetPerfMonCounterName(GSPerfMon::counter_t counter, bool hw = true);
static const u32* HasSharedBitsPtr(u32 dpsm);
static bool HasSharedBits(u32 spsm, const u32* ptr);

View File

@ -591,6 +591,13 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
if (GSConfig.SaveTransferImages)
DumpTransferImages();
if (GSConfig.SaveFrameStats)
{
m_perfmon_frame = g_perfmon - m_perfmon_frame;
m_perfmon_frame.Dump(GetDrawDumpPath("%05d_f%05lld_frame_stats.txt", s_n, g_perfmon.GetFrame()), GSIsHardwareRenderer());
m_perfmon_frame = g_perfmon;
}
}
const int fb_sprite_blits = g_perfmon.GetDisplayFramebufferSpriteBlits();

View File

@ -991,6 +991,8 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBoolEx(SaveAlpha, "SaveAlpha");
SettingsWrapBitBoolEx(SaveInfo, "SaveInfo");
SettingsWrapBitBoolEx(SaveTransferImages, "SaveTransferImages");
SettingsWrapBitBoolEx(SaveDrawStats, "SaveDrawStats");
SettingsWrapBitBoolEx(SaveFrameStats, "SaveFrameStats");
SettingsWrapBitBool(DumpReplaceableTextures);
SettingsWrapBitBool(DumpReplaceableMipmaps);
SettingsWrapBitBool(DumpTexturesWithFMVActive);