diff options
| author | Pokechu22 <Pokechu022@gmail.com> | 2021-04-22 20:57:56 -0700 |
|---|---|---|
| committer | Pokechu22 <Pokechu022@gmail.com> | 2021-12-18 15:21:36 -0800 |
| commit | b5fd35f95145ecc8f88a179229ed69b390eb76be (patch) | |
| tree | 7b6700d35c609ba6a43fcdf55a12d6756e9a206b /Source/Core/VideoCommon | |
| parent | 04418262063bf6034bc2be172a64eb99a9e9a2bb (diff) | |
Refactor OpcodeDecoding and FIFO analyzer to use callbacks
Diffstat (limited to 'Source/Core/VideoCommon')
| -rw-r--r-- | Source/Core/VideoCommon/BPMemory.h | 4 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/BPStructs.cpp | 22 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CPMemory.cpp | 169 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CPMemory.h | 18 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CommandProcessor.cpp | 7 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CommandProcessor.h | 2 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/Fifo.cpp | 14 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/OpcodeDecoding.cpp | 364 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/OpcodeDecoding.h | 222 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/RenderBase.cpp | 2 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VertexLoaderARM64.cpp | 2 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VertexLoaderManager.cpp | 147 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/XFMemory.h | 6 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/XFStructs.cpp | 29 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/XFStructs.h | 6 |
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); |
