diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index 68b6564f86..c89ac88f84 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -33,6 +33,7 @@ #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" namespace VideoInterface { @@ -839,6 +840,7 @@ void VideoInterfaceManager::EndField(FieldType field, u64 ticks) OutputField(field, ticks); g_perf_metrics.CountVBlank(); + VIEndFieldEvent::Trigger(); Core::OnFrameEnd(); } diff --git a/Source/Core/DolphinQt/CheatSearchWidget.cpp b/Source/Core/DolphinQt/CheatSearchWidget.cpp index d595bb78b4..302bd645d0 100644 --- a/Source/Core/DolphinQt/CheatSearchWidget.cpp +++ b/Source/Core/DolphinQt/CheatSearchWidget.cpp @@ -263,6 +263,7 @@ void CheatSearchWidget::ConnectWidgets() void CheatSearchWidget::OnNextScanClicked() { + Core::CPUThreadGuard guard(Core::System::GetInstance()); const bool had_old_results = m_session->WasFirstSearchDone(); const size_t old_count = m_session->GetResultCount(); @@ -288,8 +289,7 @@ void CheatSearchWidget::OnNextScanClicked() } } - const Cheats::SearchErrorCode error_code = [this] { - Core::CPUThreadGuard guard(Core::System::GetInstance()); + const Cheats::SearchErrorCode error_code = [this, &guard] { return m_session->RunSearch(guard); }(); @@ -371,17 +371,11 @@ void CheatSearchWidget::OnNextScanClicked() } } -bool CheatSearchWidget::RefreshValues() +bool CheatSearchWidget::UpdateTableRows(const Core::CPUThreadGuard& guard, const size_t begin_index, + const size_t end_index) { - const size_t displayed_result_count = std::min(TABLE_MAX_ROWS, m_session->GetResultCount()); - if (displayed_result_count == 0) - { - m_info_label_1->setText(tr("Cannot refresh without results.")); - return false; - } - std::unique_ptr tmp = - m_session->ClonePartial(0, displayed_result_count); + m_session->ClonePartial(begin_index, end_index); tmp->SetFilterType(Cheats::FilterType::DoNotFilter); const Cheats::SearchErrorCode error_code = [&tmp] { @@ -395,7 +389,6 @@ bool CheatSearchWidget::RefreshValues() return false; } - m_address_table_current_values.clear(); const bool show_in_hex = m_display_values_in_hex_checkbox->isChecked(); const size_t result_count_to_display = tmp->GetResultCount(); for (size_t i = 0; i < result_count_to_display; ++i) @@ -404,18 +397,41 @@ bool CheatSearchWidget::RefreshValues() tmp->GetResultValueAsString(i, show_in_hex); } - RefreshGUICurrentValues(); + RefreshGUICurrentValues(begin_index, end_index); m_info_label_1->setText(tr("Refreshed current values.")); return true; } +void CheatSearchWidget::UpdateTableVisibleCurrentValues() +{ + Core::CPUThreadGuard guard(Core::System::GetInstance()); + if (m_address_table->rowCount() == 0) + return; + + UpdateTableRows(guard, GetVisibleRowsBeginIndex(), GetVisibleRowsEndIndex()); +} + +bool CheatSearchWidget::UpdateTableAllCurrentValues() +{ + Core::CPUThreadGuard guard(Core::System::GetInstance()); + const size_t result_count = m_address_table->rowCount(); + if (result_count == 0) + { + m_info_label_1->setText(tr("Cannot refresh without results.")); + return false; + } + + return UpdateTableRows(guard, 0, result_count); +} + void CheatSearchWidget::OnRefreshClicked() { - RefreshValues(); + UpdateTableAllCurrentValues(); } void CheatSearchWidget::OnResetClicked() { + Core::CPUThreadGuard guard(Core::System::GetInstance()); m_session->ResetResults(); m_address_table_current_values.clear(); @@ -441,6 +457,18 @@ void CheatSearchWidget::OnAddressTableItemChanged(QTableWidgetItem* item) } } +int CheatSearchWidget::GetVisibleRowsBeginIndex() const +{ + return m_address_table->rowAt(0); +} + +int CheatSearchWidget::GetVisibleRowsEndIndex() const +{ + const int row_at_bottom_of_viewport = m_address_table->rowAt(m_address_table->height()); + const int end_index = m_address_table->rowCount(); + return row_at_bottom_of_viewport == -1 ? end_index : row_at_bottom_of_viewport + 1; +} + void CheatSearchWidget::OnAddressTableContextMenu() { if (m_address_table->selectedItems().isEmpty()) @@ -474,12 +502,14 @@ void CheatSearchWidget::OnDisplayHexCheckboxStateChanged() if (!m_session->WasFirstSearchDone()) return; - if (!RefreshValues()) - RefreshGUICurrentValues(); + // If the game is running CheatsManager::OnFrameEnd will update values automatically. + if (Core::GetState() != Core::State::Running) + UpdateTableAllCurrentValues(); } void CheatSearchWidget::GenerateARCode() { + Core::CPUThreadGuard guard(Core::System::GetInstance()); if (m_address_table->selectedItems().isEmpty()) return; @@ -522,9 +552,9 @@ void CheatSearchWidget::RefreshCurrentValueTableItem( current_value_table_item->setText(QStringLiteral("---")); } -void CheatSearchWidget::RefreshGUICurrentValues() +void CheatSearchWidget::RefreshGUICurrentValues(const size_t begin_index, const size_t end_index) { - for (size_t i = 0; i < m_session->GetResultCount(); ++i) + for (size_t i = begin_index; i < end_index; ++i) { QTableWidgetItem* const current_value_table_item = m_address_table->item(static_cast(i), ADDRESS_TABLE_COLUMN_INDEX_CURRENT_VALUE); diff --git a/Source/Core/DolphinQt/CheatSearchWidget.h b/Source/Core/DolphinQt/CheatSearchWidget.h index 30b8cc5f44..a9955632ee 100644 --- a/Source/Core/DolphinQt/CheatSearchWidget.h +++ b/Source/Core/DolphinQt/CheatSearchWidget.h @@ -39,6 +39,9 @@ public: explicit CheatSearchWidget(std::unique_ptr session); ~CheatSearchWidget() override; + bool UpdateTableAllCurrentValues(); + void UpdateTableVisibleCurrentValues(); + signals: void ActionReplayCodeGenerated(const ActionReplay::ARCode& ar_code); void RequestWatch(QString name, u32 address); @@ -56,11 +59,13 @@ private: void OnValueSourceChanged(); void OnDisplayHexCheckboxStateChanged(); - bool RefreshValues(); void RefreshCurrentValueTableItem(QTableWidgetItem* current_value_table_item); - void RefreshGUICurrentValues(); + void RefreshGUICurrentValues(size_t begin_index, size_t end_index); + bool UpdateTableRows(const Core::CPUThreadGuard& guard, size_t begin_index, size_t end_index); void RecreateGUITable(); void GenerateARCode(); + int GetVisibleRowsBeginIndex() const; + int GetVisibleRowsEndIndex() const; std::unique_ptr m_session; diff --git a/Source/Core/DolphinQt/CheatsManager.cpp b/Source/Core/DolphinQt/CheatsManager.cpp index 743126f574..c083081549 100644 --- a/Source/Core/DolphinQt/CheatsManager.cpp +++ b/Source/Core/DolphinQt/CheatsManager.cpp @@ -19,8 +19,10 @@ #include "DolphinQt/CheatSearchWidget.h" #include "DolphinQt/Config/ARCodeWidget.h" #include "DolphinQt/Config/GeckoCodeWidget.h" +#include "DolphinQt/QtUtils/PartiallyClosableTabWidget.h" #include "DolphinQt/Settings.h" -#include "QtUtils/PartiallyClosableTabWidget.h" + +#include "VideoCommon/VideoEvents.h" CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent) { @@ -48,6 +50,49 @@ CheatsManager::~CheatsManager() void CheatsManager::OnStateChanged(Core::State state) { RefreshCodeTabs(state, false); + if (state == Core::State::Paused) + UpdateAllCheatSearchWidgetCurrentValues(); +} + +void CheatsManager::OnFrameEnd() +{ + if (!isVisible()) + return; + + auto* const selected_cheat_search_widget = + qobject_cast(m_tab_widget->currentWidget()); + if (selected_cheat_search_widget != nullptr) + selected_cheat_search_widget->UpdateTableVisibleCurrentValues(); +} + +void CheatsManager::UpdateAllCheatSearchWidgetCurrentValues() +{ + for (int i = 0; i < m_tab_widget->count(); ++i) + { + auto* const cheat_search_widget = qobject_cast(m_tab_widget->widget(i)); + if (cheat_search_widget != nullptr) + cheat_search_widget->UpdateTableAllCurrentValues(); + } +} + +void CheatsManager::RegisterAfterFrameEventCallback() +{ + m_VI_end_field_event = VIEndFieldEvent::Register([this] { OnFrameEnd(); }, "CheatsManager"); +} + +void CheatsManager::RemoveAfterFrameEventCallback() +{ + m_VI_end_field_event.reset(); +} + +void CheatsManager::hideEvent(QHideEvent* event) +{ + RemoveAfterFrameEventCallback(); +} + +void CheatsManager::showEvent(QShowEvent* event) +{ + RegisterAfterFrameEventCallback(); } void CheatsManager::RefreshCodeTabs(Core::State state, bool force) diff --git a/Source/Core/DolphinQt/CheatsManager.h b/Source/Core/DolphinQt/CheatsManager.h index 5e7ddb8bb2..2692999bb7 100644 --- a/Source/Core/DolphinQt/CheatsManager.h +++ b/Source/Core/DolphinQt/CheatsManager.h @@ -11,14 +11,16 @@ #include #include "Common/CommonTypes.h" -#include "DolphinQt/GameList/GameListModel.h" - #include "Core/CheatSearch.h" +#include "DolphinQt/GameList/GameListModel.h" +#include "VideoCommon/VideoEvents.h" class ARCodeWidget; class GeckoCodeWidget; class CheatSearchFactoryWidget; class QDialogButtonBox; +class QHideEvent; +class QShowEvent; class PartiallyClosableTabWidget; namespace Core @@ -38,14 +40,22 @@ signals: void ShowMemory(u32 address); void RequestWatch(QString name, u32 address); +protected: + void hideEvent(QHideEvent* event) override; + void showEvent(QShowEvent* event) override; + private: void CreateWidgets(); void ConnectWidgets(); void OnStateChanged(Core::State state); + void OnFrameEnd(); + void RegisterAfterFrameEventCallback(); + void RemoveAfterFrameEventCallback(); void OnNewSessionCreated(const Cheats::CheatSearchSessionBase& session); void OnTabCloseRequested(int index); void RefreshCodeTabs(Core::State state, bool force); + void UpdateAllCheatSearchWidgetCurrentValues(); std::string m_game_id; std::string m_game_tdb_id; @@ -57,4 +67,6 @@ private: ARCodeWidget* m_ar_code = nullptr; GeckoCodeWidget* m_gecko_code = nullptr; CheatSearchFactoryWidget* m_cheat_search_new = nullptr; + + Common::EventHook m_VI_end_field_event; }; diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index c611925b2c..191db9802c 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -81,3 +81,6 @@ using BeforePresentEvent = Common::HookableEvent<"BeforePresent", PresentInfo&>; // An event that is triggered after a frame is presented. // The exact timing of this event depends on backend/driver support. using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>; + +// An end of frame event that runs on the CPU thread +using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;