From d5cfac71d09c4a751af607f16ba9c56755054d0a Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Wed, 21 Apr 2021 18:06:33 -0700 Subject: [PATCH] Refactor object listing code This also adds the commands after the last primitive data but before the next frame as a unique object; this is mainly just the XFB copy. It's nice to have these visible, though disabling the object does nothing since only primitive data is disabled and there is no primitive data in this case. --- .../Core/FifoPlayer/FifoPlaybackAnalyzer.cpp | 38 +++---- .../Core/FifoPlayer/FifoPlaybackAnalyzer.h | 33 +++++- Source/Core/Core/FifoPlayer/FifoPlayer.cpp | 89 ++++++--------- Source/Core/Core/FifoPlayer/FifoPlayer.h | 3 +- Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp | 103 +++++++++++++----- Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h | 10 +- 6 files changed, 163 insertions(+), 113 deletions(-) diff --git a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp index d5ddf4310f..b5dcb5cd8c 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.cpp @@ -46,7 +46,9 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, s_DrawingObject = false; u32 cmdStart = 0; - u32 nextMemUpdate = 0; + + u32 part_start = 0; + FifoAnalyzer::CPMemory cpmem; #if LOG_FIFO_CMDS // Debugging @@ -55,14 +57,6 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, while (cmdStart < frame.fifoData.size()) { - // Add memory updates that have occurred before this point in the frame - while (nextMemUpdate < frame.memoryUpdates.size() && - frame.memoryUpdates[nextMemUpdate].fifoPosition <= cmdStart) - { - analyzed.memoryUpdates.push_back(frame.memoryUpdates[nextMemUpdate]); - ++nextMemUpdate; - } - const bool wasDrawing = s_DrawingObject; const u32 cmdSize = FifoAnalyzer::AnalyzeCommand(&frame.fifoData[cmdStart], DecodeMode::Playback); @@ -79,9 +73,7 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, if (cmdSize == 0) { // Clean up frame analysis - analyzed.objectStarts.clear(); - analyzed.objectCPStates.clear(); - analyzed.objectEnds.clear(); + analyzed.parts.clear(); return; } @@ -90,22 +82,28 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, { if (s_DrawingObject) { - analyzed.objectStarts.push_back(cmdStart); - analyzed.objectCPStates.push_back(s_CpMem); + // Start of primitive data for an object + analyzed.AddPart(FramePartType::Commands, part_start, cmdStart, s_CpMem); + part_start = cmdStart; + // Copy cpmem now, because end_of_primitives isn't triggered until the first opcode after + // primitive data, and the first opcode might update cpmem + std::memcpy(&cpmem, &s_CpMem, sizeof(FifoAnalyzer::CPMemory)); } else { - analyzed.objectEnds.push_back(cmdStart); + // End of primitive data for an object, and thus end of the object + analyzed.AddPart(FramePartType::PrimitiveData, part_start, cmdStart, cpmem); + part_start = cmdStart; } } cmdStart += cmdSize; } - 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()); + if (part_start != cmdStart) + { + // Remaining data, usually without any primitives + analyzed.AddPart(FramePartType::Commands, part_start, cmdStart, s_CpMem); + } } } diff --git a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h index 78e4c6e7d8..071279c885 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h +++ b/Source/Core/Core/FifoPlayer/FifoPlaybackAnalyzer.h @@ -9,14 +9,35 @@ #include "Core/FifoPlayer/FifoAnalyzer.h" #include "Core/FifoPlayer/FifoDataFile.h" +enum class FramePartType +{ + Commands, + PrimitiveData, +}; + +struct FramePart +{ + constexpr FramePart(FramePartType type, u32 start, u32 end, const FifoAnalyzer::CPMemory& cpmem) + : m_type(type), m_start(start), m_end(end), m_cpmem(cpmem) + { + } + + const FramePartType m_type; + const u32 m_start; + const u32 m_end; + const FifoAnalyzer::CPMemory m_cpmem; +}; + 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; + std::vector parts; + Common::EnumMap part_type_counts; + + void AddPart(FramePartType type, u32 start, u32 end, const FifoAnalyzer::CPMemory& cpmem) + { + parts.emplace_back(type, start, end, cpmem); + part_type_counts[type]++; + } }; namespace FifoPlaybackAnalyzer diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp index 47b9ac2f87..cae033aa93 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp @@ -191,7 +191,7 @@ u32 FifoPlayer::GetMaxObjectCount() const u32 result = 0; for (auto& frame : m_FrameInfo) { - const u32 count = static_cast(frame.objectStarts.size()); + const u32 count = frame.part_type_counts[FramePartType::PrimitiveData]; if (count > result) result = count; } @@ -202,7 +202,7 @@ u32 FifoPlayer::GetFrameObjectCount(u32 frame) const { if (frame < m_FrameInfo.size()) { - return static_cast(m_FrameInfo[frame].objectStarts.size()); + return m_FrameInfo[frame].part_type_counts[FramePartType::PrimitiveData]; } return 0; @@ -262,55 +262,35 @@ void FifoPlayer::WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& m_ElapsedCycles = 0; m_FrameFifoSize = static_cast(frame.fifoData.size()); - // Determine start and end objects - u32 numObjects = (u32)(info.objectStarts.size()); - u32 drawStart = std::min(numObjects, m_ObjectRangeStart); - u32 drawEnd = std::min(numObjects - 1, m_ObjectRangeEnd); + u32 memory_update = 0; + u32 object_num = 0; - u32 position = 0; - u32 memoryUpdate = 0; - - // Skip memory updates during frame if true + // Skip all memory updates if early memory updates are enabled, as we already wrote them if (m_EarlyMemoryUpdates) { - memoryUpdate = (u32)(frame.memoryUpdates.size()); + memory_update = (u32)(frame.memoryUpdates.size()); } - if (numObjects > 0) + for (const FramePart& part : info.parts) { - u32 objectNum = 0; + bool show_part; - // Write fifo data skipping objects before the draw range - while (objectNum < drawStart) + if (part.m_type == FramePartType::PrimitiveData) { - WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info); - - position = info.objectEnds[objectNum]; - ++objectNum; + show_part = m_ObjectRangeStart <= object_num && object_num <= m_ObjectRangeEnd; + object_num++; + } + else + { + // We always include commands and EFB copies, as commands from earlier objects still apply to + // later ones (games generally do not reconfigure everything for each object) + show_part = true; } - // Write objects in draw range - if (objectNum < numObjects && drawStart <= drawEnd) - { - objectNum = drawEnd; - WriteFramePart(position, info.objectEnds[objectNum], memoryUpdate, frame, info); - position = info.objectEnds[objectNum]; - ++objectNum; - } - - // Write fifo data skipping objects after the draw range - while (objectNum < numObjects) - { - WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info); - - position = info.objectEnds[objectNum]; - ++objectNum; - } + if (show_part) + WriteFramePart(part, &memory_update, frame); } - // Write data after the last object - WriteFramePart(position, static_cast(frame.fifoData.size()), memoryUpdate, frame, info); - FlushWGP(); // Sleep while the GPU is active @@ -321,36 +301,39 @@ void FifoPlayer::WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& } } -void FifoPlayer::WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, - const FifoFrameInfo& frame, const AnalyzedFrameInfo& info) +void FifoPlayer::WriteFramePart(const FramePart& part, u32* next_mem_update, + const FifoFrameInfo& frame) { const u8* const data = frame.fifoData.data(); - while (nextMemUpdate < frame.memoryUpdates.size() && dataStart < dataEnd) - { - const MemoryUpdate& memUpdate = info.memoryUpdates[nextMemUpdate]; + u32 data_start = part.m_start; + const u32 data_end = part.m_end; - if (memUpdate.fifoPosition < dataEnd) + while (*next_mem_update < frame.memoryUpdates.size() && data_start < data_end) + { + const MemoryUpdate& memUpdate = frame.memoryUpdates[*next_mem_update]; + + if (memUpdate.fifoPosition < data_end) { - if (dataStart < memUpdate.fifoPosition) + if (data_start < memUpdate.fifoPosition) { - WriteFifo(data, dataStart, memUpdate.fifoPosition); - dataStart = memUpdate.fifoPosition; + WriteFifo(data, data_start, memUpdate.fifoPosition); + data_start = memUpdate.fifoPosition; } WriteMemory(memUpdate); - ++nextMemUpdate; + ++*next_mem_update; } else { - WriteFifo(data, dataStart, dataEnd); - dataStart = dataEnd; + WriteFifo(data, data_start, data_end); + data_start = data_end; } } - if (dataStart < dataEnd) - WriteFifo(data, dataStart, dataEnd); + if (data_start < data_end) + WriteFifo(data, data_start, data_end); } void FifoPlayer::WriteAllMemoryUpdates() diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.h b/Source/Core/Core/FifoPlayer/FifoPlayer.h index 01ce07c4a0..8083612658 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.h +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.h @@ -108,8 +108,7 @@ private: CPU::State AdvanceFrame(); void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info); - void WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame, - const AnalyzedFrameInfo& info); + void WriteFramePart(const FramePart& part, u32* next_mem_update, const FifoFrameInfo& frame); void WriteAllMemoryUpdates(); void WriteMemory(const MemoryUpdate& memUpdate); diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp index 73ba823ec1..223fdd7145 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp +++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp @@ -3,6 +3,8 @@ #include "DolphinQt/FIFO/FIFOAnalyzer.h" +#include + #include #include #include @@ -27,8 +29,12 @@ #include "VideoCommon/VertexLoaderBase.h" #include "VideoCommon/XFStructs.h" +// Values range from 0 to number of frames - 1 constexpr int FRAME_ROLE = Qt::UserRole; -constexpr int OBJECT_ROLE = Qt::UserRole + 1; +// Values range from 0 to number of parts - 1 +constexpr int PART_START_ROLE = Qt::UserRole + 1; +// Values range from 1 to number of parts +constexpr int PART_END_ROLE = Qt::UserRole + 2; FIFOAnalyzer::FIFOAnalyzer() { @@ -144,22 +150,58 @@ void FIFOAnalyzer::UpdateTree() auto* file = FifoPlayer::GetInstance().GetFile(); const u32 frame_count = file->GetFrameCount(); + for (u32 frame = 0; frame < frame_count; frame++) { auto* frame_item = new QTreeWidgetItem({tr("Frame %1").arg(frame)}); recording_item->addChild(frame_item); - 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(object)}); + const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame); + ASSERT(frame_info.parts.size() != 0); + Common::EnumMap part_counts; + u32 part_start = 0; + + for (u32 part_nr = 0; part_nr < frame_info.parts.size(); part_nr++) + { + const auto& part = frame_info.parts[part_nr]; + + const u32 part_type_nr = part_counts[part.m_type]; + part_counts[part.m_type]++; + + QTreeWidgetItem* object_item = nullptr; + if (part.m_type == FramePartType::PrimitiveData) + object_item = new QTreeWidgetItem({tr("Object %1").arg(part_type_nr)}); + // We don't create dedicated labels for FramePartType::Command; + // those are grouped with the primitive + + if (object_item != nullptr) + { + frame_item->addChild(object_item); + + object_item->setData(0, FRAME_ROLE, frame); + object_item->setData(0, PART_START_ROLE, part_start); + object_item->setData(0, PART_END_ROLE, part_nr); + + part_start = part_nr + 1; + } + } + + // Final data (the XFB copy) + if (part_start != frame_info.parts.size()) + { + QTreeWidgetItem* object_item = new QTreeWidgetItem({tr("Final Data")}); frame_item->addChild(object_item); object_item->setData(0, FRAME_ROLE, frame); - object_item->setData(0, OBJECT_ROLE, object); + object_item->setData(0, PART_START_ROLE, part_start); + object_item->setData(0, PART_END_ROLE, u32(frame_info.parts.size() - 1)); } + + // The counts we computed should match the frame's counts + ASSERT(std::equal(frame_info.part_type_counts.begin(), frame_info.part_type_counts.end(), + part_counts.begin())); } } @@ -196,19 +238,19 @@ void FIFOAnalyzer::UpdateDetails() const auto items = m_tree_widget->selectedItems(); - if (items.isEmpty() || items[0]->data(0, OBJECT_ROLE).isNull()) + if (items.isEmpty() || items[0]->data(0, PART_START_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 start_part_nr = items[0]->data(0, PART_START_ROLE).toUInt(); + const u32 end_part_nr = items[0]->data(0, PART_END_ROLE).toUInt(); - const auto& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); + const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); const auto& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr); - // 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; + const u32 object_start = frame_info.parts[start_part_nr].m_start; + const u32 object_end = frame_info.parts[end_part_nr].m_end; + const u32 object_size = object_end - object_start; const u8* const object = &fifo_frame.fifoData[object_start]; @@ -348,10 +390,9 @@ void FIFOAnalyzer::UpdateDetails() 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& vtx_desc = frame_info.parts[end_part_nr].m_cpmem.vtxDesc; + const auto& vtx_attr = frame_info.parts[end_part_nr].m_cpmem.vtxAttr[vat]; const auto name = GetPrimitiveName(command); @@ -396,8 +437,6 @@ void FIFOAnalyzer::UpdateDetails() 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); } @@ -412,12 +451,15 @@ void FIFOAnalyzer::BeginSearch() const auto items = m_tree_widget->selectedItems(); if (items.isEmpty() || items[0]->data(0, FRAME_ROLE).isNull() || - items[0]->data(0, OBJECT_ROLE).isNull()) + items[0]->data(0, PART_START_ROLE).isNull()) { m_search_label->setText(tr("Invalid search parameters (no object selected)")); return; } + // Having PART_START_ROLE indicates that this is valid + const int object_idx = items[0]->parent()->indexOfChild(items[0]); + // TODO: Remove even string length limit if (search_str.length() % 2) { @@ -448,13 +490,15 @@ void FIFOAnalyzer::BeginSearch() m_search_results.clear(); const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt(); - const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt(); + const u32 start_part_nr = items[0]->data(0, PART_START_ROLE).toUInt(); + const u32 end_part_nr = items[0]->data(0, PART_END_ROLE).toUInt(); const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr); const FifoFrameInfo& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr); - 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 u32 object_start = frame_info.parts[start_part_nr].m_start; + const u32 object_end = frame_info.parts[end_part_nr].m_end; + const u32 object_size = object_end - object_start; const u8* const object = &fifo_frame.fifoData[object_start]; @@ -473,7 +517,7 @@ void FIFOAnalyzer::BeginSearch() { if (std::equal(search_val.begin(), search_val.end(), ptr)) { - m_search_results.emplace_back(frame_nr, object_nr, cmd_nr); + m_search_results.emplace_back(frame_nr, object_idx, cmd_nr); break; } } @@ -527,7 +571,7 @@ void FIFOAnalyzer::ShowSearchResult(size_t index) const auto& result = m_search_results[index]; QTreeWidgetItem* object_item = - m_tree_widget->topLevelItem(0)->child(result.m_frame)->child(result.m_object); + m_tree_widget->topLevelItem(0)->child(result.m_frame)->child(result.m_object_idx); m_tree_widget->setCurrentItem(object_item); m_detail_list->setCurrentRow(result.m_cmd); @@ -550,17 +594,18 @@ void FIFOAnalyzer::UpdateDescription() if (items.isEmpty() || m_object_data_offsets.empty()) return; - if (items[0]->data(0, FRAME_ROLE).isNull() || items[0]->data(0, OBJECT_ROLE).isNull()) + if (items[0]->data(0, FRAME_ROLE).isNull() || items[0]->data(0, PART_START_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 start_part_nr = items[0]->data(0, PART_START_ROLE).toUInt(); + const u32 end_part_nr = items[0]->data(0, PART_END_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 u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]); + const u32 object_start = frame_info.parts[start_part_nr].m_start; const u32 entry_start = m_object_data_offsets[entry_nr]; const u8* cmddata = &fifo_frame.fifoData[object_start + entry_start]; @@ -671,8 +716,8 @@ void FIFOAnalyzer::UpdateDescription() text = tr("Primitive %1").arg(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& vtx_desc = frame_info.parts[end_part_nr].m_cpmem.vtxDesc; + const auto& vtx_attr = frame_info.parts[end_part_nr].m_cpmem.vtxAttr[vat]; const auto component_sizes = VertexLoaderBase::GetVertexComponentSizes(vtx_desc, vtx_attr); u32 i = 3; diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h index 6a1c0a948a..222ce8e06b 100644 --- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h +++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.h @@ -58,15 +58,19 @@ private: struct SearchResult { - constexpr SearchResult(u32 frame, u32 object, u32 cmd) - : m_frame(frame), m_object(object), m_cmd(cmd) + constexpr SearchResult(u32 frame, u32 object_idx, u32 cmd) + : m_frame(frame), m_object_idx(object_idx), m_cmd(cmd) { } const u32 m_frame; - const u32 m_object; + // Index in tree view. Does not correspond with object numbers or part numbers. + const u32 m_object_idx; const u32 m_cmd; }; + // Offsets from the start of the first part in an object for each command within the currently + // selected object. std::vector m_object_data_offsets; + std::vector m_search_results; };