summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorPokechu22 <Pokechu022@gmail.com>2021-04-22 20:57:56 -0700
committerPokechu22 <Pokechu022@gmail.com>2021-12-18 15:21:36 -0800
commitb5fd35f95145ecc8f88a179229ed69b390eb76be (patch)
tree7b6700d35c609ba6a43fcdf55a12d6756e9a206b /Source/Core/VideoCommon
parent04418262063bf6034bc2be172a64eb99a9e9a2bb (diff)
Refactor OpcodeDecoding and FIFO analyzer to use callbacks
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/BPMemory.h4
-rw-r--r--Source/Core/VideoCommon/BPStructs.cpp22
-rw-r--r--Source/Core/VideoCommon/CPMemory.cpp169
-rw-r--r--Source/Core/VideoCommon/CPMemory.h18
-rw-r--r--Source/Core/VideoCommon/CommandProcessor.cpp7
-rw-r--r--Source/Core/VideoCommon/CommandProcessor.h2
-rw-r--r--Source/Core/VideoCommon/Fifo.cpp14
-rw-r--r--Source/Core/VideoCommon/OpcodeDecoding.cpp364
-rw-r--r--Source/Core/VideoCommon/OpcodeDecoding.h222
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp2
-rw-r--r--Source/Core/VideoCommon/VertexLoaderARM64.cpp2
-rw-r--r--Source/Core/VideoCommon/VertexLoaderManager.cpp147
-rw-r--r--Source/Core/VideoCommon/XFMemory.h6
-rw-r--r--Source/Core/VideoCommon/XFStructs.cpp29
-rw-r--r--Source/Core/VideoCommon/XFStructs.h6
15 files changed, 602 insertions, 412 deletions
diff --git a/Source/Core/VideoCommon/BPMemory.h b/Source/Core/VideoCommon/BPMemory.h
index fdd32746a9..97dc5993bd 100644
--- a/Source/Core/VideoCommon/BPMemory.h
+++ b/Source/Core/VideoCommon/BPMemory.h
@@ -2205,7 +2205,7 @@ struct BPMemory
extern BPMemory bpmem;
-void LoadBPReg(u32 value0, int cycles_into_future);
-void LoadBPRegPreprocess(u32 value0, int cycles_into_future);
+void LoadBPReg(u8 reg, u32 value, int cycles_into_future);
+void LoadBPRegPreprocess(u8 reg, u32 value, int cycles_into_future);
std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata);
diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp
index 0fc4ca6785..503ef6154f 100644
--- a/Source/Core/VideoCommon/BPStructs.cpp
+++ b/Source/Core/VideoCommon/BPStructs.cpp
@@ -716,29 +716,27 @@ static void BPWritten(const BPCmd& bp, int cycles_into_future)
bp.newvalue);
}
-// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
-void LoadBPReg(u32 value0, int cycles_into_future)
+// Call browser: OpcodeDecoding.cpp RunCallback::OnBP()
+void LoadBPReg(u8 reg, u32 value, int cycles_into_future)
{
- int regNum = value0 >> 24;
- int oldval = ((u32*)&bpmem)[regNum];
- int newval = (oldval & ~bpmem.bpMask) | (value0 & bpmem.bpMask);
+ int oldval = ((u32*)&bpmem)[reg];
+ int newval = (oldval & ~bpmem.bpMask) | (value & bpmem.bpMask);
int changes = (oldval ^ newval) & 0xFFFFFF;
- BPCmd bp = {regNum, changes, newval};
+ BPCmd bp = {reg, changes, newval};
// Reset the mask register if we're not trying to set it ourselves.
- if (regNum != BPMEM_BP_MASK)
+ if (reg != BPMEM_BP_MASK)
bpmem.bpMask = 0xFFFFFF;
BPWritten(bp, cycles_into_future);
}
-void LoadBPRegPreprocess(u32 value0, int cycles_into_future)
+void LoadBPRegPreprocess(u8 reg, u32 value, int cycles_into_future)
{
- int regNum = value0 >> 24;
- // masking could hypothetically be a problem
- u32 newval = value0 & 0xffffff;
- switch (regNum)
+ // masking via BPMEM_BP_MASK could hypothetically be a problem
+ u32 newval = value & 0xffffff;
+ switch (reg)
{
case BPMEM_SETDRAWDONE:
if ((newval & 0xff) == 0x02)
diff --git a/Source/Core/VideoCommon/CPMemory.cpp b/Source/Core/VideoCommon/CPMemory.cpp
index afa354b4e4..4781595e8c 100644
--- a/Source/Core/VideoCommon/CPMemory.cpp
+++ b/Source/Core/VideoCommon/CPMemory.cpp
@@ -2,7 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/CPMemory.h"
+
+#include <cstring>
+
#include "Common/ChunkFile.h"
+#include "Common/Logging/Log.h"
+#include "Core/DolphinAnalytics.h"
+#include "VideoCommon/CommandProcessor.h"
// CP state
CPState g_main_cp_state;
@@ -28,7 +34,7 @@ void DoCPState(PointerWrap& p)
void CopyPreprocessCPStateFromMain()
{
- memcpy(&g_preprocess_cp_state, &g_main_cp_state, sizeof(CPState));
+ std::memcpy(&g_preprocess_cp_state, &g_main_cp_state, sizeof(CPState));
}
std::pair<std::string, std::string> GetCPRegInfo(u8 cmd, u32 value)
@@ -73,3 +79,164 @@ std::pair<std::string, std::string> GetCPRegInfo(u8 cmd, u32 value)
return std::make_pair(fmt::format("Invalid CP register {:02x} = {:08x}", cmd, value), "");
}
}
+
+CPState::CPState(const u32* memory) : CPState()
+{
+ matrix_index_a.Hex = memory[MATINDEX_A];
+ matrix_index_b.Hex = memory[MATINDEX_B];
+ vtx_desc.low.Hex = memory[VCD_LO];
+ vtx_desc.high.Hex = memory[VCD_HI];
+
+ for (u32 i = 0; i < CP_NUM_VAT_REG; i++)
+ {
+ vtx_attr[i].g0.Hex = memory[CP_VAT_REG_A + i];
+ vtx_attr[i].g1.Hex = memory[CP_VAT_REG_B + i];
+ vtx_attr[i].g2.Hex = memory[CP_VAT_REG_C + i];
+ }
+
+ for (u32 i = 0; i < CP_NUM_ARRAYS; i++)
+ {
+ array_bases[static_cast<CPArray>(i)] = memory[ARRAY_BASE + i];
+ array_strides[static_cast<CPArray>(i)] = memory[ARRAY_STRIDE + i];
+ }
+}
+
+void CPState::LoadCPReg(u8 sub_cmd, u32 value)
+{
+ switch (sub_cmd & CP_COMMAND_MASK)
+ {
+ case UNKNOWN_00:
+ case UNKNOWN_10:
+ case UNKNOWN_20:
+ if (!(sub_cmd == UNKNOWN_20 && value == 0))
+ {
+ // All titles using libogc or the official SDK issue 0x20 with value=0 on startup
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_CP_PERF_COMMAND);
+ DEBUG_LOG_FMT(VIDEO, "Unknown CP command possibly relating to perf queries used: {:02x}",
+ sub_cmd);
+ }
+ break;
+
+ case MATINDEX_A:
+ if (sub_cmd != MATINDEX_A)
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO,
+ "CP MATINDEX_A: an exact value of {:02x} was expected "
+ "but instead a value of {:02x} was seen",
+ MATINDEX_A, sub_cmd);
+ }
+
+ matrix_index_a.Hex = value;
+ break;
+
+ case MATINDEX_B:
+ if (sub_cmd != MATINDEX_B)
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO,
+ "CP MATINDEX_B: an exact value of {:02x} was expected "
+ "but instead a value of {:02x} was seen",
+ MATINDEX_B, sub_cmd);
+ }
+
+ matrix_index_b.Hex = value;
+ break;
+
+ case VCD_LO:
+ if (sub_cmd != VCD_LO) // Stricter than YAGCD
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO,
+ "CP VCD_LO: an exact value of {:02x} was expected "
+ "but instead a value of {:02x} was seen",
+ VCD_LO, sub_cmd);
+ }
+
+ vtx_desc.low.Hex = value;
+ attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
+ bases_dirty = true;
+ break;
+
+ case VCD_HI:
+ if (sub_cmd != VCD_HI) // Stricter than YAGCD
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO,
+ "CP VCD_HI: an exact value of {:02x} was expected "
+ "but instead a value of {:02x} was seen",
+ VCD_HI, sub_cmd);
+ }
+
+ vtx_desc.high.Hex = value;
+ attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
+ bases_dirty = true;
+ break;
+
+ case CP_VAT_REG_A:
+ if ((sub_cmd - CP_VAT_REG_A) >= CP_NUM_VAT_REG)
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO, "CP_VAT_REG_A: Invalid VAT {}", sub_cmd - CP_VAT_REG_A);
+ }
+ vtx_attr[sub_cmd & CP_VAT_MASK].g0.Hex = value;
+ attr_dirty[sub_cmd & CP_VAT_MASK] = true;
+ break;
+
+ case CP_VAT_REG_B:
+ if ((sub_cmd - CP_VAT_REG_B) >= CP_NUM_VAT_REG)
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO, "CP_VAT_REG_B: Invalid VAT {}", sub_cmd - CP_VAT_REG_B);
+ }
+ vtx_attr[sub_cmd & CP_VAT_MASK].g1.Hex = value;
+ attr_dirty[sub_cmd & CP_VAT_MASK] = true;
+ break;
+
+ case CP_VAT_REG_C:
+ if ((sub_cmd - CP_VAT_REG_C) >= CP_NUM_VAT_REG)
+ {
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO, "CP_VAT_REG_C: Invalid VAT {}", sub_cmd - CP_VAT_REG_C);
+ }
+ vtx_attr[sub_cmd & CP_VAT_MASK].g2.Hex = value;
+ attr_dirty[sub_cmd & CP_VAT_MASK] = true;
+ break;
+
+ // Pointers to vertex arrays in GC RAM
+ case ARRAY_BASE:
+ array_bases[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] =
+ value & CommandProcessor::GetPhysicalAddressMask();
+ bases_dirty = true;
+ break;
+
+ case ARRAY_STRIDE:
+ array_strides[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] = value & 0xFF;
+ break;
+
+ default:
+ DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_CP_COMMAND);
+ WARN_LOG_FMT(VIDEO, "Unknown CP register {:02x} set to {:08x}", sub_cmd, value);
+ }
+}
+
+void CPState::FillCPMemoryArray(u32* memory) const
+{
+ memory[MATINDEX_A] = matrix_index_a.Hex;
+ memory[MATINDEX_B] = matrix_index_b.Hex;
+ memory[VCD_LO] = vtx_desc.low.Hex;
+ memory[VCD_HI] = vtx_desc.high.Hex;
+
+ for (int i = 0; i < CP_NUM_VAT_REG; ++i)
+ {
+ memory[CP_VAT_REG_A + i] = vtx_attr[i].g0.Hex;
+ memory[CP_VAT_REG_B + i] = vtx_attr[i].g1.Hex;
+ memory[CP_VAT_REG_C + i] = vtx_attr[i].g2.Hex;
+ }
+
+ for (int i = 0; i < CP_NUM_ARRAYS; ++i)
+ {
+ memory[ARRAY_BASE + i] = array_bases[static_cast<CPArray>(i)];
+ memory[ARRAY_STRIDE + i] = array_strides[static_cast<CPArray>(i)];
+ }
+}
diff --git a/Source/Core/VideoCommon/CPMemory.h b/Source/Core/VideoCommon/CPMemory.h
index 1a937d15c9..defac506b2 100644
--- a/Source/Core/VideoCommon/CPMemory.h
+++ b/Source/Core/VideoCommon/CPMemory.h
@@ -5,6 +5,7 @@
#include <array>
#include <string>
+#include <type_traits>
#include <utility>
#include "Common/BitField.h"
@@ -630,13 +631,21 @@ class VertexLoaderBase;
// STATE_TO_SAVE
struct CPState final
{
+ CPState() = default;
+ explicit CPState(const u32* memory);
+
+ // Mutates the CP state based on the given command and value.
+ void LoadCPReg(u8 sub_cmd, u32 value);
+ // Fills memory with data from CP regs. There should be space for 0x100 values in memory.
+ void FillCPMemoryArray(u32* memory) const;
+
Common::EnumMap<u32, CPArray::XF_D> array_bases;
Common::EnumMap<u32, CPArray::XF_D> array_strides;
TMatrixIndexA matrix_index_a{};
TMatrixIndexB matrix_index_b{};
TVtxDesc vtx_desc;
// Most games only use the first VtxAttr and simply reconfigure it all the time as needed.
- VAT vtx_attr[CP_NUM_VAT_REG]{};
+ std::array<VAT, CP_NUM_VAT_REG> vtx_attr{};
// Attributes that actually belong to VertexLoaderManager:
BitSet32 attr_dirty{};
@@ -644,18 +653,13 @@ struct CPState final
VertexLoaderBase* vertex_loaders[CP_NUM_VAT_REG]{};
int last_id = 0;
};
+static_assert(std::is_trivially_copyable_v<CPState>);
class PointerWrap;
extern CPState g_main_cp_state;
extern CPState g_preprocess_cp_state;
-// Might move this into its own file later.
-void LoadCPReg(u32 SubCmd, u32 Value, bool is_preprocess = false);
-
-// Fills memory with data from CP regs
-void FillCPMemoryArray(u32* memory);
-
void DoCPState(PointerWrap& p);
void CopyPreprocessCPStateFromMain();
diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp
index 83784a137f..ddaa0e72a3 100644
--- a/Source/Core/VideoCommon/CommandProcessor.cpp
+++ b/Source/Core/VideoCommon/CommandProcessor.cpp
@@ -5,6 +5,7 @@
#include <atomic>
#include <cstring>
+#include <fmt/format.h>
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
@@ -607,10 +608,10 @@ void SetCpClearRegister()
{
}
-void HandleUnknownOpcode(u8 cmd_byte, void* buffer, bool preprocess)
+void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess)
{
// TODO(Omega): Maybe dump FIFO to file on this error
- PanicAlertFmtT("GFX FIFO: Unknown Opcode ({0:#04x} @ {1}, {2}).\n"
+ PanicAlertFmtT("GFX FIFO: Unknown Opcode ({0:#04x} @ {1}, preprocess={2}).\n"
"This means one of the following:\n"
"* The emulated GPU got desynced, disabling dual core can help\n"
"* Command stream corrupted by some spurious memory bug\n"
@@ -618,7 +619,7 @@ void HandleUnknownOpcode(u8 cmd_byte, void* buffer, bool preprocess)
"* Some other sort of bug\n\n"
"Further errors will be sent to the Video Backend log and\n"
"Dolphin will now likely crash or hang. Enjoy.",
- cmd_byte, buffer, preprocess ? "preprocess=true" : "preprocess=false");
+ cmd_byte, fmt::ptr(buffer), preprocess);
{
PanicAlertFmt("Illegal command {:02x}\n"
diff --git a/Source/Core/VideoCommon/CommandProcessor.h b/Source/Core/VideoCommon/CommandProcessor.h
index 4ca73c71d2..2da7f1c84d 100644
--- a/Source/Core/VideoCommon/CommandProcessor.h
+++ b/Source/Core/VideoCommon/CommandProcessor.h
@@ -169,7 +169,7 @@ void SetCpClearRegister();
void SetCpControlRegister();
void SetCpStatusRegister();
-void HandleUnknownOpcode(u8 cmd_byte, void* buffer, bool preprocess);
+void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess);
u32 GetPhysicalAddressMask();
diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp
index 04fc00d33a..f96c71b550 100644
--- a/Source/Core/VideoCommon/Fifo.cpp
+++ b/Source/Core/VideoCommon/Fifo.cpp
@@ -273,8 +273,8 @@ static void ReadDataFromFifoOnCPU(u32 readPtr)
}
}
Memory::CopyFromEmu(s_video_buffer_write_ptr, readPtr, len);
- s_video_buffer_pp_read_ptr = OpcodeDecoder::Run<true>(
- DataReader(s_video_buffer_pp_read_ptr, write_ptr + len), nullptr, false);
+ s_video_buffer_pp_read_ptr = OpcodeDecoder::RunFifo<true>(
+ DataReader(s_video_buffer_pp_read_ptr, write_ptr + len), nullptr);
// This would have to be locked if the GPU thread didn't spin.
s_video_buffer_write_ptr = write_ptr + len;
}
@@ -316,7 +316,7 @@ void RunGpuLoop()
if (write_ptr > seen_ptr)
{
s_video_buffer_read_ptr =
- OpcodeDecoder::Run(DataReader(s_video_buffer_read_ptr, write_ptr), nullptr, false);
+ OpcodeDecoder::RunFifo(DataReader(s_video_buffer_read_ptr, write_ptr), nullptr);
s_video_buffer_seen_ptr = write_ptr;
}
}
@@ -349,8 +349,8 @@ void RunGpuLoop()
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) - 32);
u8* write_ptr = s_video_buffer_write_ptr;
- s_video_buffer_read_ptr = OpcodeDecoder::Run(
- DataReader(s_video_buffer_read_ptr, write_ptr), &cyclesExecuted, false);
+ s_video_buffer_read_ptr = OpcodeDecoder::RunFifo(
+ DataReader(s_video_buffer_read_ptr, write_ptr), &cyclesExecuted);
fifo.CPReadPointer.store(readPtr, std::memory_order_relaxed);
fifo.CPReadWriteDistance.fetch_sub(32, std::memory_order_seq_cst);
@@ -466,8 +466,8 @@ static int RunGpuOnCpu(int ticks)
}
ReadDataFromFifo(fifo.CPReadPointer.load(std::memory_order_relaxed));
u32 cycles = 0;
- s_video_buffer_read_ptr = OpcodeDecoder::Run(
- DataReader(s_video_buffer_read_ptr, s_video_buffer_write_ptr), &cycles, false);
+ s_video_buffer_read_ptr = OpcodeDecoder::RunFifo(
+ DataReader(s_video_buffer_read_ptr, s_video_buffer_write_ptr), &cycles);
available_ticks -= cycles;
}
diff --git a/Source/Core/VideoCommon/OpcodeDecoding.cpp b/Source/Core/VideoCommon/OpcodeDecoding.cpp
index be879ddfbc..239aec158a 100644
--- a/Source/Core/VideoCommon/OpcodeDecoding.cpp
+++ b/Source/Core/VideoCommon/OpcodeDecoding.cpp
@@ -14,7 +14,7 @@
#include "VideoCommon/OpcodeDecoding.h"
-#include "Common/CommonTypes.h"
+#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Core/FifoPlayer/FifoRecorder.h"
#include "Core/HW/Memmap.h"
@@ -24,55 +24,15 @@
#include "VideoCommon/DataReader.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/Statistics.h"
+#include "VideoCommon/VertexLoaderBase.h"
#include "VideoCommon/VertexLoaderManager.h"
+#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/XFMemory.h"
+#include "VideoCommon/XFStructs.h"
namespace OpcodeDecoder
{
-namespace
-{
bool s_is_fifo_error_seen = false;
-
-u32 InterpretDisplayList(u32 address, u32 size)
-{
- u8* start_address;
-
- if (Fifo::UseDeterministicGPUThread())
- start_address = static_cast<u8*>(Fifo::PopFifoAuxBuffer(size));
- else
- start_address = Memory::GetPointer(address);
-
- u32 cycles = 0;
-
- // Avoid the crash if Memory::GetPointer failed ..
- if (start_address != nullptr)
- {
- // temporarily swap dl and non-dl (small "hack" for the stats)
- g_stats.SwapDL();
-
- Run(DataReader(start_address, start_address + size), &cycles, true);
- INCSTAT(g_stats.this_frame.num_dlists_called);
-
- // un-swap
- g_stats.SwapDL();
- }
-
- return cycles;
-}
-
-void InterpretDisplayListPreprocess(u32 address, u32 size)
-{
- u8* const start_address = Memory::GetPointer(address);
-
- Fifo::PushFifoAuxBuffer(start_address, size);
-
- if (start_address == nullptr)
- return;
-
- Run<true>(DataReader(start_address, start_address + size), nullptr, true);
-}
-} // Anonymous namespace
-
bool g_record_fifo_data = false;
void Init()
@@ -81,203 +41,205 @@ void Init()
}
template <bool is_preprocess>
-u8* Run(DataReader src, u32* cycles, bool in_display_list)
+class RunCallback final : public Callback
{
- u32 total_cycles = 0;
- u8* opcode_start = nullptr;
+public:
+ OPCODE_CALLBACK(void OnXF(u16 address, u8 count, const u8* data))
+ {
+ m_cycles += 18 + 6 * count;
- const auto finish_up = [cycles, &opcode_start, &total_cycles] {
- if (cycles != nullptr)
+ if constexpr (!is_preprocess)
{
- *cycles = total_cycles;
- }
- return opcode_start;
- };
+ // HACK
+ LoadXFReg(count, address,
+ DataReader{const_cast<u8*>(data), const_cast<u8*>(data) + count * sizeof(u32)});
- while (true)
+ INCSTAT(g_stats.this_frame.num_xf_loads);
+ }
+ }
+ OPCODE_CALLBACK(void OnCP(u8 command, u32 value))
{
- opcode_start = src.GetPointer();
-
- if (!src.size())
- return finish_up();
-
- const u8 cmd_byte = src.Read<u8>();
- switch (static_cast<Opcode>(cmd_byte))
+ m_cycles += 12;
+ if constexpr (!is_preprocess)
{
- case Opcode::GX_NOP:
- total_cycles += 6; // Hm, this means that we scan over nop streams pretty slowly...
- break;
-
- case Opcode::GX_UNKNOWN_RESET:
- total_cycles += 6; // Datel software uses this command
- DEBUG_LOG_FMT(VIDEO, "GX Reset?: {:08x}", cmd_byte);
- break;
+ // TODO: Move all dirty state checking here or to VertexLoaderManager,
+ // instead of it being in CPState
+ if (command == MATINDEX_A)
+ VertexShaderManager::SetTexMatrixChangedA(value);
+ else if (command == MATINDEX_B)
+ VertexShaderManager::SetTexMatrixChangedB(value);
+
+ INCSTAT(g_stats.this_frame.num_cp_loads);
+ }
+ GetCPState().LoadCPReg(command, value);
+ }
+ OPCODE_CALLBACK(void OnBP(u8 command, u32 value))
+ {
+ m_cycles += 12;
- case Opcode::GX_LOAD_CP_REG:
+ if constexpr (is_preprocess)
{
- if (src.size() < 1 + 4)
- return finish_up();
-
- total_cycles += 12;
-
- const u8 sub_cmd = src.Read<u8>();
- const u32 value = src.Read<u32>();
- LoadCPReg(sub_cmd, value, is_preprocess);
- if constexpr (!is_preprocess)
- INCSTAT(g_stats.this_frame.num_cp_loads);
+ LoadBPRegPreprocess(command, value, m_cycles);
}
- break;
-
- case Opcode::GX_LOAD_XF_REG:
+ else
{
- if (src.size() < 4)
- return finish_up();
-
- const u32 cmd2 = src.Read<u32>();
- const u32 transfer_size = ((cmd2 >> 16) & 15) + 1;
- if (src.size() < transfer_size * sizeof(u32))
- return finish_up();
-
- total_cycles += 18 + 6 * transfer_size;
-
- if constexpr (!is_preprocess)
- {
- const u32 xf_address = cmd2 & 0xFFFF;
- LoadXFReg(transfer_size, xf_address, src);
-
- INCSTAT(g_stats.this_frame.num_xf_loads);
- }
- src.Skip<u32>(transfer_size);
+ LoadBPReg(command, value, m_cycles);
+ INCSTAT(g_stats.this_frame.num_bp_loads);
}
- break;
+ }
+ OPCODE_CALLBACK(void OnIndexedLoad(CPArray array, u32 index, u16 address, u8 size))
+ {
+ m_cycles += 6;
- case Opcode::GX_LOAD_INDX_A: // Used for position matrices
- case Opcode::GX_LOAD_INDX_B: // Used for normal matrices
- case Opcode::GX_LOAD_INDX_C: // Used for postmatrices
- case Opcode::GX_LOAD_INDX_D: // Used for lights
- {
- if (src.size() < 4)
- return finish_up();
+ if constexpr (is_preprocess)
+ PreprocessIndexedXF(array, index, address, size);
+ else
+ LoadIndexedXF(array, index, address, size);
+ }
+ OPCODE_CALLBACK(void OnPrimitiveCommand(OpcodeDecoder::Primitive primitive, u8 vat,
+ u32 vertex_size, u16 num_vertices, const u8* vertex_data))
+ {
+ // load vertices
+ const u32 size = vertex_size * num_vertices;
- total_cycles += 6;
+ // HACK
+ DataReader src{const_cast<u8*>(vertex_data), const_cast<u8*>(vertex_data) + size};
+ const u32 bytes =
+ VertexLoaderManager::RunVertices(vat, primitive, num_vertices, src, is_preprocess);
- // Map the command byte to its ref array.
- // GX_LOAD_INDX_A (32) -> 0xC
- // GX_LOAD_INDX_B (40) -> 0xD
- // GX_LOAD_INDX_C (48) -> 0xE
- // GX_LOAD_INDX_D (56) -> 0xF
- const auto array = static_cast<CPArray>((cmd_byte / 8) + 8);
+ ASSERT(bytes == size);
- if constexpr (is_preprocess)
- PreprocessIndexedXF(array, src.Read<u32>());
- else
- LoadIndexedXF(array, src.Read<u32>());
- }
- break;
+ // 4 GPU ticks per vertex, 3 CPU ticks per GPU tick
+ m_cycles += num_vertices * 4 * 3 + 6;
+ }
+ // This can't be inlined since it calls Run, which makes it recursive
+ // m_in_display_list prevents it from actually recursing infinitely, but there's no real benefit
+ // to inlining Run for the display list directly.
+ OPCODE_CALLBACK_NOINLINE(void OnDisplayList(u32 address, u32 size))
+ {
+ m_cycles += 6;
- case Opcode::GX_CMD_CALL_DL:
+ if (m_in_display_list)
{
- if (src.size() < 8)
- return finish_up();
-
- const u32 address = src.Read<u32>();
- const u32 count = src.Read<u32>();
-
- if (in_display_list)
- {
- total_cycles += 6;
- INFO_LOG_FMT(VIDEO, "recursive display list detected");
- }
- else
- {
- if constexpr (is_preprocess)
- InterpretDisplayListPreprocess(address, count);
- else
- total_cycles += 6 + InterpretDisplayList(address, count);
- }
+ WARN_LOG_FMT(VIDEO, "recursive display list detected");
}
- break;
-
- case Opcode::GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics
- // registers after that
- total_cycles += 6;
- DEBUG_LOG_FMT(VIDEO, "GX 0x44: {:08x}", cmd_byte);
- break;
-
- case Opcode::GX_CMD_INVL_VC: // Invalidate Vertex Cache
- total_cycles += 6;
- DEBUG_LOG_FMT(VIDEO, "Invalidate (vertex cache?)");
- break;
+ else
+ {
+ m_in_display_list = true;
- case Opcode::GX_LOAD_BP_REG:
- // In skipped_frame case: We have to let BP writes through because they set
- // tokens and stuff. TODO: Call a much simplified LoadBPReg instead.
+ if constexpr (is_preprocess)
{
- if (src.size() < 4)
- return finish_up();
+ const u8* const start_address = Memory::GetPointer(address);
- total_cycles += 12;
+ Fifo::PushFifoAuxBuffer(start_address, size);
- const u32 bp_cmd = src.Read<u32>();
- if constexpr (is_preprocess)
+ if (start_address != nullptr)
{
- LoadBPRegPreprocess(bp_cmd, total_cycles);
- }
- else
- {
- LoadBPReg(bp_cmd, total_cycles);
- INCSTAT(g_stats.this_frame.num_bp_loads);
+ Run(start_address, size, *this);
}
}
- break;
-
- // draw primitives
- default:
- if ((cmd_byte & 0xC0) == 0x80)
+ else
{
- // load vertices
- if (src.size() < 2)
- return finish_up();
+ const u8* start_address;
- const u16 num_vertices = src.Read<u16>();
- const int bytes = VertexLoaderManager::RunVertices(
- cmd_byte & GX_VAT_MASK, // Vertex loader index (0 - 7)
- static_cast<Primitive>((cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT),
- num_vertices, src, is_preprocess);
+ if (Fifo::UseDeterministicGPUThread())
+ start_address = static_cast<u8*>(Fifo::PopFifoAuxBuffer(size));
+ else
+ start_address = Memory::GetPointer(address);
- if (bytes < 0)
- return finish_up();
+ // Avoid the crash if Memory::GetPointer failed ..
+ if (start_address != nullptr)
+ {
+ // temporarily swap dl and non-dl (small "hack" for the stats)
+ g_stats.SwapDL();
- src.Skip(bytes);
+ Run(start_address, size, *this);
+ INCSTAT(g_stats.this_frame.num_dlists_called);
- // 4 GPU ticks per vertex, 3 CPU ticks per GPU tick
- total_cycles += num_vertices * 4 * 3 + 6;
- }
- else
- {
- if (!s_is_fifo_error_seen)
- CommandProcessor::HandleUnknownOpcode(cmd_byte, opcode_start, is_preprocess);
- ERROR_LOG_FMT(VIDEO, "FIFO: Unknown Opcode({:#04x} @ {}, preprocessing = {})", cmd_byte,
- fmt::ptr(opcode_start), is_preprocess ? "yes" : "no");
- s_is_fifo_error_seen = true;
- total_cycles += 1;
+ // un-swap
+ g_stats.SwapDL();
+ }
}
- break;
+
+ m_in_display_list = false;
}
+ }
+ OPCODE_CALLBACK(void OnNop(u32 count))
+ {
+ m_cycles += 6 * count; // Hm, this means that we scan over nop streams pretty slowly...
+ }
+ OPCODE_CALLBACK(void OnUnknown(u8 opcode, const u8* data))
+ {
+ if (static_cast<Opcode>(opcode) == Opcode::GX_UNKNOWN_RESET)
+ {
+ // Datel software uses this command
+ m_cycles += 6;
+ DEBUG_LOG_FMT(VIDEO, "GX Reset?");
+ }
+ else if (static_cast<Opcode>(opcode) == Opcode::GX_CMD_UNKNOWN_METRICS)
+ {
+ // 'Zelda Four Swords' calls it and checks the metrics registers after that
+ m_cycles += 6;
+ DEBUG_LOG_FMT(VIDEO, "GX 0x44");
+ }
+ else if (static_cast<Opcode>(opcode) == Opcode::GX_CMD_INVL_VC)
+ {
+ // Invalidate Vertex Cache
+ m_cycles += 6;
+ DEBUG_LOG_FMT(VIDEO, "Invalidate (vertex cache?)");
+ }
+ else
+ {
+ if (!s_is_fifo_error_seen)
+ CommandProcessor::HandleUnknownOpcode(opcode, data, is_preprocess);
+ ERROR_LOG_FMT(VIDEO, "FIFO: Unknown Opcode({:#04x} @ {}, preprocessing = {})", opcode,
+ fmt::ptr(data), is_preprocess ? "yes" : "no");
+ s_is_fifo_error_seen = true;
+ m_cycles += 1;
+ }
+ }
- // Display lists get added directly into the FIFO stream
+ OPCODE_CALLBACK(void OnCommand(const u8* data, u32 size))
+ {
+ ASSERT(size >= 1);
if constexpr (!is_preprocess)
{
- if (g_record_fifo_data && static_cast<Opcode>(cmd_byte) != Opcode::GX_CMD_CALL_DL)
+ // Display lists get added directly into the FIFO stream since this same callback is used to
+ // process them.
+ if (g_record_fifo_data && static_cast<Opcode>(data[0]) != Opcode::GX_CMD_CALL_DL)
{
- const u8* const opcode_end = src.GetPointer();
- FifoRecorder::GetInstance().WriteGPCommand(opcode_start, u32(opcode_end - opcode_start));
+ FifoRecorder::GetInstance().WriteGPCommand(data, size);
}
}
}
+
+ OPCODE_CALLBACK(CPState& GetCPState())
+ {
+ if constexpr (is_preprocess)
+ return g_preprocess_cp_state;
+ else
+ return g_main_cp_state;
+ }
+
+ u32 m_cycles = 0;
+ bool m_in_display_list = false;
+};
+
+template <bool is_preprocess>
+u8* RunFifo(DataReader src, u32* cycles)
+{
+ using CallbackT = RunCallback<is_preprocess>;
+ auto callback = CallbackT{};
+ u32 size = Run(src.GetPointer(), static_cast<u32>(src.size()), callback);
+
+ if (cycles != nullptr)
+ *cycles = callback.m_cycles;
+
+ src.Skip(size);
+ return src.GetPointer();
}
-template u8* Run<true>(DataReader src, u32* cycles, bool in_display_list);
-template u8* Run<false>(DataReader src, u32* cycles, bool in_display_list);
+template u8* RunFifo<true>(DataReader src, u32* cycles);
+template u8* RunFifo<false>(DataReader src, u32* cycles);
} // namespace OpcodeDecoder
diff --git a/Source/Core/VideoCommon/OpcodeDecoding.h b/Source/Core/VideoCommon/OpcodeDecoding.h
index df1059f221..2d1632efc3 100644
--- a/Source/Core/VideoCommon/OpcodeDecoding.h
+++ b/Source/Core/VideoCommon/OpcodeDecoding.h
@@ -3,9 +3,17 @@
#pragma once
+#include <type_traits>
+
+#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/EnumFormatter.h"
+#include "Common/Inline.h"
+#include "Common/Swap.h"
+#include "VideoCommon/CPMemory.h"
+#include "VideoCommon/VertexLoaderBase.h"
+struct CPState;
class DataReader;
namespace OpcodeDecoder
@@ -55,8 +63,220 @@ enum class Primitive : u8
void Init();
+// Interface for the Run and RunCommand functions below.
+// The functions themselves are templates so that the compiler generates separate versions for each
+// callback (with the callback functions inlined), so the callback doesn't actually need to be
+// publicly inherited.
+// Compilers don't generate warnings for failed inlining with virtual functions, so this define
+// allows disabling the use of virtual functions to generate those warnings. However, this means
+// that missing functions will generate errors on their use in RunCommand, instead of in the
+// subclass, which can be confusing.
+#define OPCODE_CALLBACK_USE_INHERITANCE
+
+#ifdef OPCODE_CALLBACK_USE_INHERITANCE
+#define OPCODE_CALLBACK(sig) DOLPHIN_FORCE_INLINE sig override
+#define OPCODE_CALLBACK_NOINLINE(sig) sig override
+#else
+#define OPCODE_CALLBACK(sig) DOLPHIN_FORCE_INLINE sig
+#define OPCODE_CALLBACK_NOINLINE(sig) sig
+#endif
+class Callback
+{
+#ifdef OPCODE_CALLBACK_USE_INHERITANCE
+public:
+ virtual ~Callback() = default;
+
+ // Called on any XF command.
+ virtual void OnXF(u16 address, u8 count, const u8* data) = 0;
+ // Called on any CP command.
+ // Subclasses should update the CP state with GetCPState().LoadCPReg(command, value) so that
+ // primitive commands decode properly.
+ virtual void OnCP(u8 command, u32 value) = 0;
+ // Called on any BP command.
+ virtual void OnBP(u8 command, u32 value) = 0;
+ // Called on any indexed XF load command.
+ virtual void OnIndexedLoad(CPArray array, u32 index, u16 address, u8 size) = 0;
+ // Called on any primitive command.
+ virtual void OnPrimitiveCommand(OpcodeDecoder::Primitive primitive, u8 vat, u32 vertex_size,
+ u16 num_vertices, const u8* vertex_data) = 0;
+ // Called on a display list.
+ virtual void OnDisplayList(u32 address, u32 size) = 0;
+ // Called on any NOP commands (which are all merged into a single call).
+ virtual void OnNop(u32 count) = 0;
+ // Called on an unknown opcode, or an opcode that is known but not implemented.
+ // data[0] is opcode.
+ virtual void OnUnknown(u8 opcode, const u8* data) = 0;
+
+ // Called on ANY command. The first byte of data is the opcode. Size will be at least 1.
+ // This function is called after one of the above functions is called.
+ virtual void OnCommand(const u8* data, u32 size) = 0;
+
+ // Get the current CP state. Needed for vertex decoding; will also be mutated for CP commands.
+ virtual CPState& GetCPState() = 0;
+#endif
+};
+
+namespace detail
+{
+// Main logic; split so that the main RunCommand can call OnCommand with the returned size.
+template <typename T, typename = std::enable_if_t<std::is_base_of_v<Callback, T>>>
+static DOLPHIN_FORCE_INLINE u32 RunCommand(const u8* data, u32 available, T& callback)
+{
+ if (available < 1)
+ return 0;
+
+ const Opcode cmd = static_cast<Opcode>(data[0]);
+
+ switch (cmd)
+ {
+ case Opcode::GX_NOP:
+ {
+ u32 count = 1;
+ while (count < available && static_cast<Opcode>(data[count]) == Opcode::GX_NOP)
+ count++;
+ callback.OnNop(count);
+ return count;
+ }
+
+ case Opcode::GX_LOAD_CP_REG:
+ {
+ if (available < 6)
+ return 0;
+
+ const u8 cmd2 = data[1];
+ const u32 value = Common::swap32(&data[2]);
+
+ callback.OnCP(cmd2, value);
+
+ return 6;
+ }
+
+ case Opcode::GX_LOAD_XF_REG:
+ {
+ if (available < 5)
+ return 0;
+
+ const u32 cmd2 = Common::swap32(&data[1]);
+ const u16 base_address = cmd2 & 0xffff;
+
+ const u16 stream_size_temp = cmd2 >> 16;
+ ASSERT(stream_size_temp < 16);
+ const u8 stream_size = (stream_size_temp & 0xf) + 1;
+
+ if (available < u32(5 + stream_size * 4))
+ return 0;
+
+ callback.OnXF(base_address, stream_size, &data[5]);
+
+ return 5 + stream_size * 4;
+ }
+
+ case Opcode::GX_LOAD_INDX_A: // Used for position matrices
+ case Opcode::GX_LOAD_INDX_B: // Used for normal matrices
+ case Opcode::GX_LOAD_INDX_C: // Used for postmatrices
+ case Opcode::GX_LOAD_INDX_D: // Used for lights
+ {
+ if (available < 5)
+ return 0;
+
+ const u32 value = Common::swap32(&data[1]);
+
+ const u32 index = value >> 16;
+ const u16 address = value & 0xFFF; // TODO: check mask
+ const u8 size = ((value >> 12) & 0xF) + 1;
+
+ // Map the command byte to its ref array.
+ // GX_LOAD_INDX_A (32 = 8*4) . CPArray::XF_A (4+8 = 12)
+ // GX_LOAD_INDX_B (40 = 8*5) . CPArray::XF_B (5+8 = 13)
+ // GX_LOAD_INDX_C (48 = 8*6) . CPArray::XF_C (6+8 = 14)
+ // GX_LOAD_INDX_D (56 = 8*7) . CPArray::XF_D (7+8 = 15)
+ const auto ref_array = static_cast<CPArray>((static_cast<u8>(cmd) / 8) + 8);
+
+ callback.OnIndexedLoad(ref_array, index, address, size);
+ return 5;
+ }
+
+ case Opcode::GX_CMD_CALL_DL:
+ {
+ if (available < 9)
+ return 0;
+
+ const u32 address = Common::swap32(&data[1]);
+ const u32 size = Common::swap32(&data[5]);
+
+ callback.OnDisplayList(address, size);
+ return 9;
+ }
+
+ case Opcode::GX_LOAD_BP_REG:
+ {
+ if (available < 5)
+ return 0;
+
+ const u8 cmd2 = data[1];
+ const u32 value = Common::swap24(&data[2]);
+
+ callback.OnBP(cmd2, value);
+
+ return 5;
+ }
+
+ default:
+ if (cmd >= Opcode::GX_PRIMITIVE_START && cmd <= Opcode::GX_PRIMITIVE_END)
+ {
+ if (available < 3)
+ return 0;
+
+ const u8 cmdbyte = static_cast<u8>(cmd);
+ const OpcodeDecoder::Primitive primitive = static_cast<OpcodeDecoder::Primitive>(
+ (cmdbyte & OpcodeDecoder::GX_PRIMITIVE_MASK) >> OpcodeDecoder::GX_PRIMITIVE_SHIFT);
+ const u8 vat = cmdbyte & OpcodeDecoder::GX_VAT_MASK;
+
+ const u32 vertex_size = VertexLoaderBase::GetVertexSize(callback.GetCPState().vtx_desc,
+ callback.GetCPState().vtx_attr[vat]);
+ const u16 num_vertices = Common::swap16(&data[1]);
+
+ if (available < 3 + num_vertices * vertex_size)
+ return 0;
+
+ callback.OnPrimitiveCommand(primitive, vat, vertex_size, num_vertices, &data[3]);
+
+ return 3 + num_vertices * vertex_size;
+ }
+ }
+
+ callback.OnUnknown(static_cast<u8>(cmd), data);
+ return 1;
+}
+} // namespace detail
+
+template <typename T, typename = std::enable_if_t<std::is_base_of_v<Callback, T>>>
+DOLPHIN_FORCE_INLINE u32 RunCommand(const u8* data, u32 available, T& callback)
+{
+ const u32 size = detail::RunCommand(data, available, callback);
+ if (size > 0)
+ {
+ callback.OnCommand(data, size);
+ }
+ return size;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_base_of_v<Callback, T>>>
+DOLPHIN_FORCE_INLINE u32 Run(const u8* data, u32 available, T& callback)
+{
+ u32 size = 0;
+ while (size < available)
+ {
+ const u32 command_size = RunCommand(&data[size], available - size, callback);
+ if (command_size == 0)
+ break;
+ size += command_size;
+ }
+ return size;
+}
+
template <bool is_preprocess = false>
-u8* Run(DataReader src, u32* cycles, bool in_display_list);
+u8* RunFifo(DataReader src, u32* cycles);
} // namespace OpcodeDecoder
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index e606a464e6..16d36f7453 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -963,7 +963,7 @@ void Renderer::RecordVideoMemory()
const u32* xfregs_ptr = reinterpret_cast<const u32*>(&xfmem) + FifoDataFile::XF_MEM_SIZE;
u32 xfregs_size = sizeof(XFMemory) / 4 - FifoDataFile::XF_MEM_SIZE;
- FillCPMemoryArray(cpmem);
+ g_main_cp_state.FillCPMemoryArray(cpmem);
FifoRecorder::GetInstance().SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size,
texMem);
diff --git a/Source/Core/VideoCommon/VertexLoaderARM64.cpp b/Source/Core/VideoCommon/VertexLoaderARM64.cpp
index 6663e6c8ae..75afefed16 100644
--- a/Source/Core/VideoCommon/VertexLoaderARM64.cpp
+++ b/Source/Core/VideoCommon/VertexLoaderARM64.cpp
@@ -405,7 +405,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
MOV(skipped_reg, ARM64Reg::WZR);
MOV(saved_count, count_reg);
- MOVP2R(stride_reg, g_main_cp_state.array_strides);
+ MOVP2R(stride_reg, g_main_cp_state.array_strides.data());
MOVP2R(arraybase_reg, VertexLoaderManager::cached_arraybases);
if (need_scale)
diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp
index e2475d666f..901e14044f 100644
--- a/Source/Core/VideoCommon/VertexLoaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp
@@ -12,17 +12,14 @@
#include <utility>
#include <vector>
-#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/EnumMap.h"
#include "Common/Logging/Log.h"
-#include "Core/DolphinAnalytics.h"
#include "Core/HW/Memmap.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
-#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/DataReader.h"
#include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/NativeVertexFormat.h"
@@ -298,147 +295,3 @@ NativeVertexFormat* GetCurrentVertexFormat()
}
} // namespace VertexLoaderManager
-
-void LoadCPReg(u32 sub_cmd, u32 value, bool is_preprocess)
-{
- bool update_global_state = !is_preprocess;
- CPState* state = is_preprocess ? &g_preprocess_cp_state : &g_main_cp_state;
- switch (sub_cmd & CP_COMMAND_MASK)
- {
- case UNKNOWN_00:
- case UNKNOWN_10:
- case UNKNOWN_20:
- if (!(sub_cmd == UNKNOWN_20 && value == 0))
- {
- // All titles using libogc or the official SDK issue 0x20 with value=0 on startup
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_CP_PERF_COMMAND);
- DEBUG_LOG_FMT(VIDEO, "Unknown CP command possibly relating to perf queries used: {:02x}",
- sub_cmd);
- }
- break;
-
- case MATINDEX_A:
- if (sub_cmd != MATINDEX_A)
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO,
- "CP MATINDEX_A: an exact value of {:02x} was expected "
- "but instead a value of {:02x} was seen",
- MATINDEX_A, sub_cmd);
- }
-
- if (update_global_state)
- VertexShaderManager::SetTexMatrixChangedA(value);
- break;
-
- case MATINDEX_B:
- if (sub_cmd != MATINDEX_B)
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO,
- "CP MATINDEX_B: an exact value of {:02x} was expected "
- "but instead a value of {:02x} was seen",
- MATINDEX_B, sub_cmd);
- }
-
- if (update_global_state)
- VertexShaderManager::SetTexMatrixChangedB(value);
- break;
-
- case VCD_LO:
- if (sub_cmd != VCD_LO) // Stricter than YAGCD
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO,
- "CP VCD_LO: an exact value of {:02x} was expected "
- "but instead a value of {:02x} was seen",
- VCD_LO, sub_cmd);
- }
-
- state->vtx_desc.low.Hex = value;
- state->attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
- state->bases_dirty = true;
- break;
-
- case VCD_HI:
- if (sub_cmd != VCD_HI) // Stricter than YAGCD
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO,
- "CP VCD_HI: an exact value of {:02x} was expected "
- "but instead a value of {:02x} was seen",
- VCD_HI, sub_cmd);
- }
-
- state->vtx_desc.high.Hex = value;
- state->attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
- state->bases_dirty = true;
- break;
-
- case CP_VAT_REG_A:
- if ((sub_cmd - CP_VAT_REG_A) >= CP_NUM_VAT_REG)
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO, "CP_VAT_REG_A: Invalid VAT {}", sub_cmd - CP_VAT_REG_A);
- }
- state->vtx_attr[sub_cmd & CP_VAT_MASK].g0.Hex = value;
- state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
- break;
-
- case CP_VAT_REG_B:
- if ((sub_cmd - CP_VAT_REG_B) >= CP_NUM_VAT_REG)
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO, "CP_VAT_REG_B: Invalid VAT {}", sub_cmd - CP_VAT_REG_B);
- }
- state->vtx_attr[sub_cmd & CP_VAT_MASK].g1.Hex = value;
- state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
- break;
-
- case CP_VAT_REG_C:
- if ((sub_cmd - CP_VAT_REG_C) >= CP_NUM_VAT_REG)
- {
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
- WARN_LOG_FMT(VIDEO, "CP_VAT_REG_C: Invalid VAT {}", sub_cmd - CP_VAT_REG_C);
- }
- state->vtx_attr[sub_cmd & CP_VAT_MASK].g2.Hex = value;
- state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
- break;
-
- // Pointers to vertex arrays in GC RAM
- case ARRAY_BASE:
- state->array_bases[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] =
- value & CommandProcessor::GetPhysicalAddressMask();
- state->bases_dirty = true;
- break;
-
- case ARRAY_STRIDE:
- state->array_strides[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] = value & 0xFF;
- break;
-
- default:
- DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_CP_COMMAND);
- WARN_LOG_FMT(VIDEO, "Unknown CP register {:02x} set to {:08x}", sub_cmd, value);
- }
-}
-
-void FillCPMemoryArray(u32* memory)
-{
- memory[MATINDEX_A] = g_main_cp_state.matrix_index_a.Hex;
- memory[MATINDEX_B] = g_main_cp_state.matrix_index_b.Hex;
- memory[VCD_LO] = g_main_cp_state.vtx_desc.low.Hex;
- memory[VCD_HI] = g_main_cp_state.vtx_desc.high.Hex;
-
- for (int i = 0; i < CP_NUM_VAT_REG; ++i)
- {
- memory[CP_VAT_REG_A + i] = g_main_cp_state.vtx_attr[i].g0.Hex;
- memory[CP_VAT_REG_B + i] = g_main_cp_state.vtx_attr[i].g1.Hex;
- memory[CP_VAT_REG_C + i] = g_main_cp_state.vtx_attr[i].g2.Hex;
- }
-
- for (u8 i = 0; i < CP_NUM_ARRAYS; ++i)
- {
- memory[ARRAY_BASE + i] = g_main_cp_state.array_bases[static_cast<CPArray>(i)];
- memory[ARRAY_STRIDE + i] = g_main_cp_state.array_strides[static_cast<CPArray>(i)];
- }
-}
diff --git a/Source/Core/VideoCommon/XFMemory.h b/Source/Core/VideoCommon/XFMemory.h
index 32c5dbb607..4fb538b09e 100644
--- a/Source/Core/VideoCommon/XFMemory.h
+++ b/Source/Core/VideoCommon/XFMemory.h
@@ -454,10 +454,10 @@ struct XFMemory
u32 unk9[8]; // 0x1048 - 0x104f
PostMtxInfo postMtxInfo[8]; // 0x1050 - 0x1057
};
-static_assert(sizeof(XFMemory) == sizeof(u32) * 0x1058);
+static_assert(sizeof(XFMemory) == sizeof(u32) * XFMEM_REGISTERS_END);
extern XFMemory xfmem;
void LoadXFReg(u32 transferSize, u32 address, DataReader src);
-void LoadIndexedXF(CPArray array, u32 val);
-void PreprocessIndexedXF(CPArray array, u32 val);
+void LoadIndexedXF(CPArray array, u32 index, u16 address, u8 size);
+void PreprocessIndexedXF(CPArray array, u32 index, u16 address, u8 size);
diff --git a/Source/Core/VideoCommon/XFStructs.cpp b/Source/Core/VideoCommon/XFStructs.cpp
index c31b8cbb54..65ebd548b8 100644
--- a/Source/Core/VideoCommon/XFStructs.cpp
+++ b/Source/Core/VideoCommon/XFStructs.cpp
@@ -264,19 +264,9 @@ void LoadXFReg(u32 transferSize, u32 baseAddress, DataReader src)
}
}
-constexpr std::tuple<u32, u32, u32> ExtractIndexedXF(u32 val)
-{
- const u32 index = val >> 16;
- const u32 address = val & 0xFFF; // check mask
- const u32 size = ((val >> 12) & 0xF) + 1;
-
- return {index, address, size};
-}
-
// TODO - verify that it is correct. Seems to work, though.
-void LoadIndexedXF(CPArray array, u32 val)
+void LoadIndexedXF(CPArray array, u32 index, u16 address, u8 size)
{
- const auto [index, address, size] = ExtractIndexedXF(val);
// load stuff from array to address in xf mem
u32* currData = (u32*)(&xfmem) + address;
@@ -307,10 +297,8 @@ void LoadIndexedXF(CPArray array, u32 val)
}
}
-void PreprocessIndexedXF(CPArray array, u32 val)
+void PreprocessIndexedXF(CPArray array, u32 index, u16 address, u8 size)
{
- const auto [index, address, size] = ExtractIndexedXF(val);
-
const u8* new_data = Memory::GetPointer(g_preprocess_cp_state.array_bases[array] +
g_preprocess_cp_state.array_strides[array] * index);
@@ -581,13 +569,9 @@ std::string GetXFMemDescription(u32 address, u32 value)
}
}
-std::pair<std::string, std::string> GetXFTransferInfo(const u8* data)
+std::pair<std::string, std::string> GetXFTransferInfo(u16 base_address, u8 transfer_size,
+ const u8* data)
{
- const u32 cmd = Common::swap32(data);
- data += 4;
- u32 base_address = cmd & 0xFFFF;
- const u32 transfer_size = ((cmd >> 16) & 15) + 1;
-
if (base_address > XFMEM_REGISTERS_END)
{
return std::make_pair("Invalid XF Transfer", "Base address past end of address space");
@@ -655,10 +639,9 @@ std::pair<std::string, std::string> GetXFTransferInfo(const u8* data)
return std::make_pair(fmt::to_string(name), fmt::to_string(desc));
}
-std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 value)
+std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 index, u16 address,
+ u8 size)
{
- const auto [index, address, size] = ExtractIndexedXF(value);
-
const auto desc = fmt::format("Load {} bytes to XF address {:03x} from CP array {} row {}", size,
address, array, index);
fmt::memory_buffer written;
diff --git a/Source/Core/VideoCommon/XFStructs.h b/Source/Core/VideoCommon/XFStructs.h
index 7e1cc2c49f..caf197b7f0 100644
--- a/Source/Core/VideoCommon/XFStructs.h
+++ b/Source/Core/VideoCommon/XFStructs.h
@@ -11,5 +11,7 @@
std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value);
std::string GetXFMemName(u32 address);
std::string GetXFMemDescription(u32 address, u32 value);
-std::pair<std::string, std::string> GetXFTransferInfo(const u8* data);
-std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 value);
+std::pair<std::string, std::string> GetXFTransferInfo(u16 base_address, u8 transfer_size,
+ const u8* data);
+std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 index, u16 address,
+ u8 size);