diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp index 8e806bef6f..4db865b069 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp +++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp @@ -23,6 +23,7 @@ #include "DolphinQt/Settings.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/CPMemory.h" #include "VideoCommon/OpcodeDecoding.h" constexpr int FRAME_ROLE = Qt::UserRole; @@ -224,10 +225,13 @@ void FIFOAnalyzer::UpdateDetails() u32 cmd2 = *objectdata++; u32 value = Common::swap32(objectdata); objectdata += 4; + const auto [name, desc] = GetCPRegInfo(cmd2, value); + ASSERT(!name.empty()); - new_label = QStringLiteral("CP %1 %2") + new_label = QStringLiteral("CP %1 %2 %3") .arg(cmd2, 2, 16, QLatin1Char('0')) - .arg(value, 8, 16, QLatin1Char('0')); + .arg(value, 8, 16, QLatin1Char('0')) + .arg(QString::fromStdString(name)); } break; @@ -484,7 +488,20 @@ void FIFOAnalyzer::UpdateDescription() } else if (*cmddata == OpcodeDecoder::GX_LOAD_CP_REG) { + const u8 cmd = *(cmddata + 1); + const u32 value = Common::swap32(cmddata + 2); + + const auto [name, desc] = GetCPRegInfo(cmd, value); + ASSERT(!name.empty()); + text = tr("CP register "); + text += QString::fromStdString(name); + text += QLatin1Char{'\n'}; + + if (desc.empty()) + text += tr("No description available"); + else + text += QString::fromStdString(desc); } else if (*cmddata == OpcodeDecoder::GX_LOAD_XF_REG) { diff --git a/Source/Core/VideoCommon/CPMemory.cpp b/Source/Core/VideoCommon/CPMemory.cpp index b4d118bbb4..eb59869368 100644 --- a/Source/Core/VideoCommon/CPMemory.cpp +++ b/Source/Core/VideoCommon/CPMemory.cpp @@ -33,3 +33,44 @@ void CopyPreprocessCPStateFromMain() { memcpy(&g_preprocess_cp_state, &g_main_cp_state, sizeof(CPState)); } + +std::pair GetCPRegInfo(u8 cmd, u32 value) +{ + switch (cmd & CP_COMMAND_MASK) + { + case MATINDEX_A: + return std::make_pair("MATINDEX_A", fmt::to_string(TMatrixIndexA{.Hex = value})); + case MATINDEX_B: + return std::make_pair("MATINDEX_B", fmt::to_string(TMatrixIndexB{.Hex = value})); + case VCD_LO: + return std::make_pair("VCD_LO", fmt::to_string(TVtxDesc::Low{.Hex = value})); + case VCD_HI: + return std::make_pair("VCD_HI", fmt::to_string(TVtxDesc::High{.Hex = value})); + case CP_VAT_REG_A: + if (cmd - CP_VAT_REG_A >= CP_NUM_VAT_REG) + return std::make_pair("CP_VAT_REG_A invalid", ""); + + return std::make_pair(fmt::format("CP_VAT_REG_A - Format {}", cmd & CP_VAT_MASK), + fmt::to_string(UVAT_group0{.Hex = value})); + case CP_VAT_REG_B: + if (cmd - CP_VAT_REG_B >= CP_NUM_VAT_REG) + return std::make_pair("CP_VAT_REG_B invalid", ""); + + return std::make_pair(fmt::format("CP_VAT_REG_B - Format {}", cmd & CP_VAT_MASK), + fmt::to_string(UVAT_group1{.Hex = value})); + case CP_VAT_REG_C: + if (cmd - CP_VAT_REG_C >= CP_NUM_VAT_REG) + return std::make_pair("CP_VAT_REG_C invalid", ""); + + return std::make_pair(fmt::format("CP_VAT_REG_C - Format {}", cmd & CP_VAT_MASK), + fmt::to_string(UVAT_group2{.Hex = value})); + case ARRAY_BASE: + return std::make_pair(fmt::format("ARRAY_BASE Array {}", cmd & CP_ARRAY_MASK), + fmt::format("Base address {:08x}", value)); + case ARRAY_STRIDE: + return std::make_pair(fmt::format("ARRAY_STRIDE Array {}", cmd - ARRAY_STRIDE), + fmt::format("Stride {:02x}", value & 0xff)); + default: + return std::make_pair(fmt::format("Invalid CP register {:02x} = {:08x}", cmd, value), ""); + } +} diff --git a/Source/Core/VideoCommon/CPMemory.h b/Source/Core/VideoCommon/CPMemory.h index 83550eb5f2..3b22992f15 100644 --- a/Source/Core/VideoCommon/CPMemory.h +++ b/Source/Core/VideoCommon/CPMemory.h @@ -4,6 +4,10 @@ #pragma once +#include +#include +#include + #include "Common/BitField.h" #include "Common/BitSet.h" #include "Common/CommonTypes.h" @@ -222,6 +226,65 @@ struct TVtxDesc high.Hex = value >> 17; } }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const TVtxDesc::Low& desc, FormatContext& ctx) + { + static constexpr std::array present = {"Not present", "Present"}; + + return format_to(ctx.out(), + "Position and normal matrix index: {}\n" + "Texture Coord 0 matrix index: {}\n" + "Texture Coord 1 matrix index: {}\n" + "Texture Coord 2 matrix index: {}\n" + "Texture Coord 3 matrix index: {}\n" + "Texture Coord 4 matrix index: {}\n" + "Texture Coord 5 matrix index: {}\n" + "Texture Coord 6 matrix index: {}\n" + "Texture Coord 7 matrix index: {}\n" + "Position: {}\n" + "Normal: {}\n" + "Color 0: {}\n" + "Color 1: {}", + present[desc.PosMatIdx], present[desc.Tex0MatIdx], present[desc.Tex1MatIdx], + present[desc.Tex2MatIdx], present[desc.Tex3MatIdx], present[desc.Tex4MatIdx], + present[desc.Tex5MatIdx], present[desc.Tex6MatIdx], present[desc.Tex7MatIdx], + desc.Position, desc.Normal, desc.Color0, desc.Color1); + } +}; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const TVtxDesc::High& desc, FormatContext& ctx) + { + return format_to(ctx.out(), + "Texture Coord 0: {}\n" + "Texture Coord 1: {}\n" + "Texture Coord 2: {}\n" + "Texture Coord 3: {}\n" + "Texture Coord 4: {}\n" + "Texture Coord 5: {}\n" + "Texture Coord 6: {}\n" + "Texture Coord 7: {}", + desc.Tex0Coord, desc.Tex1Coord, desc.Tex2Coord, desc.Tex3Coord, desc.Tex4Coord, + desc.Tex5Coord, desc.Tex6Coord, desc.Tex7Coord); + } +}; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const TVtxDesc& desc, FormatContext& ctx) + { + return format_to(ctx.out(), "{}\n{}", desc.low, desc.high); + } +}; union UVAT_group0 { @@ -247,6 +310,40 @@ union UVAT_group0 BitField<30, 1, u32> ByteDequant; BitField<31, 1, u32> NormalIndex3; }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const UVAT_group0& g0, FormatContext& ctx) + { + static constexpr std::array byte_dequant = { + "shift does not apply to u8/s8 components", "shift applies to u8/s8 components"}; + static constexpr std::array normalindex3 = {"single index per normal", + "triple-index per nine-normal"}; + + return format_to(ctx.out(), + "Position elements: {}\n" + "Position format: {}\n" + "Position shift: {} ({})\n" + "Normal elements: {}\n" + "Normal format: {}\n" + "Color 0 elements: {}\n" + "Color 0 format: {}\n" + "Color 1 elements: {}\n" + "Color 1 format: {}\n" + "Texture coord 0 elements: {}\n" + "Texture coord 0 format: {}\n" + "Texture coord 0 shift: {} ({})\n" + "Byte dequant: {}\n" + "Normal index 3: {}", + g0.PosElements, g0.PosFormat, g0.PosFrac, 1.f / (1 << g0.PosFrac), + g0.NormalElements, g0.NormalFormat, g0.Color0Elements, g0.Color0Comp, + g0.Color1Elements, g0.Color1Comp, g0.Tex0CoordElements, g0.Tex0CoordFormat, + g0.Tex0Frac, 1.f / (1 << g0.Tex0Frac), byte_dequant[g0.ByteDequant], + normalindex3[g0.NormalIndex3]); + } +}; union UVAT_group1 { @@ -269,6 +366,33 @@ union UVAT_group1 // 31 BitField<31, 1, u32> VCacheEnhance; }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const UVAT_group1& g1, FormatContext& ctx) + { + return format_to(ctx.out(), + "Texture coord 1 elements: {}\n" + "Texture coord 1 format: {}\n" + "Texture coord 1 shift: {} ({})\n" + "Texture coord 2 elements: {}\n" + "Texture coord 2 format: {}\n" + "Texture coord 2 shift: {} ({})\n" + "Texture coord 3 elements: {}\n" + "Texture coord 3 format: {}\n" + "Texture coord 3 shift: {} ({})\n" + "Texture coord 4 elements: {}\n" + "Texture coord 4 format: {}\n" + "Enhance VCache (must always be on): {}", + g1.Tex1CoordElements, g1.Tex1CoordFormat, g1.Tex1Frac, + 1.f / (1 << g1.Tex1Frac), g1.Tex2CoordElements, g1.Tex2CoordFormat, + g1.Tex2Frac, 1.f / (1 << g1.Tex2Frac), g1.Tex3CoordElements, + g1.Tex3CoordFormat, g1.Tex3Frac, 1.f / (1 << g1.Tex3Frac), + g1.Tex4CoordElements, g1.Tex4CoordFormat, g1.VCacheEnhance ? "Yes" : "No"); + } +}; union UVAT_group2 { @@ -288,6 +412,31 @@ union UVAT_group2 BitField<24, 3, ComponentFormat> Tex7CoordFormat; BitField<27, 5, u32> Tex7Frac; }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const UVAT_group2& g2, FormatContext& ctx) + { + return format_to(ctx.out(), + "Texture coord 4 shift: {} ({})\n" + "Texture coord 5 elements: {}\n" + "Texture coord 5 format: {}\n" + "Texture coord 5 shift: {} ({})\n" + "Texture coord 6 elements: {}\n" + "Texture coord 6 format: {}\n" + "Texture coord 6 shift: {} ({})\n" + "Texture coord 7 elements: {}\n" + "Texture coord 7 format: {}\n" + "Texture coord 7 shift: {} ({})", + g2.Tex4Frac, 1.f / (1 << g2.Tex4Frac), g2.Tex5CoordElements, + g2.Tex5CoordFormat, g2.Tex5Frac, 1.f / (1 << g2.Tex5Frac), + g2.Tex6CoordElements, g2.Tex6CoordFormat, g2.Tex6Frac, + 1.f / (1 << g2.Tex6Frac), g2.Tex7CoordElements, g2.Tex7CoordFormat, + g2.Tex7Frac, 1.f / (1 << g2.Tex7Frac)); + } +}; struct ColorAttr { @@ -325,6 +474,17 @@ union TMatrixIndexA BitField<24, 6, u32> Tex3MtxIdx; u32 Hex; }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const TMatrixIndexA& m, FormatContext& ctx) + { + return format_to(ctx.out(), "PosNormal: {}\nTex0: {}\nTex1: {}\nTex2: {}\nTex3: {}", + m.PosNormalMtxIdx, m.Tex0MtxIdx, m.Tex1MtxIdx, m.Tex2MtxIdx, m.Tex3MtxIdx); + } +}; union TMatrixIndexB { @@ -334,6 +494,17 @@ union TMatrixIndexB BitField<18, 6, u32> Tex7MtxIdx; u32 Hex; }; +template <> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const TMatrixIndexB& m, FormatContext& ctx) + { + return format_to(ctx.out(), "Tex4: {}\nTex5: {}\nTex6: {}\nTex7: {}", m.Tex4MtxIdx, + m.Tex5MtxIdx, m.Tex6MtxIdx, m.Tex7MtxIdx); + } +}; struct VAT { @@ -376,3 +547,5 @@ void FillCPMemoryArray(u32* memory); void DoCPState(PointerWrap& p); void CopyPreprocessCPStateFromMain(); + +std::pair GetCPRegInfo(u8 cmd, u32 value);