diff --git a/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp index e19bc22139..27936506e5 100644 --- a/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp @@ -258,37 +258,37 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode) void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem) { - switch (subCmd & 0xF0) + switch (subCmd & CP_COMMAND_MASK) { - case 0x50: + case VCD_LO: cpMem.vtxDesc.low.Hex = value; break; - case 0x60: + case VCD_HI: cpMem.vtxDesc.high.Hex = value; break; - case 0x70: - ASSERT((subCmd & 0x0F) < 8); - cpMem.vtxAttr[subCmd & 7].g0.Hex = value; + case CP_VAT_REG_A: + ASSERT(subCmd - CP_VAT_REG_A < CP_NUM_VAT_REG); + cpMem.vtxAttr[subCmd & CP_VAT_MASK].g0.Hex = value; break; - case 0x80: - ASSERT((subCmd & 0x0F) < 8); - cpMem.vtxAttr[subCmd & 7].g1.Hex = value; + case CP_VAT_REG_B: + ASSERT(subCmd - CP_VAT_REG_B < CP_NUM_VAT_REG); + cpMem.vtxAttr[subCmd & CP_VAT_MASK].g1.Hex = value; break; - case 0x90: - ASSERT((subCmd & 0x0F) < 8); - cpMem.vtxAttr[subCmd & 7].g2.Hex = value; + case CP_VAT_REG_C: + ASSERT(subCmd - CP_VAT_REG_C < CP_NUM_VAT_REG); + cpMem.vtxAttr[subCmd & CP_VAT_MASK].g2.Hex = value; break; - case 0xA0: - cpMem.arrayBases[subCmd & 0xF] = value; + case ARRAY_BASE: + cpMem.arrayBases[subCmd & CP_ARRAY_MASK] = value; break; - case 0xB0: - cpMem.arrayStrides[subCmd & 0xF] = value & 0xFF; + case ARRAY_STRIDE: + cpMem.arrayStrides[subCmd & CP_ARRAY_MASK] = value & 0xFF; break; } } diff --git a/Source/Core/Core/FifoPlayer/FifoAnalyzer.h b/Source/Core/Core/FifoPlayer/FifoAnalyzer.h index 5f1d883ba2..4adfc4e259 100644 --- a/Source/Core/Core/FifoPlayer/FifoAnalyzer.h +++ b/Source/Core/Core/FifoPlayer/FifoAnalyzer.h @@ -22,9 +22,9 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode); struct CPMemory { TVtxDesc vtxDesc; - std::array vtxAttr; - std::array arrayBases; - std::array arrayStrides; + std::array vtxAttr; + std::array arrayBases; + std::array arrayStrides; }; void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem); diff --git a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp index 4b8da762bc..9eb589491b 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp @@ -6,6 +6,7 @@ #include +#include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Core/FifoPlayer/FifoAnalyzer.h" #include "Core/FifoPlayer/FifoDataFile.h" @@ -25,14 +26,14 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, std::vector& frameInfo) { u32* cpMem = file->GetCPMem(); - FifoAnalyzer::LoadCPReg(0x50, cpMem[0x50], s_CpMem); - FifoAnalyzer::LoadCPReg(0x60, cpMem[0x60], s_CpMem); + FifoAnalyzer::LoadCPReg(VCD_LO, cpMem[VCD_LO], s_CpMem); + FifoAnalyzer::LoadCPReg(VCD_HI, cpMem[VCD_HI], s_CpMem); - for (int i = 0; i < 8; ++i) + for (u32 i = 0; i < CP_NUM_VAT_REG; ++i) { - FifoAnalyzer::LoadCPReg(0x70 + i, cpMem[0x70 + i], s_CpMem); - FifoAnalyzer::LoadCPReg(0x80 + i, cpMem[0x80 + i], s_CpMem); - FifoAnalyzer::LoadCPReg(0x90 + i, cpMem[0x90 + i], s_CpMem); + FifoAnalyzer::LoadCPReg(CP_VAT_REG_A + i, cpMem[CP_VAT_REG_A + i], s_CpMem); + FifoAnalyzer::LoadCPReg(CP_VAT_REG_B + i, cpMem[CP_VAT_REG_B + i], s_CpMem); + FifoAnalyzer::LoadCPReg(CP_VAT_REG_C + i, cpMem[CP_VAT_REG_C + i], s_CpMem); } frameInfo.clear(); @@ -80,6 +81,7 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, { // Clean up frame analysis analyzed.objectStarts.clear(); + analyzed.objectCPStates.clear(); analyzed.objectEnds.clear(); return; @@ -88,9 +90,14 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, if (wasDrawing != s_DrawingObject) { if (s_DrawingObject) + { analyzed.objectStarts.push_back(cmdStart); + analyzed.objectCPStates.push_back(s_CpMem); + } else + { analyzed.objectEnds.push_back(cmdStart); + } } cmdStart += cmdSize; @@ -98,5 +105,8 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, if (analyzed.objectEnds.size() < analyzed.objectStarts.size()) analyzed.objectEnds.push_back(cmdStart); + + ASSERT(analyzed.objectStarts.size() == analyzed.objectCPStates.size()); + ASSERT(analyzed.objectStarts.size() == analyzed.objectEnds.size()); } } diff --git a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h index 47cbfa6ae8..f7847a3a2b 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h +++ b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h @@ -7,11 +7,15 @@ #include #include +#include "Core/FifoPlayer/FifoAnalyzer.h" #include "Core/FifoPlayer/FifoDataFile.h" struct AnalyzedFrameInfo { + // Start of the primitives for the object (after previous update commands) std::vector objectStarts; + std::vector objectCPStates; + // End of the primitives for the object std::vector objectEnds; std::vector memoryUpdates; }; diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp index 880ef791a5..da35c44646 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp @@ -51,7 +51,7 @@ bool FifoPlayer::Open(const std::string& filename) { FifoPlaybackAnalyzer::AnalyzeFrames(m_File.get(), m_FrameInfo); - m_FrameRangeEnd = m_File->GetFrameCount(); + m_FrameRangeEnd = m_File->GetFrameCount() - 1; } if (m_FileLoadedCb) @@ -131,13 +131,10 @@ private: CPU::State FifoPlayer::AdvanceFrame() { - if (m_CurrentFrame >= m_FrameRangeEnd) + if (m_CurrentFrame > m_FrameRangeEnd) { if (!m_Loop) return CPU::State::PowerDown; - // If there are zero frames in the range then sleep instead of busy spinning - if (m_FrameRangeStart >= m_FrameRangeEnd) - return CPU::State::Stepping; // When looping, reload the contents of all the BP/CP/CF registers. // This ensures that each time the first frame is played back, the state of the @@ -189,23 +186,40 @@ bool FifoPlayer::IsRunningWithFakeVideoInterfaceUpdates() const return m_File->ShouldGenerateFakeVIUpdates(); } -u32 FifoPlayer::GetFrameObjectCount() const +u32 FifoPlayer::GetMaxObjectCount() const { - if (m_CurrentFrame < m_FrameInfo.size()) + u32 result = 0; + for (auto& frame : m_FrameInfo) { - return (u32)(m_FrameInfo[m_CurrentFrame].objectStarts.size()); + const u32 count = static_cast(frame.objectStarts.size()); + if (count > result) + result = count; + } + return result; +} + +u32 FifoPlayer::GetFrameObjectCount(u32 frame) const +{ + if (frame < m_FrameInfo.size()) + { + return static_cast(m_FrameInfo[frame].objectStarts.size()); } return 0; } +u32 FifoPlayer::GetCurrentFrameObjectCount() const +{ + return GetFrameObjectCount(m_CurrentFrame); +} + void FifoPlayer::SetFrameRangeStart(u32 start) { if (m_File) { - u32 frameCount = m_File->GetFrameCount(); - if (start > frameCount) - start = frameCount; + const u32 lastFrame = m_File->GetFrameCount() - 1; + if (start > lastFrame) + start = lastFrame; m_FrameRangeStart = start; if (m_FrameRangeEnd < start) @@ -220,9 +234,9 @@ void FifoPlayer::SetFrameRangeEnd(u32 end) { if (m_File) { - u32 frameCount = m_File->GetFrameCount(); - if (end > frameCount) - end = frameCount; + const u32 lastFrame = m_File->GetFrameCount() - 1; + if (end > lastFrame) + end = lastFrame; m_FrameRangeEnd = end; if (m_FrameRangeStart > end) @@ -375,6 +389,8 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end) { while (IsHighWatermarkSet()) { + if (CPU::GetState() != CPU::State::Running) + break; CoreTiming::Idle(); CoreTiming::Advance(); } diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.h b/Source/Core/Core/FifoPlayer/FifoPlayer.h index 5302e8cfcb..2b9b8947d9 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.h +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.h @@ -74,7 +74,9 @@ public: bool IsPlaying() const; FifoDataFile* GetFile() const { return m_File.get(); } - u32 GetFrameObjectCount() const; + u32 GetMaxObjectCount() const; + u32 GetFrameObjectCount(u32 frame) const; + u32 GetCurrentFrameObjectCount() const; u32 GetCurrentFrameNum() const { return m_CurrentFrame; } const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; } // Frame range diff --git a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp index c9c88c6bb8..9f8a3266ac 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp @@ -17,16 +17,16 @@ void FifoRecordAnalyzer::Initialize(const u32* cpMem) { s_DrawingObject = false; - FifoAnalyzer::LoadCPReg(0x50, *(cpMem + 0x50), s_CpMem); - FifoAnalyzer::LoadCPReg(0x60, *(cpMem + 0x60), s_CpMem); - for (int i = 0; i < 8; ++i) - FifoAnalyzer::LoadCPReg(0x70 + i, *(cpMem + 0x70 + i), s_CpMem); + FifoAnalyzer::LoadCPReg(VCD_LO, cpMem[VCD_LO], s_CpMem); + FifoAnalyzer::LoadCPReg(VCD_HI, cpMem[VCD_HI], s_CpMem); + for (u32 i = 0; i < CP_NUM_VAT_REG; ++i) + FifoAnalyzer::LoadCPReg(CP_VAT_REG_A + i, cpMem[CP_VAT_REG_A + i], s_CpMem); - const u32* const bases_start = cpMem + 0xA0; + const u32* const bases_start = cpMem + ARRAY_BASE; const u32* const bases_end = bases_start + s_CpMem.arrayBases.size(); std::copy(bases_start, bases_end, s_CpMem.arrayBases.begin()); - const u32* const strides_start = cpMem + 0xB0; + const u32* const strides_start = cpMem + ARRAY_STRIDE; const u32* const strides_end = strides_start + s_CpMem.arrayStrides.size(); std::copy(strides_start, strides_end, s_CpMem.arrayStrides.begin()); } diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp index 4b9020ab8f..453a01bf81 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp +++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp @@ -25,6 +25,7 @@ #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/OpcodeDecoding.h" +#include "VideoCommon/VertexLoaderBase.h" #include "VideoCommon/XFStructs.h" constexpr int FRAME_ROLE = Qt::UserRole; @@ -81,6 +82,9 @@ void FIFOAnalyzer::CreateWidgets() m_search_previous = new QPushButton(tr("Previous Match")); m_search_label = new QLabel; + m_search_next->setEnabled(false); + m_search_previous->setEnabled(false); + auto* box_layout = new QHBoxLayout; box_layout->addWidget(m_search_edit); @@ -111,6 +115,7 @@ void FIFOAnalyzer::ConnectWidgets() connect(m_detail_list, &QListWidget::itemSelectionChanged, this, &FIFOAnalyzer::UpdateDescription); + connect(m_search_edit, &QLineEdit::returnPressed, this, &FIFOAnalyzer::BeginSearch); connect(m_search_new, &QPushButton::clicked, this, &FIFOAnalyzer::BeginSearch); connect(m_search_next, &QPushButton::clicked, this, &FIFOAnalyzer::FindNext); connect(m_search_previous, &QPushButton::clicked, this, &FIFOAnalyzer::FindPrevious); @@ -139,189 +144,277 @@ void FIFOAnalyzer::UpdateTree() auto* file = FifoPlayer::GetInstance().GetFile(); - int object_count = FifoPlayer::GetInstance().GetFrameObjectCount(); - int frame_count = file->GetFrameCount(); - - for (int i = 0; i < frame_count; i++) + const u32 frame_count = file->GetFrameCount(); + for (u32 frame = 0; frame < frame_count; frame++) { - auto* frame_item = new QTreeWidgetItem({tr("Frame %1").arg(i)}); + auto* frame_item = new QTreeWidgetItem({tr("Frame %1").arg(frame)}); recording_item->addChild(frame_item); - for (int j = 0; j < object_count; j++) + const u32 object_count = FifoPlayer::GetInstance().GetFrameObjectCount(frame); + for (u32 object = 0; object < object_count; object++) { - auto* object_item = new QTreeWidgetItem({tr("Object %1").arg(j)}); + auto* object_item = new QTreeWidgetItem({tr("Object %1").arg(object)}); frame_item->addChild(object_item); - object_item->setData(0, FRAME_ROLE, i); - object_item->setData(0, OBJECT_ROLE, j); + object_item->setData(0, FRAME_ROLE, frame); + object_item->setData(0, OBJECT_ROLE, object); } } } +static std::string GetPrimitiveName(u8 cmd) +{ + if ((cmd & 0xC0) != 0x80) + { + PanicAlertFmt("Not a primitive command: {:#04x}", cmd); + return ""; + } + const u8 vat = cmd & OpcodeDecoder::GX_VAT_MASK; // Vertex loader index (0 - 7) + const u8 primitive = + (cmd & OpcodeDecoder::GX_PRIMITIVE_MASK) >> OpcodeDecoder::GX_PRIMITIVE_SHIFT; + static constexpr std::array names = { + "GX_DRAW_QUADS", "GX_DRAW_QUADS_2 (nonstandard)", + "GX_DRAW_TRIANGLES", "GX_DRAW_TRIANGLE_STRIP", + "GX_DRAW_TRIANGLE_FAN", "GX_DRAW_LINES", + "GX_DRAW_LINE_STRIP", "GX_DRAW_POINTS", + }; + return fmt::format("{} VAT {}", names[primitive], vat); +} + void FIFOAnalyzer::UpdateDetails() { - m_detail_list->clear(); + // Clearing the detail list can update the selection, which causes UpdateDescription to be called + // immediately. However, the object data offsets have not been recalculated yet, which can cause + // the wrong data to be used, potentially leading to out of bounds data or other bad things. + // Clear m_object_data_offsets first, so that UpdateDescription exits immediately. m_object_data_offsets.clear(); + m_detail_list->clear(); + m_search_results.clear(); + m_search_next->setEnabled(false); + m_search_previous->setEnabled(false); + m_search_label->clear(); - auto items = m_tree_widget->selectedItems(); + if (!FifoPlayer::GetInstance().IsPlaying()) + return; + + const auto items = m_tree_widget->selectedItems(); if (items.isEmpty() || items[0]->data(0, OBJECT_ROLE).isNull()) return; - int frame_nr = items[0]->data(0, FRAME_ROLE).toInt(); - int object_nr = items[0]->data(0, OBJECT_ROLE).toInt(); + const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt(); + const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt(); const auto& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); const auto& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr); - const u8* objectdata_start = &fifo_frame.fifoData[frame_info.objectStarts[object_nr]]; - const u8* objectdata_end = &fifo_frame.fifoData[frame_info.objectEnds[object_nr]]; - const u8* objectdata = objectdata_start; - const std::ptrdiff_t obj_offset = - objectdata_start - &fifo_frame.fifoData[frame_info.objectStarts[0]]; + // Note that frame_info.objectStarts[object_nr] is the start of the primitive data, + // but we want to start with the register updates which happen before that. + const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]); + const u32 object_size = frame_info.objectEnds[object_nr] - object_start; - int cmd = *objectdata++; - int stream_size = Common::swap16(objectdata); - objectdata += 2; - QString new_label = QStringLiteral("%1: %2 %3 ") - .arg(obj_offset, 8, 16, QLatin1Char('0')) - .arg(cmd, 2, 16, QLatin1Char('0')) - .arg(stream_size, 4, 16, QLatin1Char('0')); - if (stream_size && ((objectdata_end - objectdata) % stream_size)) - new_label += tr("NOTE: Stream size doesn't match actual data length\n"); + const u8* const object = &fifo_frame.fifoData[object_start]; - while (objectdata < objectdata_end) - new_label += QStringLiteral("%1").arg(*objectdata++, 2, 16, QLatin1Char('0')); - - m_detail_list->addItem(new_label); - m_object_data_offsets.push_back(0); - - // Between objectdata_end and next_objdata_start, there are register setting commands - if (object_nr + 1 < static_cast(frame_info.objectStarts.size())) + u32 object_offset = 0; + while (object_offset < object_size) { - const u8* next_objdata_start = &fifo_frame.fifoData[frame_info.objectStarts[object_nr + 1]]; - while (objectdata < next_objdata_start) + QString new_label; + const u32 start_offset = object_offset; + m_object_data_offsets.push_back(start_offset); + + const u8 command = object[object_offset++]; + switch (command) { - m_object_data_offsets.push_back(objectdata - objectdata_start); - int new_offset = objectdata - &fifo_frame.fifoData[frame_info.objectStarts[0]]; - int command = *objectdata++; - switch (command) + case OpcodeDecoder::GX_NOP: + if (object[object_offset] == OpcodeDecoder::GX_NOP) + { + u32 nop_count = 2; + while (object[++object_offset] == OpcodeDecoder::GX_NOP) + nop_count++; + + new_label = QStringLiteral("NOP (%1x)").arg(nop_count); + } + else { - case OpcodeDecoder::GX_NOP: new_label = QStringLiteral("NOP"); - break; - - case OpcodeDecoder::GX_CMD_UNKNOWN_METRICS: - new_label = QStringLiteral("GX_CMD_UNKNOWN_METRICS"); - break; - - case OpcodeDecoder::GX_CMD_INVL_VC: - new_label = QStringLiteral("GX_CMD_INVL_VC"); - break; - - case OpcodeDecoder::GX_LOAD_CP_REG: - { - 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 %3") - .arg(cmd2, 2, 16, QLatin1Char('0')) - .arg(value, 8, 16, QLatin1Char('0')) - .arg(QString::fromStdString(name)); } break; - case OpcodeDecoder::GX_LOAD_XF_REG: - { - const auto [name, desc] = GetXFTransferInfo(objectdata); - u32 cmd2 = Common::swap32(objectdata); - objectdata += 4; - ASSERT(!name.empty()); - - u8 streamSize = ((cmd2 >> 16) & 15) + 1; - - const u8* stream_start = objectdata; - const u8* stream_end = stream_start + streamSize * 4; - - new_label = QStringLiteral("XF %1 ").arg(cmd2, 8, 16, QLatin1Char('0')); - while (objectdata < stream_end) - { - new_label += QStringLiteral("%1").arg(*objectdata++, 2, 16, QLatin1Char('0')); - - if (((objectdata - stream_start) % 4) == 0) - new_label += QLatin1Char(' '); - } - - new_label += QStringLiteral(" ") + QString::fromStdString(name); - } + case OpcodeDecoder::GX_CMD_UNKNOWN_METRICS: + new_label = QStringLiteral("GX_CMD_UNKNOWN_METRICS"); break; - case OpcodeDecoder::GX_LOAD_INDX_A: - case OpcodeDecoder::GX_LOAD_INDX_B: - case OpcodeDecoder::GX_LOAD_INDX_C: - case OpcodeDecoder::GX_LOAD_INDX_D: - { - objectdata += 4; - new_label = (command == OpcodeDecoder::GX_LOAD_INDX_A) ? - QStringLiteral("LOAD INDX A") : - (command == OpcodeDecoder::GX_LOAD_INDX_B) ? - QStringLiteral("LOAD INDX B") : - (command == OpcodeDecoder::GX_LOAD_INDX_C) ? QStringLiteral("LOAD INDX C") : - QStringLiteral("LOAD INDX D"); - } + case OpcodeDecoder::GX_CMD_INVL_VC: + new_label = QStringLiteral("GX_CMD_INVL_VC"); break; - case OpcodeDecoder::GX_CMD_CALL_DL: - // The recorder should have expanded display lists into the fifo stream and skipped the - // call to start them - // That is done to make it easier to track where memory is updated - ASSERT(false); - objectdata += 8; - new_label = QStringLiteral("CALL DL"); - break; + case OpcodeDecoder::GX_LOAD_CP_REG: + { + const u8 cmd2 = object[object_offset++]; + const u32 value = Common::swap32(&object[object_offset]); + object_offset += 4; - case OpcodeDecoder::GX_LOAD_BP_REG: - { - const u8 cmd2 = *objectdata++; - const u32 cmddata = Common::swap24(objectdata); - objectdata += 3; + const auto [name, desc] = GetCPRegInfo(cmd2, value); + ASSERT(!name.empty()); - const auto [name, desc] = GetBPRegInfo(cmd2, cmddata); - ASSERT(!name.empty()); - - new_label = QStringLiteral("BP %1 %2 %3") - .arg(cmd2, 2, 16, QLatin1Char('0')) - .arg(cmddata, 6, 16, QLatin1Char('0')) - .arg(QString::fromStdString(name)); - } - break; - - default: - new_label = tr("Unexpected 0x80 call? Aborting..."); - objectdata = static_cast(next_objdata_start); - break; - } - new_label = QStringLiteral("%1: ").arg(new_offset, 8, 16, QLatin1Char('0')) + new_label; - m_detail_list->addItem(new_label); + new_label = QStringLiteral("CP %1 %2 %3") + .arg(cmd2, 2, 16, QLatin1Char('0')) + .arg(value, 8, 16, QLatin1Char('0')) + .arg(QString::fromStdString(name)); } + break; + + case OpcodeDecoder::GX_LOAD_XF_REG: + { + const auto [name, desc] = GetXFTransferInfo(&object[object_offset]); + const u32 cmd2 = Common::swap32(&object[object_offset]); + object_offset += 4; + ASSERT(!name.empty()); + + const u8 stream_size = ((cmd2 >> 16) & 15) + 1; + + new_label = QStringLiteral("XF %1 ").arg(cmd2, 8, 16, QLatin1Char('0')); + + for (u8 i = 0; i < stream_size; i++) + { + const u32 value = Common::swap32(&object[object_offset]); + object_offset += 4; + + new_label += QStringLiteral("%1 ").arg(value, 8, 16, QLatin1Char('0')); + } + + new_label += QStringLiteral(" ") + QString::fromStdString(name); + } + break; + + case OpcodeDecoder::GX_LOAD_INDX_A: + { + const auto [desc, written] = + GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(&object[object_offset])); + object_offset += 4; + new_label = QStringLiteral("LOAD INDX A %1").arg(QString::fromStdString(desc)); + } + break; + case OpcodeDecoder::GX_LOAD_INDX_B: + { + const auto [desc, written] = + GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(&object[object_offset])); + object_offset += 4; + new_label = QStringLiteral("LOAD INDX B %1").arg(QString::fromStdString(desc)); + } + break; + case OpcodeDecoder::GX_LOAD_INDX_C: + { + const auto [desc, written] = + GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(&object[object_offset])); + object_offset += 4; + new_label = QStringLiteral("LOAD INDX C %1").arg(QString::fromStdString(desc)); + } + break; + case OpcodeDecoder::GX_LOAD_INDX_D: + { + const auto [desc, written] = + GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(&object[object_offset])); + object_offset += 4; + new_label = QStringLiteral("LOAD INDX D %1").arg(QString::fromStdString(desc)); + } + break; + + case OpcodeDecoder::GX_CMD_CALL_DL: + // The recorder should have expanded display lists into the fifo stream and skipped the + // call to start them + // That is done to make it easier to track where memory is updated + ASSERT(false); + object_offset += 8; + new_label = QStringLiteral("CALL DL"); + break; + + case OpcodeDecoder::GX_LOAD_BP_REG: + { + const u8 cmd2 = object[object_offset++]; + const u32 cmddata = Common::swap24(&object[object_offset]); + object_offset += 3; + + const auto [name, desc] = GetBPRegInfo(cmd2, cmddata); + ASSERT(!name.empty()); + + new_label = QStringLiteral("BP %1 %2 %3") + .arg(cmd2, 2, 16, QLatin1Char('0')) + .arg(cmddata, 6, 16, QLatin1Char('0')) + .arg(QString::fromStdString(name)); + } + break; + + default: + if ((command & 0xC0) == 0x80) + { + // Object primitive data + + const u8 vat = command & OpcodeDecoder::GX_VAT_MASK; + const auto& vtx_desc = frame_info.objectCPStates[object_nr].vtxDesc; + const auto& vtx_attr = frame_info.objectCPStates[object_nr].vtxAttr[vat]; + + const auto name = GetPrimitiveName(command); + + const u16 vertex_count = Common::swap16(&object[object_offset]); + object_offset += 2; + const u32 vertex_size = VertexLoaderBase::GetVertexSize(vtx_desc, vtx_attr); + + // Note that vertex_count is allowed to be 0, with no special treatment + // (another command just comes right after the current command, with no vertices in between) + const u32 object_prim_size = vertex_count * vertex_size; + + new_label = QStringLiteral("PRIMITIVE %1 (%2) %3 vertices %4 bytes/vertex %5 total bytes") + .arg(QString::fromStdString(name)) + .arg(command, 2, 16, QLatin1Char('0')) + .arg(vertex_count) + .arg(vertex_size) + .arg(object_prim_size); + + // It's not really useful to have a massive unreadable hex string for the object primitives. + // Put it in the description instead. + +// #define INCLUDE_HEX_IN_PRIMITIVES +#ifdef INCLUDE_HEX_IN_PRIMITIVES + new_label += QStringLiteral(" "); + for (u32 i = 0; i < object_prim_size; i++) + { + new_label += QStringLiteral("%1").arg(object[object_offset++], 2, 16, QLatin1Char('0')); + } +#else + object_offset += object_prim_size; +#endif + } + else + { + new_label = QStringLiteral("Unknown opcode %1").arg(command, 2, 16); + } + break; + } + new_label = QStringLiteral("%1: ").arg(object_start + start_offset, 8, 16, QLatin1Char('0')) + + new_label; + m_detail_list->addItem(new_label); } + + ASSERT(object_offset == object_size); + + // Needed to ensure the description updates when changing objects + m_detail_list->setCurrentRow(0); } void FIFOAnalyzer::BeginSearch() { - QString search_str = m_search_edit->text(); + const QString search_str = m_search_edit->text(); - auto items = m_tree_widget->selectedItems(); - - if (items.isEmpty() || items[0]->data(0, FRAME_ROLE).isNull()) + if (!FifoPlayer::GetInstance().IsPlaying()) return; - if (items[0]->data(0, OBJECT_ROLE).isNull()) + const auto items = m_tree_widget->selectedItems(); + + if (items.isEmpty() || items[0]->data(0, FRAME_ROLE).isNull() || + items[0]->data(0, OBJECT_ROLE).isNull()) { m_search_label->setText(tr("Invalid search parameters (no object selected)")); return; @@ -356,36 +449,35 @@ void FIFOAnalyzer::BeginSearch() m_search_results.clear(); - int frame_nr = items[0]->data(0, FRAME_ROLE).toInt(); - int object_nr = items[0]->data(0, OBJECT_ROLE).toInt(); + const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt(); + const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt(); const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); const FifoFrameInfo& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr); - // TODO: Support searching through the last object...how do we know where the cmd data ends? + const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]); + const u32 object_size = frame_info.objectEnds[object_nr] - object_start; + + const u8* const object = &fifo_frame.fifoData[object_start]; + // TODO: Support searching for bit patterns - - const auto* start_ptr = &fifo_frame.fifoData[frame_info.objectStarts[object_nr]]; - const auto* end_ptr = &fifo_frame.fifoData[frame_info.objectStarts[object_nr + 1]]; - - for (const u8* ptr = start_ptr; ptr < end_ptr - length + 1; ++ptr) + for (u32 cmd_nr = 0; cmd_nr < m_object_data_offsets.size(); cmd_nr++) { - if (std::equal(search_val.begin(), search_val.end(), ptr)) - { - SearchResult result; - result.frame = frame_nr; + const u32 cmd_start = m_object_data_offsets[cmd_nr]; + const u32 cmd_end = (cmd_nr + 1 == m_object_data_offsets.size()) ? + object_size : + m_object_data_offsets[cmd_nr + 1]; - result.object = object_nr; - result.cmd = 0; - for (unsigned int cmd_nr = 1; cmd_nr < m_object_data_offsets.size(); ++cmd_nr) + const u8* const cmd_start_ptr = &object[cmd_start]; + const u8* const cmd_end_ptr = &object[cmd_end]; + + for (const u8* ptr = cmd_start_ptr; ptr < cmd_end_ptr - length + 1; ptr++) + { + if (std::equal(search_val.begin(), search_val.end(), ptr)) { - if (ptr < start_ptr + m_object_data_offsets[cmd_nr]) - { - result.cmd = cmd_nr - 1; - break; - } + m_search_results.emplace_back(frame_nr, object_nr, cmd_nr); + break; } - m_search_results.push_back(result); } } @@ -397,41 +489,29 @@ void FIFOAnalyzer::BeginSearch() void FIFOAnalyzer::FindNext() { - int index = m_detail_list->currentRow(); + const int index = m_detail_list->currentRow(); + ASSERT(index >= 0); - if (index == -1) + auto next_result = + std::find_if(m_search_results.begin(), m_search_results.end(), + [index](auto& result) { return result.m_cmd > static_cast(index); }); + if (next_result != m_search_results.end()) { - ShowSearchResult(0); - return; - } - - for (auto it = m_search_results.begin(); it != m_search_results.end(); ++it) - { - if (it->cmd > index) - { - ShowSearchResult(it - m_search_results.begin()); - return; - } + ShowSearchResult(next_result - m_search_results.begin()); } } void FIFOAnalyzer::FindPrevious() { - int index = m_detail_list->currentRow(); + const int index = m_detail_list->currentRow(); + ASSERT(index >= 0); - if (index == -1) + auto prev_result = + std::find_if(m_search_results.rbegin(), m_search_results.rend(), + [index](auto& result) { return result.m_cmd < static_cast(index); }); + if (prev_result != m_search_results.rend()) { - ShowSearchResult(m_search_results.size() - 1); - return; - } - - for (auto it = m_search_results.rbegin(); it != m_search_results.rend(); ++it) - { - if (it->cmd < index) - { - ShowSearchResult(m_search_results.size() - 1 - (it - m_search_results.rbegin())); - return; - } + ShowSearchResult((m_search_results.rend() - prev_result) - 1); } } @@ -440,7 +520,7 @@ void FIFOAnalyzer::ShowSearchResult(size_t index) if (m_search_results.empty()) return; - if (index > m_search_results.size()) + if (index >= m_search_results.size()) { ShowSearchResult(m_search_results.size() - 1); return; @@ -449,10 +529,10 @@ void FIFOAnalyzer::ShowSearchResult(size_t index) const auto& result = m_search_results[index]; QTreeWidgetItem* object_item = - m_tree_widget->topLevelItem(0)->child(result.frame)->child(result.object); + m_tree_widget->topLevelItem(0)->child(result.m_frame)->child(result.m_object); m_tree_widget->setCurrentItem(object_item); - m_detail_list->setCurrentRow(result.cmd); + m_detail_list->setCurrentRow(result.m_cmd); m_search_next->setEnabled(index + 1 < m_search_results.size()); m_search_previous->setEnabled(index > 0); @@ -462,20 +542,28 @@ void FIFOAnalyzer::UpdateDescription() { m_entry_detail_browser->clear(); - auto items = m_tree_widget->selectedItems(); - - if (items.isEmpty()) + if (!FifoPlayer::GetInstance().IsPlaying()) return; - int frame_nr = items[0]->data(0, FRAME_ROLE).toInt(); - int object_nr = items[0]->data(0, OBJECT_ROLE).toInt(); - int entry_nr = m_detail_list->currentRow(); + const auto items = m_tree_widget->selectedItems(); - const AnalyzedFrameInfo& frame = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); + if (items.isEmpty() || m_object_data_offsets.empty()) + return; + + if (items[0]->data(0, FRAME_ROLE).isNull() || items[0]->data(0, OBJECT_ROLE).isNull()) + return; + + const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt(); + const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt(); + const u32 entry_nr = m_detail_list->currentRow(); + + const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); const FifoFrameInfo& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr); - const u8* cmddata = - &fifo_frame.fifoData[frame.objectStarts[object_nr]] + m_object_data_offsets[entry_nr]; + const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]); + const u32 entry_start = m_object_data_offsets[entry_nr]; + + const u8* cmddata = &fifo_frame.fifoData[object_start + entry_start]; // TODO: Not sure whether we should bother translating the descriptions @@ -528,6 +616,74 @@ void FIFOAnalyzer::UpdateDescription() else text += QString::fromStdString(desc); } + else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_A) + { + const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(cmddata + 1)); + + text = QString::fromStdString(desc); + text += QLatin1Char{'\n'}; + text += tr("Usually used for position matrices"); + text += QLatin1Char{'\n'}; + text += QString::fromStdString(written); + } + else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_B) + { + const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(cmddata + 1)); + + text = QString::fromStdString(desc); + text += QLatin1Char{'\n'}; + text += tr("Usually used for normal matrices"); + text += QLatin1Char{'\n'}; + text += QString::fromStdString(written); + } + else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_C) + { + const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(cmddata + 1)); + + text = QString::fromStdString(desc); + text += QLatin1Char{'\n'}; + text += tr("Usually used for tex coord matrices"); + text += QLatin1Char{'\n'}; + text += QString::fromStdString(written); + } + else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_D) + { + const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(cmddata + 1)); + + text = QString::fromStdString(desc); + text += QLatin1Char{'\n'}; + text += tr("Usually used for light objects"); + text += QLatin1Char{'\n'}; + text += QString::fromStdString(written); + } + else if ((*cmddata & 0xC0) == 0x80) + { + const u8 vat = *cmddata & OpcodeDecoder::GX_VAT_MASK; + const auto name = GetPrimitiveName(*cmddata); + const u16 vertex_count = Common::swap16(cmddata + 1); + + text = tr("Primitive "); + text += QString::fromStdString(name); + text += QLatin1Char{'\n'}; + + const auto& vtx_desc = frame_info.objectCPStates[object_nr].vtxDesc; + const auto& vtx_attr = frame_info.objectCPStates[object_nr].vtxAttr[vat]; + const auto component_sizes = VertexLoaderBase::GetVertexComponentSizes(vtx_desc, vtx_attr); + + u32 i = 3; + for (u32 vertex_num = 0; vertex_num < vertex_count; vertex_num++) + { + text += QLatin1Char{'\n'}; + for (u32 comp_size : component_sizes) + { + for (u32 comp_off = 0; comp_off < comp_size; comp_off++) + { + text += QStringLiteral("%1").arg(cmddata[i++], 2, 16, QLatin1Char('0')); + } + text += QLatin1Char{' '}; + } + } + } else { text = tr("No description available"); diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h index f31ddde2a9..dba746cdb6 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h +++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h @@ -8,6 +8,8 @@ #include +#include "Common/CommonTypes.h" + class QGroupBox; class QLabel; class QLineEdit; @@ -57,9 +59,13 @@ private: struct SearchResult { - int frame; - int object; - int cmd; + constexpr SearchResult(u32 frame, u32 object, u32 cmd) + : m_frame(frame), m_object(object), m_cmd(cmd) + { + } + const u32 m_frame; + const u32 m_object; + const u32 m_cmd; }; std::vector m_object_data_offsets; diff --git a/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.cpp b/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.cpp index ae351792fe..dc1b4f44b7 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.cpp +++ b/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.cpp @@ -6,9 +6,13 @@ #include #include +#include #include #include #include +#include +#include +#include #include #include #include @@ -26,12 +30,13 @@ #include "DolphinQt/FIFO/FIFOAnalyzer.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/QtUtils/QueueOnObject.h" +#include "DolphinQt/Resources.h" #include "DolphinQt/Settings.h" -FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QDialog(parent) +FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QWidget(parent) { setWindowTitle(tr("FIFO Player")); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowIcon(Resources::GetAppIcon()); CreateWidgets(); ConnectWidgets(); @@ -50,11 +55,14 @@ FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QDialog(parent) }); connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { - if (state == Core::State::Running) + if (state == Core::State::Running && m_emu_state != Core::State::Paused) OnEmulationStarted(); else if (state == Core::State::Uninitialized) OnEmulationStopped(); + m_emu_state = state; }); + + installEventFilter(this); } FIFOPlayerWindow::~FIFOPlayerWindow() @@ -166,7 +174,7 @@ void FIFOPlayerWindow::ConnectWidgets() connect(m_save, &QPushButton::clicked, this, &FIFOPlayerWindow::SaveRecording); connect(m_record, &QPushButton::clicked, this, &FIFOPlayerWindow::StartRecording); connect(m_stop, &QPushButton::clicked, this, &FIFOPlayerWindow::StopRecording); - connect(m_button_box, &QDialogButtonBox::rejected, this, &FIFOPlayerWindow::reject); + connect(m_button_box, &QDialogButtonBox::rejected, this, &FIFOPlayerWindow::hide); connect(m_early_memory_updates, &QCheckBox::toggled, this, &FIFOPlayerWindow::OnEarlyMemoryUpdatesChanged); connect(m_frame_range_from, qOverload(&QSpinBox::valueChanged), this, @@ -244,6 +252,7 @@ void FIFOPlayerWindow::OnEmulationStopped() StopRecording(); UpdateControls(); + m_analyzer->Update(); } void FIFOPlayerWindow::OnRecordingDone() @@ -260,7 +269,7 @@ void FIFOPlayerWindow::UpdateInfo() m_info_label->setText( tr("%1 frame(s)\n%2 object(s)\nCurrent Frame: %3") .arg(QString::number(file->GetFrameCount()), - QString::number(FifoPlayer::GetInstance().GetFrameObjectCount()), + QString::number(FifoPlayer::GetInstance().GetCurrentFrameObjectCount()), QString::number(FifoPlayer::GetInstance().GetCurrentFrameNum()))); return; } @@ -297,14 +306,16 @@ void FIFOPlayerWindow::OnFIFOLoaded() { FifoDataFile* file = FifoPlayer::GetInstance().GetFile(); - auto object_count = FifoPlayer::GetInstance().GetFrameObjectCount(); + auto object_count = FifoPlayer::GetInstance().GetMaxObjectCount(); auto frame_count = file->GetFrameCount(); - m_frame_range_to->setMaximum(frame_count); - m_object_range_to->setMaximum(object_count); + m_frame_range_to->setMaximum(frame_count - 1); + m_object_range_to->setMaximum(object_count - 1); - m_frame_range_to->setValue(frame_count); - m_object_range_to->setValue(object_count); + m_frame_range_from->setValue(0); + m_object_range_from->setValue(0); + m_frame_range_to->setValue(frame_count - 1); + m_object_range_to->setValue(object_count - 1); UpdateInfo(); UpdateLimits(); @@ -331,10 +342,10 @@ void FIFOPlayerWindow::OnLimitsChanged() void FIFOPlayerWindow::UpdateLimits() { - m_frame_range_from->setMaximum(std::max(m_frame_range_to->value() - 1, 0)); - m_frame_range_to->setMinimum(m_frame_range_from->value() + 1); - m_object_range_from->setMaximum(std::max(m_object_range_to->value() - 1, 0)); - m_object_range_to->setMinimum(m_object_range_from->value() + 1); + m_frame_range_from->setMaximum(m_frame_range_to->value()); + m_frame_range_to->setMinimum(m_frame_range_from->value()); + m_object_range_from->setMaximum(m_object_range_to->value()); + m_object_range_to->setMinimum(m_object_range_from->value()); } void FIFOPlayerWindow::UpdateControls() @@ -367,3 +378,15 @@ void FIFOPlayerWindow::UpdateControls() m_save->setEnabled(FifoRecorder::GetInstance().IsRecordingDone()); } + +bool FIFOPlayerWindow::eventFilter(QObject* object, QEvent* event) +{ + // Close when escape is pressed + if (event->type() == QEvent::KeyPress) + { + if (static_cast(event)->matches(QKeySequence::Cancel)) + hide(); + } + + return false; +} diff --git a/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.h b/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.h index 5441b38aba..d6bc8b3600 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.h +++ b/Source/Core/DolphinQt/FIFO/FIFOPlayerWindow.h @@ -4,7 +4,9 @@ #pragma once -#include +#include + +#include "Core/Core.h" class QCheckBox; class QDialogButtonBox; @@ -13,7 +15,7 @@ class QPushButton; class QSpinBox; class FIFOAnalyzer; -class FIFOPlayerWindow : public QDialog +class FIFOPlayerWindow : public QWidget { Q_OBJECT public: @@ -43,6 +45,8 @@ private: void UpdateInfo(); void UpdateLimits(); + bool eventFilter(QObject* object, QEvent* event) final override; + QLabel* m_info_label; QPushButton* m_load; QPushButton* m_save; @@ -62,4 +66,5 @@ private: QDialogButtonBox* m_button_box; FIFOAnalyzer* m_analyzer; + Core::State m_emu_state = Core::State::Uninitialized; }; diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 3261a4b740..61e58eb19f 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1214,7 +1214,7 @@ void MainWindow::ShowFIFOPlayer() { if (!m_fifo_window) { - m_fifo_window = new FIFOPlayerWindow(this); + m_fifo_window = new FIFOPlayerWindow; connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this, [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); } diff --git a/Source/Core/VideoCommon/CPMemory.h b/Source/Core/VideoCommon/CPMemory.h index f54445940f..488d210129 100644 --- a/Source/Core/VideoCommon/CPMemory.h +++ b/Source/Core/VideoCommon/CPMemory.h @@ -63,6 +63,11 @@ enum ARRAY_TEXCOORD0 = 4, NUM_TEXCOORD_ARRAYS = 8, + ARRAY_XF_A = 12, // Usually used for position matrices + ARRAY_XF_B = 13, // Usually used for normal matrices + ARRAY_XF_C = 14, // Usually used for tex coord matrices + ARRAY_XF_D = 15, // Usually used for light objects + // Number of arrays related to vertex components (position, normal, color, tex coord) // Excludes the 4 arrays used for indexed XF loads NUM_VERTEX_COMPONENT_ARRAYS = 12, @@ -317,10 +322,10 @@ union UVAT_group0 // 21:29 BitField<21, 1, TexComponentCount> Tex0CoordElements; BitField<22, 3, ComponentFormat> Tex0CoordFormat; - BitField<25, 5, u32> Tex0Frac; + BitField<25, 5, u8, u32> Tex0Frac; // 30:31 - BitField<30, 1, u32> ByteDequant; - BitField<31, 1, u32> NormalIndex3; + BitField<30, 1, bool, u32> ByteDequant; + BitField<31, 1, bool, u32> NormalIndex3; }; template <> struct fmt::formatter @@ -363,20 +368,20 @@ union UVAT_group1 // 0:8 BitField<0, 1, TexComponentCount> Tex1CoordElements; BitField<1, 3, ComponentFormat> Tex1CoordFormat; - BitField<4, 5, u32> Tex1Frac; + BitField<4, 5, u8, u32> Tex1Frac; // 9:17 BitField<9, 1, TexComponentCount> Tex2CoordElements; BitField<10, 3, ComponentFormat> Tex2CoordFormat; - BitField<13, 5, u32> Tex2Frac; + BitField<13, 5, u8, u32> Tex2Frac; // 18:26 BitField<18, 1, TexComponentCount> Tex3CoordElements; BitField<19, 3, ComponentFormat> Tex3CoordFormat; - BitField<22, 5, u32> Tex3Frac; + BitField<22, 5, u8, u32> Tex3Frac; // 27:30 BitField<27, 1, TexComponentCount> Tex4CoordElements; BitField<28, 3, ComponentFormat> Tex4CoordFormat; // 31 - BitField<31, 1, u32> VCacheEnhance; + BitField<31, 1, bool, u32> VCacheEnhance; }; template <> struct fmt::formatter @@ -410,19 +415,19 @@ union UVAT_group2 { u32 Hex; // 0:4 - BitField<0, 5, u32> Tex4Frac; + BitField<0, 5, u8, u32> Tex4Frac; // 5:13 BitField<5, 1, TexComponentCount> Tex5CoordElements; BitField<6, 3, ComponentFormat> Tex5CoordFormat; - BitField<9, 5, u32> Tex5Frac; + BitField<9, 5, u8, u32> Tex5Frac; // 14:22 BitField<14, 1, TexComponentCount> Tex6CoordElements; BitField<15, 3, ComponentFormat> Tex6CoordFormat; - BitField<18, 5, u32> Tex6Frac; + BitField<18, 5, u8, u32> Tex6Frac; // 23:31 BitField<23, 1, TexComponentCount> Tex7CoordElements; BitField<24, 3, ComponentFormat> Tex7CoordFormat; - BitField<27, 5, u32> Tex7Frac; + BitField<27, 5, u8, u32> Tex7Frac; }; template <> struct fmt::formatter @@ -450,30 +455,123 @@ struct fmt::formatter } }; -struct ColorAttr +struct VAT { - ColorComponentCount Elements; - ColorFormat Comp; -}; + UVAT_group0 g0; + UVAT_group1 g1; + UVAT_group2 g2; -struct TexAttr -{ - TexComponentCount Elements; - ComponentFormat Format; - u8 Frac; + constexpr ColorComponentCount GetColorElements(size_t idx) const + { + switch (idx) + { + case 0: + return g0.Color0Elements; + case 1: + return g0.Color1Elements; + default: + PanicAlertFmt("Invalid color index {}", idx); + return ColorComponentCount::RGB; + } + } + constexpr ColorFormat GetColorFormat(size_t idx) const + { + switch (idx) + { + case 0: + return g0.Color0Comp; + case 1: + return g0.Color1Comp; + default: + PanicAlertFmt("Invalid color index {}", idx); + return ColorFormat::RGB565; + } + } + constexpr TexComponentCount GetTexElements(size_t idx) const + { + switch (idx) + { + case 0: + return g0.Tex0CoordElements; + case 1: + return g1.Tex1CoordElements; + case 2: + return g1.Tex2CoordElements; + case 3: + return g1.Tex3CoordElements; + case 4: + return g1.Tex4CoordElements; + case 5: + return g2.Tex5CoordElements; + case 6: + return g2.Tex6CoordElements; + case 7: + return g2.Tex7CoordElements; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + return TexComponentCount::S; + } + } + constexpr ComponentFormat GetTexFormat(size_t idx) const + { + switch (idx) + { + case 0: + return g0.Tex0CoordFormat; + case 1: + return g1.Tex1CoordFormat; + case 2: + return g1.Tex2CoordFormat; + case 3: + return g1.Tex3CoordFormat; + case 4: + return g1.Tex4CoordFormat; + case 5: + return g2.Tex5CoordFormat; + case 6: + return g2.Tex6CoordFormat; + case 7: + return g2.Tex7CoordFormat; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + return ComponentFormat::UByte; + } + } + constexpr u8 GetTexFrac(size_t idx) const + { + switch (idx) + { + case 0: + return g0.Tex0Frac; + case 1: + return g1.Tex1Frac; + case 2: + return g1.Tex2Frac; + case 3: + return g1.Tex3Frac; + case 4: + return g2.Tex4Frac; + case 5: + return g2.Tex5Frac; + case 6: + return g2.Tex6Frac; + case 7: + return g2.Tex7Frac; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + return 0; + } + } }; - -struct TVtxAttr +template <> +struct fmt::formatter { - CoordComponentCount PosElements; - ComponentFormat PosFormat; - u8 PosFrac; - NormalComponentCount NormalElements; - ComponentFormat NormalFormat; - ColorAttr color[2]; - TexAttr texCoord[8]; - bool ByteDequant; - u8 NormalIndex3; + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const VAT& vat, FormatContext& ctx) + { + return format_to(ctx.out(), "{}\n{}\n{}", vat.g0, vat.g1, vat.g2); + } }; // Matrix indices @@ -518,13 +616,6 @@ struct fmt::formatter } }; -struct VAT -{ - UVAT_group0 g0; - UVAT_group1 g1; - UVAT_group2 g2; -}; - class VertexLoaderBase; // STATE_TO_SAVE diff --git a/Source/Core/VideoCommon/VertexLoader.cpp b/Source/Core/VideoCommon/VertexLoader.cpp index f06b60a430..0d49ef1a4b 100644 --- a/Source/Core/VideoCommon/VertexLoader.cpp +++ b/Source/Core/VideoCommon/VertexLoader.cpp @@ -72,21 +72,16 @@ VertexLoader::VertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr) CompileVertexTranslator(); // generate frac factors - m_posScale = 1.0f / (1U << m_VtxAttr.PosFrac); - for (int i = 0; i < 8; i++) - m_tcScale[i] = 1.0f / (1U << m_VtxAttr.texCoord[i].Frac); + m_posScale = 1.0f / (1U << m_VtxAttr.g0.PosFrac); + for (u32 i = 0; i < 8; i++) + m_tcScale[i] = 1.0f / (1U << m_VtxAttr.GetTexFrac(i)); } void VertexLoader::CompileVertexTranslator() { - m_VertexSize = 0; - const TVtxAttr& vtx_attr = m_VtxAttr; - // Reset pipeline m_numPipelineStages = 0; - u32 components = 0; - // Position in pc vertex format. int nat_offset = 0; @@ -94,72 +89,25 @@ void VertexLoader::CompileVertexTranslator() if (m_VtxDesc.low.PosMatIdx) { WriteCall(PosMtx_ReadDirect_UByte); - components |= VB_HAS_POSMTXIDX; m_native_vtx_decl.posmtx.components = 4; m_native_vtx_decl.posmtx.enable = true; m_native_vtx_decl.posmtx.offset = nat_offset; m_native_vtx_decl.posmtx.type = VAR_UNSIGNED_BYTE; m_native_vtx_decl.posmtx.integer = true; nat_offset += 4; - m_VertexSize += 1; } - if (m_VtxDesc.low.Tex0MatIdx) + for (auto texmtxidx : m_VtxDesc.low.TexMatIdx) { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX0; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex1MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX1; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex2MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX2; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex3MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX3; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex4MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX4; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex5MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX5; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex6MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX6; - WriteCall(TexMtx_ReadDirect_UByte); - } - if (m_VtxDesc.low.Tex7MatIdx) - { - m_VertexSize += 1; - components |= VB_HAS_TEXMTXIDX7; - WriteCall(TexMtx_ReadDirect_UByte); + if (texmtxidx) + WriteCall(TexMtx_ReadDirect_UByte); } // Write vertex position loader - WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.low.Position, m_VtxAttr.PosFormat, - m_VtxAttr.PosElements)); + WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, + m_VtxAttr.g0.PosElements)); - m_VertexSize += VertexLoader_Position::GetSize(m_VtxDesc.low.Position, m_VtxAttr.PosFormat, - m_VtxAttr.PosElements); - int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3; + int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3; m_native_vtx_decl.position.components = pos_elements; m_native_vtx_decl.position.enable = true; m_native_vtx_decl.position.offset = nat_offset; @@ -170,22 +118,19 @@ void VertexLoader::CompileVertexTranslator() // Normals if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent) { - m_VertexSize += VertexLoader_Normal::GetSize(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, - m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3); - TPipelineFunction pFunc = - VertexLoader_Normal::GetFunction(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, - m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3); + VertexLoader_Normal::GetFunction(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, + m_VtxAttr.g0.NormalElements, m_VtxAttr.g0.NormalIndex3); if (pFunc == nullptr) { PanicAlertFmt("VertexLoader_Normal::GetFunction({} {} {} {}) returned zero!", - m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, m_VtxAttr.NormalElements, - m_VtxAttr.NormalIndex3); + m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, m_VtxAttr.g0.NormalElements, + m_VtxAttr.g0.NormalIndex3); } WriteCall(pFunc); - for (int i = 0; i < (vtx_attr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++) + for (int i = 0; i < (m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++) { m_native_vtx_decl.normals[i].components = 3; m_native_vtx_decl.normals[i].enable = true; @@ -194,10 +139,6 @@ void VertexLoader::CompileVertexTranslator() m_native_vtx_decl.normals[i].integer = false; nat_offset += 12; } - - components |= VB_HAS_NRM0; - if (m_VtxAttr.NormalElements == NormalComponentCount::NBT) - components |= VB_HAS_NRM1 | VB_HAS_NRM2; } for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++) @@ -205,101 +146,17 @@ void VertexLoader::CompileVertexTranslator() m_native_vtx_decl.colors[i].components = 4; m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE; m_native_vtx_decl.colors[i].integer = false; - switch (m_VtxDesc.low.Color[i]) - { - case VertexComponentFormat::NotPresent: - break; - case VertexComponentFormat::Direct: - switch (m_VtxAttr.color[i].Comp) - { - case ColorFormat::RGB565: - m_VertexSize += 2; - WriteCall(Color_ReadDirect_16b_565); - break; - case ColorFormat::RGB888: - m_VertexSize += 3; - WriteCall(Color_ReadDirect_24b_888); - break; - case ColorFormat::RGB888x: - m_VertexSize += 4; - WriteCall(Color_ReadDirect_32b_888x); - break; - case ColorFormat::RGBA4444: - m_VertexSize += 2; - WriteCall(Color_ReadDirect_16b_4444); - break; - case ColorFormat::RGBA6666: - m_VertexSize += 3; - WriteCall(Color_ReadDirect_24b_6666); - break; - case ColorFormat::RGBA8888: - m_VertexSize += 4; - WriteCall(Color_ReadDirect_32b_8888); - break; - default: - ASSERT(0); - break; - } - break; - case VertexComponentFormat::Index8: - m_VertexSize += 1; - switch (m_VtxAttr.color[i].Comp) - { - case ColorFormat::RGB565: - WriteCall(Color_ReadIndex8_16b_565); - break; - case ColorFormat::RGB888: - WriteCall(Color_ReadIndex8_24b_888); - break; - case ColorFormat::RGB888x: - WriteCall(Color_ReadIndex8_32b_888x); - break; - case ColorFormat::RGBA4444: - WriteCall(Color_ReadIndex8_16b_4444); - break; - case ColorFormat::RGBA6666: - WriteCall(Color_ReadIndex8_24b_6666); - break; - case ColorFormat::RGBA8888: - WriteCall(Color_ReadIndex8_32b_8888); - break; - default: - ASSERT(0); - break; - } - break; - case VertexComponentFormat::Index16: - m_VertexSize += 2; - switch (m_VtxAttr.color[i].Comp) - { - case ColorFormat::RGB565: - WriteCall(Color_ReadIndex16_16b_565); - break; - case ColorFormat::RGB888: - WriteCall(Color_ReadIndex16_24b_888); - break; - case ColorFormat::RGB888x: - WriteCall(Color_ReadIndex16_32b_888x); - break; - case ColorFormat::RGBA4444: - WriteCall(Color_ReadIndex16_16b_4444); - break; - case ColorFormat::RGBA6666: - WriteCall(Color_ReadIndex16_24b_6666); - break; - case ColorFormat::RGBA8888: - WriteCall(Color_ReadIndex16_32b_8888); - break; - default: - ASSERT(0); - break; - } - break; - } - // Common for the three bottom cases + + TPipelineFunction pFunc = + VertexLoader_Color::GetFunction(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i)); + + if (pFunc != nullptr) + WriteCall(pFunc); + else + ASSERT(m_VtxDesc.low.Color[i] == VertexComponentFormat::NotPresent); + if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent) { - components |= VB_HAS_COL0 << i; m_native_vtx_decl.colors[i].offset = nat_offset; m_native_vtx_decl.colors[i].enable = true; nat_offset += 4; @@ -314,8 +171,8 @@ void VertexLoader::CompileVertexTranslator() m_native_vtx_decl.texcoords[i].integer = false; const auto tc = m_VtxDesc.high.TexCoord[i].Value(); - const auto format = m_VtxAttr.texCoord[i].Format; - const auto elements = m_VtxAttr.texCoord[i].Elements; + const auto format = m_VtxAttr.GetTexFormat(i); + const auto elements = m_VtxAttr.GetTexElements(i); if (tc != VertexComponentFormat::NotPresent) { @@ -326,26 +183,21 @@ void VertexLoader::CompileVertexTranslator() ASSERT_MSG(VIDEO, elements == TexComponentCount::S || elements == TexComponentCount::ST, "Invalid number of texture coordinates elements!\n(elements = %d)", (u32)elements); - components |= VB_HAS_UV0 << i; WriteCall(VertexLoader_TextCoord::GetFunction(tc, format, elements)); - m_VertexSize += VertexLoader_TextCoord::GetSize(tc, format, elements); } - if (components & (VB_HAS_TEXMTXIDX0 << i)) + if (m_VtxDesc.low.TexMatIdx[i]) { m_native_vtx_decl.texcoords[i].enable = true; + m_native_vtx_decl.texcoords[i].components = 3; + nat_offset += 12; if (tc != VertexComponentFormat::NotPresent) { // if texmtx is included, texcoord will always be 3 floats, z will be the texmtx index - m_native_vtx_decl.texcoords[i].components = 3; - nat_offset += 12; - WriteCall(m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? TexMtx_Write_Float : - TexMtx_Write_Float2); + WriteCall(elements == TexComponentCount::ST ? TexMtx_Write_Float : TexMtx_Write_Float2); } else { - m_native_vtx_decl.texcoords[i].components = 3; - nat_offset += 12; WriteCall(TexMtx_Write_Float3); } } @@ -354,26 +206,29 @@ void VertexLoader::CompileVertexTranslator() if (tc != VertexComponentFormat::NotPresent) { m_native_vtx_decl.texcoords[i].enable = true; - m_native_vtx_decl.texcoords[i].components = - vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1; - nat_offset += 4 * (vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1); + m_native_vtx_decl.texcoords[i].components = elements == TexComponentCount::ST ? 2 : 1; + nat_offset += 4 * (elements == TexComponentCount::ST ? 2 : 1); } } if (tc == VertexComponentFormat::NotPresent) { // if there's more tex coords later, have to write a dummy call - size_t j = i + 1; - for (; j < m_VtxDesc.high.TexCoord.Size(); ++j) + bool has_more = false; + for (size_t j = 0; j < m_VtxDesc.high.TexCoord.Size(); ++j) { if (m_VtxDesc.high.TexCoord[j] != VertexComponentFormat::NotPresent) { + has_more = true; WriteCall(VertexLoader_TextCoord::GetDummyFunction()); // important to get indices right! break; } + else if (m_VtxDesc.low.TexMatIdx[i]) + { + has_more = true; + } } - // tricky! - if (j == 8 && !((components & VB_HAS_TEXMTXIDXALL) & (VB_HAS_TEXMTXIDXALL << (i + 1)))) + if (!has_more) { // no more tex coords and tex matrices, so exit loop break; @@ -387,7 +242,6 @@ void VertexLoader::CompileVertexTranslator() WriteCall(SkipVertex); } - m_native_components = components; m_native_vtx_decl.stride = nat_offset; } diff --git a/Source/Core/VideoCommon/VertexLoader.h b/Source/Core/VideoCommon/VertexLoader.h index 2caae086d3..4be2f41a65 100644 --- a/Source/Core/VideoCommon/VertexLoader.h +++ b/Source/Core/VideoCommon/VertexLoader.h @@ -22,8 +22,6 @@ public: VertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr); int RunVertices(DataReader src, DataReader dst, int count) override; - std::string GetName() const override { return "OldLoader"; } - bool IsInitialized() override { return true; } // This vertex loader supports all formats // They are used for the communication with the loader functions float m_posScale; float m_tcScale[8]; diff --git a/Source/Core/VideoCommon/VertexLoaderARM64.cpp b/Source/Core/VideoCommon/VertexLoaderARM64.cpp index 94c821a26b..e546c42be8 100644 --- a/Source/Core/VideoCommon/VertexLoaderARM64.cpp +++ b/Source/Core/VideoCommon/VertexLoaderARM64.cpp @@ -25,6 +25,20 @@ constexpr ARM64Reg stride_reg = ARM64Reg::X11; constexpr ARM64Reg arraybase_reg = ARM64Reg::X10; constexpr ARM64Reg scale_reg = ARM64Reg::X9; +static constexpr int GetLoadSize(int load_bytes) +{ + if (load_bytes == 1) + return 1; + else if (load_bytes <= 2) + return 2; + else if (load_bytes <= 4) + return 4; + else if (load_bytes <= 8) + return 8; + else + return 16; +} + alignas(16) static const float scale_factors[] = { 1.0 / (1ULL << 0), 1.0 / (1ULL << 1), 1.0 / (1ULL << 2), 1.0 / (1ULL << 3), 1.0 / (1ULL << 4), 1.0 / (1ULL << 5), 1.0 / (1ULL << 6), 1.0 / (1ULL << 7), @@ -39,9 +53,6 @@ alignas(16) static const float scale_factors[] = { VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_att) : VertexLoaderBase(vtx_desc, vtx_att), m_float_emit(this) { - if (!IsInitialized()) - return; - AllocCodeSpace(4096); ClearCodeSpace(); GenerateVertexLoader(); @@ -120,8 +131,7 @@ int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentForm int elem_size = GetElementSize(format); int load_bytes = elem_size * count_in; - int load_size = - load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16; + int load_size = GetLoadSize(load_bytes); load_size <<= 3; elem_size <<= 3; @@ -380,10 +390,11 @@ void VertexLoaderARM64::GenerateVertexLoader() for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++) { has_tc |= m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent; - has_tc_scale |= !!m_VtxAttr.texCoord[i].Frac; + has_tc_scale |= (m_VtxAttr.GetTexFrac(i) != 0); } - bool need_scale = (m_VtxAttr.ByteDequant && m_VtxAttr.PosFrac) || (has_tc && has_tc_scale) || + bool need_scale = (m_VtxAttr.g0.ByteDequant && m_VtxAttr.g0.PosFrac) || + (has_tc && has_tc_scale) || (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent); AlignCode16(); @@ -412,7 +423,6 @@ void VertexLoaderARM64::GenerateVertexLoader() STR(IndexType::Unsigned, scratch1_reg, EncodeRegTo64(scratch2_reg), 0); SetJumpTarget(dont_store); - m_native_components |= VB_HAS_POSMTXIDX; m_native_vtx_decl.posmtx.components = 4; m_native_vtx_decl.posmtx.enable = true; m_native_vtx_decl.posmtx.offset = m_dst_ofs; @@ -431,36 +441,33 @@ void VertexLoaderARM64::GenerateVertexLoader() // Position { - int elem_size = GetElementSize(m_VtxAttr.PosFormat); - int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3; + int elem_size = GetElementSize(m_VtxAttr.g0.PosFormat); + int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3; int load_bytes = elem_size * pos_elements; - int load_size = - load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16; + int load_size = GetLoadSize(load_bytes); load_size <<= 3; s32 offset = GetAddressImm(ARRAY_POSITION, m_VtxDesc.low.Position, EncodeRegTo64(scratch1_reg), load_size); - ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements, - m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position, offset); + ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements, + m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position, offset); } if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent) { static const u8 map[8] = {7, 6, 15, 14}; - const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)]; - const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1; + const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())]; + const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1; s32 offset = -1; - for (int i = 0; i < (m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++) + for (int i = 0; i < (m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++) { - if (!i || m_VtxAttr.NormalIndex3) + if (!i || m_VtxAttr.g0.NormalIndex3) { - int elem_size = GetElementSize(m_VtxAttr.NormalFormat); + int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat); int load_bytes = elem_size * 3; - int load_size = load_bytes == 1 ? - 1 : - load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16; + int load_size = GetLoadSize(load_bytes); offset = GetAddressImm(ARRAY_NORMAL, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg), load_size << 3); @@ -470,7 +477,7 @@ void VertexLoaderARM64::GenerateVertexLoader() else offset += i * elem_size * 3; } - int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true, + int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true, scaling_exponent, &m_native_vtx_decl.normals[i], offset); if (offset == -1) @@ -478,10 +485,6 @@ void VertexLoaderARM64::GenerateVertexLoader() else offset += bytes_read; } - - m_native_components |= VB_HAS_NRM0; - if (m_VtxAttr.NormalElements == NormalComponentCount::NBT) - m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2; } for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++) @@ -493,14 +496,13 @@ void VertexLoaderARM64::GenerateVertexLoader() if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent) { u32 align = 4; - if (m_VtxAttr.color[i].Comp == ColorFormat::RGB565 || - m_VtxAttr.color[i].Comp == ColorFormat::RGBA4444) + if (m_VtxAttr.GetColorFormat(i) == ColorFormat::RGB565 || + m_VtxAttr.GetColorFormat(i) == ColorFormat::RGBA4444) align = 2; s32 offset = GetAddressImm(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i], EncodeRegTo64(scratch1_reg), align); - ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp, offset); - m_native_components |= VB_HAS_COL0 << i; + ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i), offset); m_native_vtx_decl.colors[i].components = 4; m_native_vtx_decl.colors[i].enable = true; m_native_vtx_decl.colors[i].offset = m_dst_ofs; @@ -516,28 +518,23 @@ void VertexLoaderARM64::GenerateVertexLoader() m_native_vtx_decl.texcoords[i].type = VAR_FLOAT; m_native_vtx_decl.texcoords[i].integer = false; - int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::S ? 1 : 2; + int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::S ? 1 : 2; if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent) { - m_native_components |= VB_HAS_UV0 << i; - - int elem_size = GetElementSize(m_VtxAttr.texCoord[i].Format); + int elem_size = GetElementSize(m_VtxAttr.GetTexFormat(i)); int load_bytes = elem_size * (elements + 2); - int load_size = load_bytes == 1 ? - 1 : - load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16; + int load_size = GetLoadSize(load_bytes); load_size <<= 3; s32 offset = GetAddressImm(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i], EncodeRegTo64(scratch1_reg), load_size); - u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac; - ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements, - m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent, - &m_native_vtx_decl.texcoords[i], offset); + u8 scaling_exponent = m_VtxAttr.GetTexFrac(i); + ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements, + m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant, + scaling_exponent, &m_native_vtx_decl.texcoords[i], offset); } if (m_VtxDesc.low.TexMatIdx[i]) { - m_native_components |= VB_HAS_TEXMTXIDX0 << i; m_native_vtx_decl.texcoords[i].components = 3; m_native_vtx_decl.texcoords[i].enable = true; m_native_vtx_decl.texcoords[i].type = VAR_FLOAT; @@ -603,7 +600,7 @@ void VertexLoaderARM64::GenerateVertexLoader() FlushIcache(); - m_VertexSize = m_src_ofs; + ASSERT(m_vertex_size == m_src_ofs); m_native_vtx_decl.stride = m_dst_ofs; } diff --git a/Source/Core/VideoCommon/VertexLoaderARM64.h b/Source/Core/VideoCommon/VertexLoaderARM64.h index b7faec0239..2b2787709b 100644 --- a/Source/Core/VideoCommon/VertexLoaderARM64.h +++ b/Source/Core/VideoCommon/VertexLoaderARM64.h @@ -19,8 +19,6 @@ public: VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_att); protected: - std::string GetName() const override { return "VertexLoaderARM64"; } - bool IsInitialized() override { return true; } int RunVertices(DataReader src, DataReader dst, int count) override; private: diff --git a/Source/Core/VideoCommon/VertexLoaderBase.cpp b/Source/Core/VideoCommon/VertexLoaderBase.cpp index 483d46dd08..b920b16a9d 100644 --- a/Source/Core/VideoCommon/VertexLoaderBase.cpp +++ b/Source/Core/VideoCommon/VertexLoaderBase.cpp @@ -19,6 +19,10 @@ #include "VideoCommon/DataReader.h" #include "VideoCommon/VertexLoader.h" +#include "VideoCommon/VertexLoader_Color.h" +#include "VideoCommon/VertexLoader_Normal.h" +#include "VideoCommon/VertexLoader_Position.h" +#include "VideoCommon/VertexLoader_TextCoord.h" #ifdef _M_X86_64 #include "VideoCommon/VertexLoaderX64.h" @@ -26,93 +30,6 @@ #include "VideoCommon/VertexLoaderARM64.h" #endif -VertexLoaderBase::VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr) - : m_VtxDesc{vtx_desc}, m_vat{vtx_attr} -{ - SetVAT(vtx_attr); -} - -void VertexLoaderBase::SetVAT(const VAT& vat) -{ - m_VtxAttr.PosElements = vat.g0.PosElements; - m_VtxAttr.PosFormat = vat.g0.PosFormat; - m_VtxAttr.PosFrac = vat.g0.PosFrac; - m_VtxAttr.NormalElements = vat.g0.NormalElements; - m_VtxAttr.NormalFormat = vat.g0.NormalFormat; - m_VtxAttr.color[0].Elements = vat.g0.Color0Elements; - m_VtxAttr.color[0].Comp = vat.g0.Color0Comp; - m_VtxAttr.color[1].Elements = vat.g0.Color1Elements; - m_VtxAttr.color[1].Comp = vat.g0.Color1Comp; - m_VtxAttr.texCoord[0].Elements = vat.g0.Tex0CoordElements; - m_VtxAttr.texCoord[0].Format = vat.g0.Tex0CoordFormat; - m_VtxAttr.texCoord[0].Frac = vat.g0.Tex0Frac; - m_VtxAttr.ByteDequant = vat.g0.ByteDequant; - m_VtxAttr.NormalIndex3 = vat.g0.NormalIndex3; - - m_VtxAttr.texCoord[1].Elements = vat.g1.Tex1CoordElements; - m_VtxAttr.texCoord[1].Format = vat.g1.Tex1CoordFormat; - m_VtxAttr.texCoord[1].Frac = vat.g1.Tex1Frac; - m_VtxAttr.texCoord[2].Elements = vat.g1.Tex2CoordElements; - m_VtxAttr.texCoord[2].Format = vat.g1.Tex2CoordFormat; - m_VtxAttr.texCoord[2].Frac = vat.g1.Tex2Frac; - m_VtxAttr.texCoord[3].Elements = vat.g1.Tex3CoordElements; - m_VtxAttr.texCoord[3].Format = vat.g1.Tex3CoordFormat; - m_VtxAttr.texCoord[3].Frac = vat.g1.Tex3Frac; - m_VtxAttr.texCoord[4].Elements = vat.g1.Tex4CoordElements; - m_VtxAttr.texCoord[4].Format = vat.g1.Tex4CoordFormat; - - m_VtxAttr.texCoord[4].Frac = vat.g2.Tex4Frac; - m_VtxAttr.texCoord[5].Elements = vat.g2.Tex5CoordElements; - m_VtxAttr.texCoord[5].Format = vat.g2.Tex5CoordFormat; - m_VtxAttr.texCoord[5].Frac = vat.g2.Tex5Frac; - m_VtxAttr.texCoord[6].Elements = vat.g2.Tex6CoordElements; - m_VtxAttr.texCoord[6].Format = vat.g2.Tex6CoordFormat; - m_VtxAttr.texCoord[6].Frac = vat.g2.Tex6Frac; - m_VtxAttr.texCoord[7].Elements = vat.g2.Tex7CoordElements; - m_VtxAttr.texCoord[7].Format = vat.g2.Tex7CoordFormat; - m_VtxAttr.texCoord[7].Frac = vat.g2.Tex7Frac; -}; - -std::string VertexLoaderBase::ToString() const -{ - std::string dest; - dest.reserve(250); - - dest += GetName(); - dest += ": "; - - dest += fmt::format("{}b skin: {} P: {} {}-{} ", m_VertexSize, m_VtxDesc.low.PosMatIdx, - m_VtxAttr.PosElements, m_VtxDesc.low.Position, m_VtxAttr.PosFormat); - - if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent) - { - dest += fmt::format("Nrm: {} {}-{} ", m_VtxAttr.NormalElements, m_VtxDesc.low.Normal, - m_VtxAttr.NormalFormat); - } - - for (size_t i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++) - { - if (g_main_cp_state.vtx_desc.low.Color[i] == VertexComponentFormat::NotPresent) - continue; - - const auto& color = m_VtxAttr.color[i]; - dest += fmt::format("C{}: {} {}-{} ", i, color.Elements, g_main_cp_state.vtx_desc.low.Color[i], - color.Comp); - } - - for (size_t i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++) - { - if (g_main_cp_state.vtx_desc.high.TexCoord[i] == VertexComponentFormat::NotPresent) - continue; - - const auto& tex_coord = m_VtxAttr.texCoord[i]; - dest += fmt::format("T{}: {} {}-{} ", i, tex_coord.Elements, - g_main_cp_state.vtx_desc.high.TexCoord[i], tex_coord.Format); - } - dest += fmt::format(" - {} v", m_numLoadedVertices); - return dest; -} - // a hacky implementation to compare two vertex loaders class VertexLoaderTester : public VertexLoaderBase { @@ -121,31 +38,25 @@ public: const TVtxDesc& vtx_desc, const VAT& vtx_attr) : VertexLoaderBase(vtx_desc, vtx_attr), a(std::move(a_)), b(std::move(b_)) { - m_initialized = a && b && a->IsInitialized() && b->IsInitialized(); - - if (m_initialized) + ASSERT(a && b); + if (a->m_vertex_size == b->m_vertex_size && a->m_native_components == b->m_native_components && + a->m_native_vtx_decl.stride == b->m_native_vtx_decl.stride) { - m_initialized = a->m_VertexSize == b->m_VertexSize && - a->m_native_components == b->m_native_components && - a->m_native_vtx_decl.stride == b->m_native_vtx_decl.stride; + // These are generated from the VAT and vertex desc, so they should match. + // m_native_vtx_decl.stride isn't set yet, though. + ASSERT(m_vertex_size == a->m_vertex_size && m_native_components == a->m_native_components); - if (m_initialized) - { - m_VertexSize = a->m_VertexSize; - m_native_components = a->m_native_components; - memcpy(&m_native_vtx_decl, &a->m_native_vtx_decl, sizeof(PortableVertexDeclaration)); - } - else - { - ERROR_LOG_FMT(VIDEO, "Can't compare vertex loaders that expect different vertex formats!"); - ERROR_LOG_FMT(VIDEO, "a: m_VertexSize {}, m_native_components {:#010x}, stride {}", - a->m_VertexSize, a->m_native_components, a->m_native_vtx_decl.stride); - ERROR_LOG_FMT(VIDEO, "b: m_VertexSize {}, m_native_components {:#010x}, stride {}", - b->m_VertexSize, b->m_native_components, b->m_native_vtx_decl.stride); - } + memcpy(&m_native_vtx_decl, &a->m_native_vtx_decl, sizeof(PortableVertexDeclaration)); + } + else + { + PanicAlertFmt("Can't compare vertex loaders that expect different vertex formats!\n" + "a: m_vertex_size {}, m_native_components {:#010x}, stride {}\n" + "b: m_vertex_size {}, m_native_components {:#010x}, stride {}", + a->m_vertex_size, a->m_native_components, a->m_native_vtx_decl.stride, + b->m_vertex_size, b->m_native_components, b->m_native_vtx_decl.stride); } } - ~VertexLoaderTester() override {} int RunVertices(DataReader src, DataReader dst, int count) override { buffer_a.resize(count * a->m_native_vtx_decl.stride + 4); @@ -168,22 +79,17 @@ public: std::min(count_a, count_b) * m_native_vtx_decl.stride)) { ERROR_LOG_FMT(VIDEO, - "The two vertex loaders have loaded different data " - "(guru meditation {:#010x}, {:#010x}, {:#010x}, {:#010x}, {:#010x}).", - m_VtxDesc.low.Hex, m_VtxDesc.high.Hex, m_vat.g0.Hex, m_vat.g1.Hex, - m_vat.g2.Hex); + "The two vertex loaders have loaded different data. Configuration:" + "\nVertex desc:\n{}\n\nVertex attr:\n{}", + m_VtxDesc, m_VtxAttr); } memcpy(dst.GetPointer(), buffer_a.data(), count_a * m_native_vtx_decl.stride); m_numLoadedVertices += count; return count_a; } - std::string GetName() const override { return "CompareLoader"; } - bool IsInitialized() override { return m_initialized; } private: - bool m_initialized; - std::unique_ptr a; std::unique_ptr b; @@ -191,36 +97,112 @@ private: std::vector buffer_b; }; +template +static void GetComponentSizes(const TVtxDesc& vtx_desc, const VAT& vtx_attr, Function f) +{ + if (vtx_desc.low.PosMatIdx) + f(1); + for (auto texmtxidx : vtx_desc.low.TexMatIdx) + { + if (texmtxidx) + f(1); + } + const u32 pos_size = VertexLoader_Position::GetSize(vtx_desc.low.Position, vtx_attr.g0.PosFormat, + vtx_attr.g0.PosElements); + if (pos_size != 0) + f(pos_size); + const u32 norm_size = + VertexLoader_Normal::GetSize(vtx_desc.low.Normal, vtx_attr.g0.NormalFormat, + vtx_attr.g0.NormalElements, vtx_attr.g0.NormalIndex3); + if (norm_size != 0) + f(norm_size); + for (u32 i = 0; i < vtx_desc.low.Color.Size(); i++) + { + const u32 color_size = + VertexLoader_Color::GetSize(vtx_desc.low.Color[i], vtx_attr.GetColorFormat(i)); + if (color_size != 0) + f(color_size); + } + for (u32 i = 0; i < vtx_desc.high.TexCoord.Size(); i++) + { + const u32 tc_size = VertexLoader_TextCoord::GetSize( + vtx_desc.high.TexCoord[i], vtx_attr.GetTexFormat(i), vtx_attr.GetTexElements(i)); + if (tc_size != 0) + f(tc_size); + } +} + +u32 VertexLoaderBase::GetVertexSize(const TVtxDesc& vtx_desc, const VAT& vtx_attr) +{ + u32 size = 0; + GetComponentSizes(vtx_desc, vtx_attr, [&size](u32 s) { size += s; }); + return size; +} + +u32 VertexLoaderBase::GetVertexComponents(const TVtxDesc& vtx_desc, const VAT& vtx_attr) +{ + u32 components = 0; + if (vtx_desc.low.PosMatIdx) + components |= VB_HAS_POSMTXIDX; + for (u32 i = 0; i < vtx_desc.low.TexMatIdx.Size(); i++) + { + if (vtx_desc.low.TexMatIdx[i]) + components |= VB_HAS_TEXMTXIDX0 << i; + } + // Vertices always have positions; thus there is no VB_HAS_POS as it would always be set + if (vtx_desc.low.Normal != VertexComponentFormat::NotPresent) + { + components |= VB_HAS_NRM0; + if (vtx_attr.g0.NormalElements == NormalComponentCount::NBT) + components |= VB_HAS_NRM1 | VB_HAS_NRM2; + } + for (u32 i = 0; i < vtx_desc.low.Color.Size(); i++) + { + if (vtx_desc.low.Color[i] != VertexComponentFormat::NotPresent) + components |= VB_HAS_COL0 << i; + } + for (u32 i = 0; i < vtx_desc.high.TexCoord.Size(); i++) + { + if (vtx_desc.high.TexCoord[i] != VertexComponentFormat::NotPresent) + components |= VB_HAS_UV0 << i; + } + return components; +} + +std::vector VertexLoaderBase::GetVertexComponentSizes(const TVtxDesc& vtx_desc, + const VAT& vtx_attr) +{ + std::vector sizes; + GetComponentSizes(vtx_desc, vtx_attr, [&sizes](u32 s) { sizes.push_back(s); }); + return sizes; +} + std::unique_ptr VertexLoaderBase::CreateVertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr) { - std::unique_ptr loader; + std::unique_ptr loader = nullptr; //#define COMPARE_VERTEXLOADERS -#if defined(COMPARE_VERTEXLOADERS) && defined(_M_X86_64) - // first try: Any new VertexLoader vs the old one - loader = std::make_unique( - std::make_unique(vtx_desc, vtx_attr), // the software one - std::make_unique(vtx_desc, vtx_attr), // the new one to compare - vtx_desc, vtx_attr); - if (loader->IsInitialized()) - return loader; -#elif defined(_M_X86_64) +#if defined(_M_X86_64) loader = std::make_unique(vtx_desc, vtx_attr); - if (loader->IsInitialized()) - return loader; #elif defined(_M_ARM_64) loader = std::make_unique(vtx_desc, vtx_attr); - if (loader->IsInitialized()) - return loader; #endif - // last try: The old VertexLoader - loader = std::make_unique(vtx_desc, vtx_attr); - if (loader->IsInitialized()) - return loader; + // Use the software loader as a fallback + // (not currently applicable, as both VertexLoaderX64 and VertexLoaderARM64 + // are always usable, but if a loader that only works on some CPUs is created + // then this fallback would be used) + if (!loader) + loader = std::make_unique(vtx_desc, vtx_attr); - PanicAlertFmt("No Vertex Loader found."); - return nullptr; +#if defined(COMPARE_VERTEXLOADERS) + return std::make_unique( + std::make_unique(vtx_desc, vtx_attr), // the software one + std::move(loader), // the new one to compare + vtx_desc, vtx_attr); +#else + return loader; +#endif } diff --git a/Source/Core/VideoCommon/VertexLoaderBase.h b/Source/Core/VideoCommon/VertexLoaderBase.h index 77b66f629b..3dc03b5ce5 100644 --- a/Source/Core/VideoCommon/VertexLoaderBase.h +++ b/Source/Core/VideoCommon/VertexLoaderBase.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" #include "VideoCommon/CPMemory.h" @@ -60,33 +61,31 @@ struct hash class VertexLoaderBase { public: + static u32 GetVertexSize(const TVtxDesc& vtx_desc, const VAT& vtx_attr); + static u32 GetVertexComponents(const TVtxDesc& vtx_desc, const VAT& vtx_attr); + static std::vector GetVertexComponentSizes(const TVtxDesc& vtx_desc, const VAT& vtx_attr); static std::unique_ptr CreateVertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr); virtual ~VertexLoaderBase() {} virtual int RunVertices(DataReader src, DataReader dst, int count) = 0; - virtual bool IsInitialized() = 0; - - // For debugging / profiling - std::string ToString() const; - - virtual std::string GetName() const = 0; - // per loader public state - int m_VertexSize = 0; // number of bytes of a raw GC vertex PortableVertexDeclaration m_native_vtx_decl{}; - u32 m_native_components = 0; + const u32 m_vertex_size; // number of bytes of a raw GC vertex + const u32 m_native_components; // used by VertexLoaderManager NativeVertexFormat* m_native_vertex_format = nullptr; int m_numLoadedVertices = 0; protected: - VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr); - void SetVAT(const VAT& vat); + VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr) + : m_VtxDesc{vtx_desc}, m_VtxAttr{vtx_attr}, m_vertex_size{GetVertexSize(vtx_desc, vtx_attr)}, + m_native_components{GetVertexComponents(vtx_desc, vtx_attr)} + { + } // GC vertex format - TVtxAttr m_VtxAttr; // VAT decoded into easy format - TVtxDesc m_VtxDesc; // Not really used currently - or well it is, but could be easily avoided. - VAT m_vat; + const VAT m_VtxAttr; + const TVtxDesc m_VtxDesc; }; diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp index 94a331a0d2..c657ed7989 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.cpp +++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp @@ -114,34 +114,6 @@ struct entry }; } // namespace -std::string VertexLoadersToString() -{ - std::lock_guard lk(s_vertex_loader_map_lock); - std::vector entries; - - size_t total_size = 0; - for (const auto& map_entry : s_vertex_loader_map) - { - entry e = {map_entry.second->ToString(), - static_cast(map_entry.second->m_numLoadedVertices)}; - - total_size += e.text.size() + 1; - entries.push_back(std::move(e)); - } - - sort(entries.begin(), entries.end()); - - std::string dest; - dest.reserve(total_size); - for (const entry& entry : entries) - { - dest += entry.text; - dest += '\n'; - } - - return dest; -} - void MarkAllDirty() { g_main_cp_state.attr_dirty = BitSet32::AllTrue(8); @@ -275,7 +247,7 @@ int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bo VertexLoaderBase* loader = RefreshLoader(vtx_attr_group, is_preprocess); - int size = count * loader->m_VertexSize; + int size = count * loader->m_vertex_size; if ((int)src.size() < size) return -1; diff --git a/Source/Core/VideoCommon/VertexLoaderManager.h b/Source/Core/VideoCommon/VertexLoaderManager.h index 71188cca6b..74856a4d28 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.h +++ b/Source/Core/VideoCommon/VertexLoaderManager.h @@ -38,9 +38,6 @@ NativeVertexFormat* GetUberVertexFormat(const PortableVertexDeclaration& decl); // Returns -1 if buf_size is insufficient, else the amount of bytes consumed int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bool is_preprocess); -// For debugging -std::string VertexLoadersToString(); - NativeVertexFormat* GetCurrentVertexFormat(); // Resolved pointers to array bases. Used by vertex loaders. diff --git a/Source/Core/VideoCommon/VertexLoaderX64.cpp b/Source/Core/VideoCommon/VertexLoaderX64.cpp index 2c0a97b54b..89eb841f5e 100644 --- a/Source/Core/VideoCommon/VertexLoaderX64.cpp +++ b/Source/Core/VideoCommon/VertexLoaderX64.cpp @@ -45,15 +45,13 @@ static OpArg MPIC(const void* ptr) VertexLoaderX64::VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att) : VertexLoaderBase(vtx_desc, vtx_att) { - if (!IsInitialized()) - return; - AllocCodeSpace(4096); ClearCodeSpace(); GenerateVertexLoader(); WriteProtect(); - const std::string name = ToString(); + const std::string name = + fmt::format("VertexLoaderX64\nVtx desc: \n{}\nVAT:\n{}", vtx_desc, vtx_att); JitRegister::Register(region, GetCodePtr(), name.c_str()); } @@ -420,7 +418,6 @@ void VertexLoaderX64::GenerateVertexLoader() MOV(32, MPIC(VertexLoaderManager::position_matrix_index, count_reg, SCALE_4), R(scratch1)); SetJumpTarget(dont_store); - m_native_components |= VB_HAS_POSMTXIDX; m_native_vtx_decl.posmtx.components = 4; m_native_vtx_decl.posmtx.enable = true; m_native_vtx_decl.posmtx.offset = m_dst_ofs; @@ -438,31 +435,27 @@ void VertexLoaderX64::GenerateVertexLoader() } OpArg data = GetVertexAddr(ARRAY_POSITION, m_VtxDesc.low.Position); - int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3; - ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements, - m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position); + int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3; + ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements, + m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position); if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent) { static const u8 map[8] = {7, 6, 15, 14}; - const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)]; - const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1; + const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())]; + const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1; for (int i = 0; i < limit; i++) { - if (!i || m_VtxAttr.NormalIndex3) + if (!i || m_VtxAttr.g0.NormalIndex3) { data = GetVertexAddr(ARRAY_NORMAL, m_VtxDesc.low.Normal); - int elem_size = GetElementSize(m_VtxAttr.NormalFormat); + int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat); data.AddMemOffset(i * elem_size * 3); } - data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true, - scaling_exponent, &m_native_vtx_decl.normals[i])); + data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, + true, scaling_exponent, &m_native_vtx_decl.normals[i])); } - - m_native_components |= VB_HAS_NRM0; - if (m_VtxAttr.NormalElements == NormalComponentCount::NBT) - m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2; } for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++) @@ -470,8 +463,7 @@ void VertexLoaderX64::GenerateVertexLoader() if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent) { data = GetVertexAddr(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i]); - ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp); - m_native_components |= VB_HAS_COL0 << i; + ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i)); m_native_vtx_decl.colors[i].components = 4; m_native_vtx_decl.colors[i].enable = true; m_native_vtx_decl.colors[i].offset = m_dst_ofs; @@ -483,19 +475,17 @@ void VertexLoaderX64::GenerateVertexLoader() for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++) { - int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1; + int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::ST ? 2 : 1; if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent) { data = GetVertexAddr(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i]); - u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac; - ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements, - m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent, - &m_native_vtx_decl.texcoords[i]); - m_native_components |= VB_HAS_UV0 << i; + u8 scaling_exponent = m_VtxAttr.GetTexFrac(i); + ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements, + m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant, + scaling_exponent, &m_native_vtx_decl.texcoords[i]); } if (m_VtxDesc.low.TexMatIdx[i]) { - m_native_components |= VB_HAS_TEXMTXIDX0 << i; m_native_vtx_decl.texcoords[i].components = 3; m_native_vtx_decl.texcoords[i].enable = true; m_native_vtx_decl.texcoords[i].type = VAR_FLOAT; @@ -546,7 +536,7 @@ void VertexLoaderX64::GenerateVertexLoader() RET(); } - m_VertexSize = m_src_ofs; + ASSERT(m_vertex_size == m_src_ofs); m_native_vtx_decl.stride = m_dst_ofs; } diff --git a/Source/Core/VideoCommon/VertexLoaderX64.h b/Source/Core/VideoCommon/VertexLoaderX64.h index 0344d7f1c9..7b91ad6cf8 100644 --- a/Source/Core/VideoCommon/VertexLoaderX64.h +++ b/Source/Core/VideoCommon/VertexLoaderX64.h @@ -18,8 +18,6 @@ public: VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att); protected: - std::string GetName() const override { return "VertexLoaderX64"; } - bool IsInitialized() override { return true; } int RunVertices(DataReader src, DataReader dst, int count) override; private: diff --git a/Source/Core/VideoCommon/VertexLoader_Color.cpp b/Source/Core/VideoCommon/VertexLoader_Color.cpp index cef347346d..75d16ee458 100644 --- a/Source/Core/VideoCommon/VertexLoader_Color.cpp +++ b/Source/Core/VideoCommon/VertexLoader_Color.cpp @@ -5,6 +5,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/MsgHandler.h" #include "Common/Swap.h" #include "VideoCommon/VertexLoader.h" @@ -136,7 +137,6 @@ void Color_ReadIndex_32b_8888(VertexLoader* loader) (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]); SetCol(loader, Read32(address)); } -} // Anonymous namespace void Color_ReadDirect_24b_888(VertexLoader* loader) { @@ -149,10 +149,12 @@ void Color_ReadDirect_32b_888x(VertexLoader* loader) SetCol(loader, Read24(DataGetPosition())); DataSkip(4); } + void Color_ReadDirect_16b_565(VertexLoader* loader) { SetCol565(loader, DataRead()); } + void Color_ReadDirect_16b_4444(VertexLoader* loader) { u16 value; @@ -161,62 +163,52 @@ void Color_ReadDirect_16b_4444(VertexLoader* loader) SetCol4444(loader, value); DataSkip(2); } + void Color_ReadDirect_24b_6666(VertexLoader* loader) { SetCol6666(loader, Common::swap32(DataGetPosition() - 1)); DataSkip(3); } + void Color_ReadDirect_32b_8888(VertexLoader* loader) { SetCol(loader, DataReadU32Unswapped()); } -void Color_ReadIndex8_16b_565(VertexLoader* loader) +constexpr TPipelineFunction s_table_read_color[4][6] = { + {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + {Color_ReadDirect_16b_565, Color_ReadDirect_24b_888, Color_ReadDirect_32b_888x, + Color_ReadDirect_16b_4444, Color_ReadDirect_24b_6666, Color_ReadDirect_32b_8888}, + {Color_ReadIndex_16b_565, Color_ReadIndex_24b_888, Color_ReadIndex_32b_888x, + Color_ReadIndex_16b_4444, Color_ReadIndex_24b_6666, Color_ReadIndex_32b_8888}, + {Color_ReadIndex_16b_565, Color_ReadIndex_24b_888, Color_ReadIndex_32b_888x, + Color_ReadIndex_16b_4444, Color_ReadIndex_24b_6666, Color_ReadIndex_32b_8888}, +}; + +constexpr u32 s_table_read_color_vertex_size[4][6] = { + {0, 0, 0, 0, 0, 0}, + {2, 3, 4, 2, 3, 4}, + {1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2}, +}; +} // Anonymous namespace + +u32 VertexLoader_Color::GetSize(VertexComponentFormat type, ColorFormat format) { - Color_ReadIndex_16b_565(loader); -} -void Color_ReadIndex8_24b_888(VertexLoader* loader) -{ - Color_ReadIndex_24b_888(loader); -} -void Color_ReadIndex8_32b_888x(VertexLoader* loader) -{ - Color_ReadIndex_32b_888x(loader); -} -void Color_ReadIndex8_16b_4444(VertexLoader* loader) -{ - Color_ReadIndex_16b_4444(loader); -} -void Color_ReadIndex8_24b_6666(VertexLoader* loader) -{ - Color_ReadIndex_24b_6666(loader); -} -void Color_ReadIndex8_32b_8888(VertexLoader* loader) -{ - Color_ReadIndex_32b_8888(loader); + if (format > ColorFormat::RGBA8888) + { + PanicAlertFmt("Invalid color format {}", format); + return 0; + } + return s_table_read_color_vertex_size[u32(type)][u32(format)]; } -void Color_ReadIndex16_16b_565(VertexLoader* loader) +TPipelineFunction VertexLoader_Color::GetFunction(VertexComponentFormat type, ColorFormat format) { - Color_ReadIndex_16b_565(loader); -} -void Color_ReadIndex16_24b_888(VertexLoader* loader) -{ - Color_ReadIndex_24b_888(loader); -} -void Color_ReadIndex16_32b_888x(VertexLoader* loader) -{ - Color_ReadIndex_32b_888x(loader); -} -void Color_ReadIndex16_16b_4444(VertexLoader* loader) -{ - Color_ReadIndex_16b_4444(loader); -} -void Color_ReadIndex16_24b_6666(VertexLoader* loader) -{ - Color_ReadIndex_24b_6666(loader); -} -void Color_ReadIndex16_32b_8888(VertexLoader* loader) -{ - Color_ReadIndex_32b_8888(loader); + if (format > ColorFormat::RGBA8888) + { + PanicAlertFmt("Invalid color format {}", format); + return nullptr; + } + return s_table_read_color[u32(type)][u32(format)]; } diff --git a/Source/Core/VideoCommon/VertexLoader_Color.h b/Source/Core/VideoCommon/VertexLoader_Color.h index 0f83dc4b29..3fd8b078ea 100644 --- a/Source/Core/VideoCommon/VertexLoader_Color.h +++ b/Source/Core/VideoCommon/VertexLoader_Color.h @@ -4,25 +4,15 @@ #pragma once -class VertexLoader; +#include "Common/CommonTypes.h" +#include "VideoCommon/VertexLoader.h" -void Color_ReadDirect_24b_888(VertexLoader* loader); -void Color_ReadDirect_32b_888x(VertexLoader* loader); -void Color_ReadDirect_16b_565(VertexLoader* loader); -void Color_ReadDirect_16b_4444(VertexLoader* loader); -void Color_ReadDirect_24b_6666(VertexLoader* loader); -void Color_ReadDirect_32b_8888(VertexLoader* loader); +enum class VertexComponentFormat; +enum class ColorFormat; -void Color_ReadIndex8_16b_565(VertexLoader* loader); -void Color_ReadIndex8_24b_888(VertexLoader* loader); -void Color_ReadIndex8_32b_888x(VertexLoader* loader); -void Color_ReadIndex8_16b_4444(VertexLoader* loader); -void Color_ReadIndex8_24b_6666(VertexLoader* loader); -void Color_ReadIndex8_32b_8888(VertexLoader* loader); - -void Color_ReadIndex16_16b_565(VertexLoader* loader); -void Color_ReadIndex16_24b_888(VertexLoader* loader); -void Color_ReadIndex16_32b_888x(VertexLoader* loader); -void Color_ReadIndex16_16b_4444(VertexLoader* loader); -void Color_ReadIndex16_24b_6666(VertexLoader* loader); -void Color_ReadIndex16_32b_8888(VertexLoader* loader); +class VertexLoader_Color +{ +public: + static u32 GetSize(VertexComponentFormat type, ColorFormat format); + static TPipelineFunction GetFunction(VertexComponentFormat type, ColorFormat format); +}; diff --git a/Source/Core/VideoCommon/XFStructs.cpp b/Source/Core/VideoCommon/XFStructs.cpp index 9ceaf2edc1..4da229c422 100644 --- a/Source/Core/VideoCommon/XFStructs.cpp +++ b/Source/Core/VideoCommon/XFStructs.cpp @@ -259,12 +259,19 @@ void LoadXFReg(u32 transferSize, u32 baseAddress, DataReader src) } } +constexpr std::tuple 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(u32 val, int refarray) { - int index = val >> 16; - int address = val & 0xFFF; // check mask - int size = ((val >> 12) & 0xF) + 1; + const auto [index, address, size] = ExtractIndexedXF(val); // load stuff from array to address in xf mem u32* currData = (u32*)(&xfmem) + address; @@ -279,7 +286,7 @@ void LoadIndexedXF(u32 val, int refarray) g_main_cp_state.array_strides[refarray] * index); } bool changed = false; - for (int i = 0; i < size; ++i) + for (u32 i = 0; i < size; ++i) { if (currData[i] != Common::swap32(newData[i])) { @@ -290,15 +297,14 @@ void LoadIndexedXF(u32 val, int refarray) } if (changed) { - for (int i = 0; i < size; ++i) + for (u32 i = 0; i < size; ++i) currData[i] = Common::swap32(newData[i]); } } void PreprocessIndexedXF(u32 val, int refarray) { - const u32 index = val >> 16; - const u32 size = ((val >> 12) & 0xF) + 1; + const auto [index, address, size] = ExtractIndexedXF(val); const u8* new_data = Memory::GetPointer(g_preprocess_cp_state.array_bases[refarray] + g_preprocess_cp_state.array_strides[refarray] * index); @@ -643,3 +649,18 @@ std::pair GetXFTransferInfo(const u8* data) return std::make_pair(fmt::to_string(name), fmt::to_string(desc)); } + +std::pair GetXFIndexedLoadInfo(u8 array, u32 value) +{ + 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; + for (u32 i = 0; i < size; i++) + { + fmt::format_to(written, "{}\n", GetXFMemName(address + i)); + } + + return std::make_pair(desc, fmt::to_string(written)); +} diff --git a/Source/Core/VideoCommon/XFStructs.h b/Source/Core/VideoCommon/XFStructs.h index 5aac1f059b..d73f47c722 100644 --- a/Source/Core/VideoCommon/XFStructs.h +++ b/Source/Core/VideoCommon/XFStructs.h @@ -13,3 +13,4 @@ std::pair GetXFRegInfo(u32 address, u32 value); std::string GetXFMemName(u32 address); std::string GetXFMemDescription(u32 address, u32 value); std::pair GetXFTransferInfo(const u8* data); +std::pair GetXFIndexedLoadInfo(u8 array, u32 value); diff --git a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp index 7f9dbdec84..925a13114a 100644 --- a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp +++ b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp @@ -62,7 +62,7 @@ protected: void CreateAndCheckSizes(size_t input_size, size_t output_size) { m_loader = VertexLoaderBase::CreateVertexLoader(m_vtx_desc, m_vtx_attr); - ASSERT_EQ((int)input_size, m_loader->m_VertexSize); + ASSERT_EQ(input_size, m_loader->m_vertex_size); ASSERT_EQ((int)output_size, m_loader->m_native_vtx_decl.stride); }