CheatSearch: Update Current Values at end of frame

At the end of each frame automatically update the Current Value for
visible table rows in the selected and visible CheatSearchWidget (if
any). Also update all Current Values in all CheatSearchWidgets when the
State changes to Paused.

Only updating visible table rows serves to minimize the performance cost
of this feature. If the user scrolls to an un-updated cell it will
promptly be updated by either the next VIEndFieldEvent or the State
transitioning to Paused.
This commit is contained in:
Dentomologist 2023-10-28 16:30:22 -07:00
parent 7dfb23d38c
commit fdb7328c73
6 changed files with 120 additions and 23 deletions

View File

@ -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();
}

View File

@ -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<Cheats::CheatSearchSessionBase> 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<int>(i), ADDRESS_TABLE_COLUMN_INDEX_CURRENT_VALUE);

View File

@ -39,6 +39,9 @@ public:
explicit CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionBase> 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<Cheats::CheatSearchSessionBase> m_session;

View File

@ -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<CheatSearchWidget*>(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<CheatSearchWidget*>(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)

View File

@ -11,14 +11,16 @@
#include <QDialog>
#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;
};

View File

@ -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">;