From 24090772cd5855d8899f86e09783addfa75e6c9c Mon Sep 17 00:00:00 2001 From: TJnotJT Date: Mon, 22 Dec 2025 19:38:53 -0500 Subject: [PATCH] GS/HW: Add option for dumping HW config data. Dumps HW config per draw. Helps debugging/optimizing HW pipeline changes. --- pcsx2-gsrunner/Main.cpp | 6 +- pcsx2-qt/Settings/DebugGSSettingsTab.ui | 7 + pcsx2-qt/Settings/DebugSettingsWidget.cpp | 2 + pcsx2/Config.h | 1 + pcsx2/GS/Renderers/Common/GSDevice.cpp | 454 ++++++++++++++++++++++ pcsx2/GS/Renderers/Common/GSDevice.h | 41 ++ pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 17 +- pcsx2/Pcsx2Config.cpp | 1 + 8 files changed, 521 insertions(+), 8 deletions(-) diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 410583a4fe..117b82056e 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -436,8 +436,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 : Frame dump directory (will be dumped as filename_frameN.png).\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, " -dump [rt|tex|z|f|a|i|tr|ds|fs|hw]: Enabling dumping of render target, texture, z buffer, frame, " + "alphas, and info (context, vertices, list of transfers), transfers images, draw stats, frame stats, HW config, 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"); @@ -527,6 +527,8 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa s_settings_interface.SetBoolValue("EmuCore/GS", "SaveDrawStats", true); if (str.find("fs") != std::string::npos) s_settings_interface.SetBoolValue("EmuCore/GS", "SaveFrameStats", true); + if (str.find("hw") != std::string::npos) + s_settings_interface.SetBoolValue("EmuCore/GS", "SaveHWConfig", true); continue; } else if (CHECK_ARG_PARAM("-dumprange")) diff --git a/pcsx2-qt/Settings/DebugGSSettingsTab.ui b/pcsx2-qt/Settings/DebugGSSettingsTab.ui index f3e02687d9..404cdec19d 100644 --- a/pcsx2-qt/Settings/DebugGSSettingsTab.ui +++ b/pcsx2-qt/Settings/DebugGSSettingsTab.ui @@ -92,6 +92,13 @@ + + + + Save HW Config + + + diff --git a/pcsx2-qt/Settings/DebugSettingsWidget.cpp b/pcsx2-qt/Settings/DebugSettingsWidget.cpp index 65ba232f7d..614947c9ac 100644 --- a/pcsx2-qt/Settings/DebugSettingsWidget.cpp +++ b/pcsx2-qt/Settings/DebugSettingsWidget.cpp @@ -112,6 +112,7 @@ DebugSettingsWidget::DebugSettingsWidget(SettingsWindow* settings_dialog, QWidge 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::BindWidgetToBoolSetting(sif, m_gs.saveHWConfig, "EmuCore/GS", "SaveHWConfig", 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); @@ -219,6 +220,7 @@ void DebugSettingsWidget::onDrawDumpingChanged() m_gs.saveTransferImages->setEnabled(enabled); m_gs.saveDrawStats->setEnabled(enabled); m_gs.saveFrameStats->setEnabled(enabled); + m_gs.saveHWConfig->setEnabled(enabled); m_gs.saveDrawStart->setEnabled(enabled); m_gs.saveDrawCount->setEnabled(enabled); m_gs.saveFrameStart->setEnabled(enabled); diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 3949532465..755db54cbe 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -788,6 +788,7 @@ struct Pcsx2Config SaveTransferImages : 1, SaveDrawStats : 1, SaveFrameStats : 1, + SaveHWConfig : 1, DumpReplaceableTextures : 1, DumpReplaceableMipmaps : 1, DumpTexturesWithFMVActive : 1, diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index a894002432..5ceff85185 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -4,6 +4,7 @@ #include "GS/Renderers/Common/GSDevice.h" #include "GS/GSGL.h" #include "GS/GS.h" +#include "GS/GSUtil.h" #include "Host.h" #include "common/Console.h" @@ -18,6 +19,8 @@ #include "imgui.h" #include +#include +#include int SetDATMShader(SetDATM datm) { @@ -1054,6 +1057,457 @@ bool GSHWDrawConfig::BlendState::IsEffective(ColorMaskSelector colormask) const ((colormask.key & 8u) && (src_factor_alpha != GSDevice::CONST_ONE || dst_factor_alpha != GSDevice::CONST_ZERO))); } +std::string GSHWDrawConfig::GetTopologyName(u32 topology) +{ + switch (static_cast(topology)) + { + case GSHWDrawConfig::Topology::Point: return "Point"; + case GSHWDrawConfig::Topology::Line: return "Line"; + case GSHWDrawConfig::Topology::Triangle: return "Triangle"; + default: return std::to_string(topology); + } +} + +std::string GSHWDrawConfig::GetVSExpandName(u32 vsexpand) +{ + switch (static_cast(vsexpand)) + { + case GSHWDrawConfig::VSExpand::None: return "None"; + case GSHWDrawConfig::VSExpand::Point: return "Point"; + case GSHWDrawConfig::VSExpand::Line: return "Line"; + case GSHWDrawConfig::VSExpand::Sprite: return "Sprite"; + default: return std::to_string(vsexpand); + } +} + +std::string GSHWDrawConfig::GetPSDateName(u32 date) +{ + switch (date) + { + case 0: return "None"; + case 1: return "0_pass_PrimID_init"; + case 2: return "1_pass_PrimID_init"; + case 3: return "PrimID_main"; + case 5: return "0_pass_main"; + case 6: return "1_pass_main"; + default: return std::to_string(date); + } +} + +std::string GSHWDrawConfig::GetPSAlphaTestName(u32 atst) +{ + switch (static_cast(atst)) + { + case GSHWDrawConfig::PSAlphaTest::PS_ATST_NONE: return "NONE"; + case GSHWDrawConfig::PSAlphaTest::PS_ATST_LEQUAL: return "LEQUAL"; + case GSHWDrawConfig::PSAlphaTest::PS_ATST_GEQUAL: return "GEQUAL"; + case GSHWDrawConfig::PSAlphaTest::PS_ATST_EQUAL: return "EQUAL"; + case GSHWDrawConfig::PSAlphaTest::PS_ATST_NOTEQUAL: return "NOTEQUAL"; + default: return std::to_string(atst); + }; +} + +std::string GSHWDrawConfig::GetPSDstFmtName(u32 dstfmt) +{ + switch (dstfmt) + { + case 0: return "32bit"; + case 1: return "24bit"; + case 2: return "16bit"; + default: return std::to_string(dstfmt); + } +} + +std::string GSHWDrawConfig::GetPSDepthFmtName(u32 depthfmt) +{ + switch (depthfmt) + { + case 0: return "None"; + case 1: return "32bit"; + case 2: return "16bit"; + case 3: return "RGBA"; + default: return std::to_string(depthfmt); + } +} + +std::string GSHWDrawConfig::GetPSBlendABDName(u32 abd) +{ + switch (abd) + { + case 0: return "Cs"; + case 1: return "Cd"; + case 2: return "Zero"; + default: return std::to_string(abd); + } +} + +std::string GSHWDrawConfig::GetPSBlendCName(u32 c) +{ + switch (c) + { + case 0: return "As"; + case 1: return "Ad"; + case 2: return "Af"; + default: return std::to_string(c); + } +} + +std::string GSHWDrawConfig::GetPSBlendHWName(u32 blendhw) +{ + switch (static_cast(blendhw)) + { + case HWBlendType::SRC_ONE_DST_FACTOR: return "SRC_ONE_DST_FACTOR"; + case HWBlendType::SRC_ALPHA_DST_FACTOR: return "SRC_ALPHA_DST_FACTOR"; + case HWBlendType::SRC_DOUBLE: return "SRC_DOUBLE"; + case HWBlendType::SRC_HALF_ONE_DST_FACTOR: return "SRC_HALF_ONE_DST_FACTOR"; + case HWBlendType::SRC_INV_DST_BLEND_HALF: return "SRC_INV_DST_BLEND_HALF"; + case HWBlendType::INV_SRC_DST_BLEND_HALF: return "INV_SRC_DST_BLEND_HALF"; + default: return std::to_string(blendhw); + } +} + +std::string GSHWDrawConfig::GetPSBlendMixName(u32 blendmix) +{ + switch (static_cast(blendmix)) + { + case HWBlendType::BMIX1_ALPHA_HIGH_ONE: return "BMIX1_ALPHA_HIGH_ONE"; + case HWBlendType::BMIX1_SRC_HALF: return "BMIX1_SRC_HALF"; + case HWBlendType::BMIX2_OVERFLOW: return "BMIX2_OVERFLOW"; + default: return std::to_string(blendmix); + } +} + +std::string GSHWDrawConfig::GetPSChannelName(u32 channel) +{ + switch (channel) + { + case 0: return "None"; + case 1: return "FetchRed"; + case 2: return "FetchGreen"; + case 3: return "FetchBlue"; + case 4: return "FetchAlpha"; + case 5: return "FetchRGB"; + case 6: return "FetchGXBY"; + default: return std::to_string(channel); + } +} + +std::string GSHWDrawConfig::GetPSDitherName(u32 dither) +{ + switch (dither) + { + case 0: return "None"; + case 1: return "Standard"; + case 2: return "ReciprocalScaled"; + default: return std::to_string(dither); + } +} + +std::string GSHWDrawConfig::GetSSTrilnName(u32 triln) +{ + switch (static_cast(triln)) + { + case GS_MIN_FILTER::Nearest: return "Nearest"; + case GS_MIN_FILTER::Linear: return "Linear"; + case GS_MIN_FILTER::Nearest_Mipmap_Nearest: return "Nearest_Mipmap_Nearest"; + case GS_MIN_FILTER::Nearest_Mipmap_Linear: return "Nearest_Mipmap_Linear"; + case GS_MIN_FILTER::Linear_Mipmap_Nearest: return "Linear_Mipmap_Nearest"; + case GS_MIN_FILTER::Linear_Mipmap_Linear: return "Linear_Mipmap_Linear"; + default: return std::to_string(triln); + } +} + +std::string GSHWDrawConfig::GetBlendOpName(u32 blendop) +{ + switch (blendop) + { + case 0: return "ADD"; + case 1: return "SUBTRACT"; + case 2: return "REVERSE_SUBTRACT"; + default: return std::to_string(blendop); + } +} + +std::string GSHWDrawConfig::GetBlendFactorName(u32 blendfactor) +{ + switch (blendfactor) + { + case 0: return "SRC_COLOR"; + case 1: return "ONE_MINUS_SRC_COLOR"; + case 2: return "DST_COLOR"; + case 3: return "ONE_MINUS_DST_COLOR"; + case 4: return "SRC1_COLOR"; + case 5: return "ONE_MINUS_SRC1_COLOR"; + case 6: return "SRC_ALPHA"; + case 7: return "ONE_MINUS_SRC_ALPHA"; + case 8: return "DST_ALPHA"; + case 9: return "ONE_MINUS_DST_ALPHA"; + case 10: return "SRC1_ALPHA"; + case 11: return "ONE_MINUS_SRC1_ALPHA"; + case 12: return "CONSTANT_COLOR"; + case 13: return "ONE_MINUS_CONSTANT_COLOR"; + case 14: return "ONE"; + case 15: return "ZERO"; + default: return std::to_string(blendfactor); + } +} + +std::string GSHWDrawConfig::GetDestinationAlphaModeName(u32 datm) +{ + switch (static_cast(datm)) + { + case GSHWDrawConfig::DestinationAlphaMode::Off: return "Off"; + case GSHWDrawConfig::DestinationAlphaMode::Stencil: return "Stencil"; + case GSHWDrawConfig::DestinationAlphaMode::StencilOne: return "StencilOne"; + case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking: return "PrimIDTracking"; + case GSHWDrawConfig::DestinationAlphaMode::Full: return "Full"; + default: return std::to_string(datm); + } +} + +std::string GSHWDrawConfig::GetColClipModeName(u32 ccmode) +{ + switch (static_cast(ccmode)) + { + case GSHWDrawConfig::ColClipMode::NoModify: return "NoModify"; + case GSHWDrawConfig::ColClipMode::ConvertOnly: return "ConvertOnly"; + case GSHWDrawConfig::ColClipMode::ResolveOnly: return "ResolveOnly"; + case GSHWDrawConfig::ColClipMode::ConvertAndResolve: return "ConvertAndResolve"; + case GSHWDrawConfig::ColClipMode::EarlyResolve: return "EarlyResolve"; + default: return std::to_string(ccmode); + } +} + +std::string GSHWDrawConfig::GetSetDATMName(u32 datm) +{ + switch (static_cast(datm)) + { + case SetDATM::DATM0: return "DATM0"; + case SetDATM::DATM1: return "DATM1"; + case SetDATM::DATM0_RTA_CORRECTION: return "DATM0_RTA_CORRECTION"; + case SetDATM::DATM1_RTA_CORRECTION: return "DATM1_RTA_CORRECTION"; + default: return std::to_string(datm); + }; +} + +static constexpr const char* INDENT = " "; + +void GSHWDrawConfig::DumpPSSelector(std::ostream& out, const PSSelector& ps, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "aem_fmt: " << static_cast(ps.aem_fmt) << std::endl; + out << indent << "pal_fmt: " << static_cast(ps.pal_fmt) << std::endl; + out << indent << "dst_fmt: " << static_cast(ps.dst_fmt) << " # " << GetPSDstFmtName(static_cast(ps.dst_fmt)) << std::endl; + out << indent << "depth_fmt: " << static_cast(ps.depth_fmt) << " # " << GetPSDepthFmtName(static_cast(ps.depth_fmt)) << std::endl; + out << indent << "aem: " << static_cast(ps.aem) << std::endl; + out << indent << "fba: " << static_cast(ps.fba) << std::endl; + out << indent << "fog: " << static_cast(ps.fog) << std::endl; + out << indent << "iip: " << static_cast(ps.iip) << std::endl; + out << indent << "date: " << static_cast(ps.date) << " # " << GetPSDateName(static_cast(ps.date)) << std::endl; + out << indent << "atst: " << static_cast(ps.atst) << " # " << GetPSAlphaTestName(static_cast(ps.atst)) << std::endl; + out << indent << "afail: " << static_cast(ps.afail) << " # " << GSUtil::GetAFAILName(static_cast(ps.afail)) << std::endl; + out << indent << "fst: " << static_cast(ps.fst) << std::endl; + out << indent << "tfx: " << static_cast(ps.tfx) << std::endl; + out << indent << "tcc: " << GSUtil::GetTCCName(static_cast(ps.tcc)) << std::endl; + out << indent << "wms: " << static_cast(ps.tcc) << " # " << static_cast(ps.wms) << std::endl; + out << indent << "wmt: " << static_cast(ps.wmt) << std::endl; + out << indent << "adjs: " << static_cast(ps.adjs) << std::endl; + out << indent << "adjt: " << static_cast(ps.adjt) << std::endl; + out << indent << "ltf: " << static_cast(ps.ltf) << std::endl; + out << indent << "shuffle: " << static_cast(ps.shuffle) << std::endl; + out << indent << "shuffle_same: " << static_cast(ps.shuffle_same) << std::endl; + out << indent << "real16src: " << static_cast(ps.real16src) << std::endl; + out << indent << "process_ba: " << static_cast(ps.process_ba) << std::endl; + out << indent << "process_rg: " << static_cast(ps.process_rg) << std::endl; + out << indent << "shuffle_across: " << static_cast(ps.shuffle_across) << std::endl; + out << indent << "write_rg: " << static_cast(ps.write_rg) << std::endl; + out << indent << "fbmask: " << static_cast(ps.fbmask) << std::endl; + out << indent << "blend_a: " << static_cast(ps.blend_a) << " # " << GetPSBlendABDName(static_cast(ps.blend_a)) << std::endl; + out << indent << "blend_b: " << static_cast(ps.blend_b) << " # " << GetPSBlendABDName(static_cast(ps.blend_b)) << std::endl; + out << indent << "blend_c: " << static_cast(ps.blend_c) << " # " << GetPSBlendCName(static_cast(ps.blend_c)) << std::endl; + out << indent << "blend_d: " << static_cast(ps.blend_d) << " # " << GetPSBlendABDName(static_cast(ps.blend_d)) << std::endl; + out << indent << "fixed_one_a: " << static_cast(ps.fixed_one_a) << std::endl; + out << indent << "blend_hw: " << static_cast(ps.blend_hw) << " # " << GetPSBlendHWName(static_cast(ps.blend_hw)) << std::endl; + out << indent << "a_masked: " << static_cast(ps.a_masked) << std::endl; + out << indent << "colclip_hw: " << static_cast(ps.colclip_hw) << std::endl; + out << indent << "rta_correction: " << static_cast(ps.rta_correction) << std::endl; + out << indent << "rta_source_correction: " << static_cast(ps.rta_source_correction) << std::endl; + out << indent << "colclip: " << static_cast(ps.colclip) << std::endl; + out << indent << "blend_mix: " << static_cast(ps.blend_mix) << " # " << GetPSBlendMixName(static_cast(ps.blend_mix)) << std::endl; + out << indent << "round_inv: " << static_cast(ps.round_inv) << std::endl; + out << indent << "pabe: " << static_cast(ps.pabe) << std::endl; + out << indent << "no_color: " << static_cast(ps.no_color) << std::endl; + out << indent << "no_color1: " << static_cast(ps.no_color1) << std::endl; + out << indent << "channel: " << static_cast(ps.channel) << " # " << GetPSChannelName(static_cast(ps.channel)) << std::endl; + out << indent << "channel_fb: " << static_cast(ps.channel_fb) << std::endl; + out << indent << "dither: " << static_cast(ps.dither) << " # " << GetPSDitherName(static_cast(ps.dither)) << std::endl; + out << indent << "dither_adjust: " << static_cast(ps.dither_adjust) << std::endl; + out << indent << "zclamp: " << static_cast(ps.zclamp) << std::endl; + out << indent << "tcoffsethack: " << static_cast(ps.tcoffsethack) << std::endl; + out << indent << "urban_chaos_hle: " << static_cast(ps.urban_chaos_hle) << std::endl; + out << indent << "tales_of_abyss_hle: " << static_cast(ps.tales_of_abyss_hle) << std::endl; + out << indent << "tex_is_fb: " << static_cast(ps.tex_is_fb) << std::endl; + out << indent << "automatic_lod: " << static_cast(ps.automatic_lod) << std::endl; + out << indent << "manual_lod: " << static_cast(ps.manual_lod) << std::endl; + out << indent << "point_sampler: " << static_cast(ps.point_sampler) << std::endl; + out << indent << "region_rect: " << static_cast(ps.region_rect) << std::endl; + out << indent << "scanmsk: " << static_cast(ps.scanmsk) << " # " << GSUtil::GetSCANMSKName(static_cast(ps.scanmsk)) << std::endl; +} + +void GSHWDrawConfig::DumpVSSelector(std::ostream& out, const VSSelector& vs, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "fst: " << static_cast(vs.fst) << std::endl; + out << indent << "tme: " << static_cast(vs.tme) << std::endl; + out << indent << "iip: " << static_cast(vs.iip) << std::endl; + out << indent << "point_size: " << static_cast(vs.point_size) << std::endl; + out << indent << "expand: " << static_cast(vs.expand) << " # " << GetVSExpandName(static_cast(vs.expand)) << std::endl; +} + +void GSHWDrawConfig::DumpBlendState(std::ostream& out, const BlendState& bs, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "enable: " << static_cast(bs.enable) << std::endl; + out << indent << "constant_enable: " << static_cast(bs.constant_enable) << std::endl; + out << indent << "op: " << static_cast(bs.op) << " # " << GetBlendOpName(static_cast(bs.op)) << std::endl; + out << indent << "src_factor: " << static_cast(bs.src_factor) << " # " << GetBlendFactorName(static_cast(bs.src_factor)) << std::endl; + out << indent << "dst_factor: " << static_cast(bs.dst_factor) << " # " << GetBlendFactorName(static_cast(bs.dst_factor)) << std::endl; + out << indent << "src_factor_alpha: " << static_cast(bs.src_factor_alpha) << " # " << GetBlendFactorName(static_cast(bs.src_factor_alpha)) << std::endl; + out << indent << "dst_factor_alpha: " << static_cast(bs.dst_factor_alpha) << " # " << GetBlendFactorName(static_cast(bs.dst_factor_alpha)) << std::endl; + out << indent << "constant: " << static_cast(bs.constant) << std::endl; +} + +void GSHWDrawConfig::DumpDepthStencilSelctor(std::ostream& out, const DepthStencilSelector& dss, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "ztst: " << static_cast(dss.ztst) << " # " << GSUtil::GetZTSTName(static_cast(dss.ztst)) << std::endl; + out << indent << "zwe: " << static_cast(dss.zwe) << std::endl; + out << indent << "date: " << static_cast(dss.date) << std::endl; + out << indent << "date_one: " << static_cast(dss.date_one) << std::endl; +} + +void GSHWDrawConfig::DumpSamplerSelector(std::ostream& out, const SamplerSelector& ss, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "tau: " << static_cast(ss.tau) << std::endl; + out << indent << "tav: " << static_cast(ss.tav) << std::endl; + out << indent << "biln: " << static_cast(ss.biln) << std::endl; + out << indent << "triln: " << static_cast(ss.triln) << " # " << GetSSTrilnName(static_cast(ss.triln)) << std::endl; + out << indent << "aniso: " << static_cast(ss.aniso) << std::endl; + out << indent << "lodclamp: " << static_cast(ss.lodclamp) << std::endl; +} + +void GSHWDrawConfig::DumpAlphaPass(std::ostream& out, const AlphaPass& ap, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "enable: " << static_cast(ap.enable) << std::endl; + out << indent << "require_one_barrier: " << static_cast(ap.require_one_barrier) << std::endl; + out << indent << "require_full_barrier: " << static_cast(ap.require_full_barrier) << std::endl; + out << indent << "colormask: " << std::showbase << std::hex << static_cast(ap.colormask.wrgba) << std::dec << std::endl; + out << indent << "ps_aref: " << static_cast(ap.ps_aref) << std::endl; + + out << indent << "ps:" << std::endl; + DumpPSSelector(out, ap.ps, indent + INDENT); + + out << indent << "dss:" << std::endl; + DumpDepthStencilSelctor(out, ap.depth, indent + INDENT); +} + +void GSHWDrawConfig::DumpBlendMultipass(std::ostream& out, const BlendMultiPass& bmp, const std::string& indent) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << indent << "enable: " << static_cast(bmp.enable) << std::endl; + out << indent << "no_color1: " << static_cast(bmp.no_color1) << std::endl; + out << indent << "blend_hw: " << static_cast(bmp.blend_hw) << " # " << GetPSBlendHWName(static_cast(bmp.blend_hw)) << std::endl; + out << indent << "dither: " << static_cast(bmp.dither) << std::endl; + + out << indent << "blend:" << std::endl; + DumpBlendState(out, bmp.blend, indent + INDENT); +} + +void GSHWDrawConfig::DumpConfig(std::ostream& out, const GSHWDrawConfig& conf, + bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp) +{ + out.imbue(std::locale::classic()); // Disable integer separators + out << std::dec; + + out << "topology: " << static_cast(conf.topology) << " # " << GetTopologyName(static_cast(conf.topology)) << std::endl; + out << "require_one_barrier: " << static_cast(conf.require_one_barrier) << std::endl; + out << "require_full_barrier: " << static_cast(conf.require_full_barrier) << std::endl; + + out << "destination_alpha: " << static_cast(conf.destination_alpha) << " # " << GetDestinationAlphaModeName(static_cast(conf.destination_alpha)) << std::endl; + out << "datm: " << static_cast(conf.datm) << " # " << GetSetDATMName(static_cast(conf.datm)) << std::endl; + out << "line_expand: " << conf.line_expand << std::endl; + out << "colormask: " << std::hex << std::showbase << static_cast(conf.colormask.wrgba) << std::dec << std::endl; + + if (ps) + { + out << "ps:" << std::endl; + DumpPSSelector(out, conf.ps, INDENT); + } + + if (vs) + { + out << "vs:" << std::endl; + DumpVSSelector(out, conf.vs, INDENT); + } + + if (bs) + { + out << "blend:" << std::endl; + DumpBlendState(out, conf.blend, INDENT); + } + + if (ss) + { + out << "sampler:" << std::endl; + DumpSamplerSelector(out, conf.sampler, INDENT); + } + + if (dss) + { + out << "depth:" << std::endl; + DumpDepthStencilSelctor(out, conf.depth, INDENT); + } + + if (asp) + { + out << "alpha_second_pass:" << std::endl; + DumpAlphaPass(out, conf.alpha_second_pass, INDENT); + } + + if (bmp) + { + out << "blend_multi_pass:" << std::endl; + DumpBlendMultipass(out, conf.blend_multi_pass, INDENT); + } +} + +void GSHWDrawConfig::DumpConfig(const std::string& fn, const GSHWDrawConfig& conf, + bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp) +{ + std::ofstream file(fn); + + if (!file.is_open()) + return; + + DumpConfig(file, conf, ps, vs, bs, dss, ss, asp, bmp); +} + // clang-format off // Maps PS2 blend modes to our best approximation of them with PC hardware diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 6ca3e6aff3..3afa7b31d1 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -328,6 +328,16 @@ struct alignas(16) GSHWDrawConfig }; static_assert(sizeof(VSSelector) == 1, "VSSelector is a single byte"); #pragma pack(pop) + + enum PSAlphaTest + { + PS_ATST_NONE = 0, + PS_ATST_LEQUAL = 1, + PS_ATST_GEQUAL = 2, + PS_ATST_EQUAL = 3, + PS_ATST_NOTEQUAL = 4 + }; + #pragma pack(push, 4) struct PSSelector { @@ -780,6 +790,37 @@ struct alignas(16) GSHWDrawConfig ColClipMode colclip_mode; GIFRegFRAME colclip_frame; GSVector4i colclip_update_area; ///< Area in the framebuffer which colclip will modify; + + // Dumping + static std::string GetTopologyName(u32 topology); + static std::string GetVSExpandName(u32 vsexpand); + static std::string GetPSAlphaTestName(u32 dstfmt); + static std::string GetPSDstFmtName(u32 dstfmt); + static std::string GetPSDepthFmtName(u32 depthfmt); + static std::string GetPSBlendABDName(u32 abd); + static std::string GetPSBlendCName(u32 c); + static std::string GetPSBlendHWName(u32 blendhw); + static std::string GetPSBlendMixName(u32 blendmix); + static std::string GetPSDitherName(u32 dither); + static std::string GetPSChannelName(u32 channel); + static std::string GetSSTrilnName(u32 triln); + static std::string GetBlendOpName(u32 blendop); + static std::string GetBlendFactorName(u32 blendfactor); + static std::string GetDestinationAlphaModeName(u32 datm); + static std::string GetPSDateName(u32 date); + static std::string GetColClipModeName(u32 ccmode); + static std::string GetSetDATMName(u32 setdatm); + static void DumpPSSelector(std::ostream& out, const PSSelector& ps, const std::string& indent = ""); + static void DumpVSSelector(std::ostream& out, const VSSelector& vs, const std::string& indent = ""); + static void DumpBlendState(std::ostream& out, const BlendState& bs, const std::string& indent = ""); + static void DumpDepthStencilSelctor(std::ostream& out, const DepthStencilSelector& ds, const std::string& indent = ""); + static void DumpSamplerSelector(std::ostream& out, const SamplerSelector& ss, const std::string& indent = ""); + static void DumpAlphaPass(std::ostream& out, const AlphaPass& ap, const std::string& indent = ""); + static void DumpBlendMultipass(std::ostream& out, const BlendMultiPass& bmp, const std::string& indent = ""); + static void DumpConfig(std::ostream& out, const GSHWDrawConfig& conf, + bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true); + static void DumpConfig(const std::string& fn, const GSHWDrawConfig& conf, + bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true); }; static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand) diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 80a8fdf6d9..77fabac12e 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -7280,27 +7280,27 @@ void GSRendererHW::EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool { case ATST_LESS: AREF = aref - 0.1f; - ps.atst = 1; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_LEQUAL; break; case ATST_LEQUAL: AREF = aref - 0.1f + 1.0f; - ps.atst = 1; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_LEQUAL; break; case ATST_GEQUAL: AREF = aref - 0.1f; - ps.atst = 2; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_GEQUAL; break; case ATST_GREATER: AREF = aref - 0.1f + 1.0f; - ps.atst = 2; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_GEQUAL; break; case ATST_EQUAL: AREF = aref; - ps.atst = 3; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_EQUAL; break; case ATST_NOTEQUAL: AREF = aref; - ps.atst = 4; + ps.atst = GSHWDrawConfig::PSAlphaTest::PS_ATST_NOTEQUAL; break; case ATST_NEVER: // Draw won't be done so no need to implement it in shader case ATST_ALWAYS: @@ -8209,6 +8209,11 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta m_conf.alpha_second_pass.enable = false; } + if (GSConfig.SaveHWConfig && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame())) + { + GSHWDrawConfig::DumpConfig(GetDrawDumpPath("%05d_hwconfig.txt", s_n), m_conf); + } + if (!m_channel_shuffle_width) g_gs_device->RenderHW(m_conf); else diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 8f6151ceba..6f0064a24e 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -999,6 +999,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) SettingsWrapBitBoolEx(SaveTransferImages, "SaveTransferImages"); SettingsWrapBitBoolEx(SaveDrawStats, "SaveDrawStats"); SettingsWrapBitBoolEx(SaveFrameStats, "SaveFrameStats"); + SettingsWrapBitBoolEx(SaveHWConfig, "SaveHWConfig"); SettingsWrapBitBool(DumpReplaceableTextures); SettingsWrapBitBool(DumpReplaceableMipmaps); SettingsWrapBitBool(DumpTexturesWithFMVActive);