Merge pull request #4282 from zhaowenlan1779/frame-advance
core, citra_qt: add frame advancing to framelimiter
This commit is contained in:
commit
0df32275a7
4 changed files with 90 additions and 0 deletions
|
@ -352,6 +352,10 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
|
hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
|
hotkey_registry.RegisterHotkey("Main Window", "Toggle Frame Advancing", QKeySequence("CTRL+A"),
|
||||||
|
Qt::ApplicationShortcut);
|
||||||
|
hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash),
|
||||||
|
Qt::ApplicationShortcut);
|
||||||
hotkey_registry.LoadHotkeys();
|
hotkey_registry.LoadHotkeys();
|
||||||
|
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||||
|
@ -409,6 +413,10 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
UpdateStatusBar();
|
UpdateStatusBar();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(hotkey_registry.GetHotkey("Main Window", "Toggle Frame Advancing", this),
|
||||||
|
&QShortcut::activated, ui.action_Enable_Frame_Advancing, &QAction::trigger);
|
||||||
|
connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated,
|
||||||
|
ui.action_Advance_Frame, &QAction::trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::ShowUpdaterWidgets() {
|
void GMainWindow::ShowUpdaterWidgets() {
|
||||||
|
@ -540,6 +548,20 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect(ui.action_Play_Movie, &QAction::triggered, this, &GMainWindow::OnPlayMovie);
|
connect(ui.action_Play_Movie, &QAction::triggered, this, &GMainWindow::OnPlayMovie);
|
||||||
connect(ui.action_Stop_Recording_Playback, &QAction::triggered, this,
|
connect(ui.action_Stop_Recording_Playback, &QAction::triggered, this,
|
||||||
&GMainWindow::OnStopRecordingPlayback);
|
&GMainWindow::OnStopRecordingPlayback);
|
||||||
|
connect(ui.action_Enable_Frame_Advancing, &QAction::triggered, this, [this] {
|
||||||
|
if (emulation_running) {
|
||||||
|
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(
|
||||||
|
ui.action_Enable_Frame_Advancing->isChecked());
|
||||||
|
ui.action_Advance_Frame->setEnabled(ui.action_Enable_Frame_Advancing->isChecked());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(ui.action_Advance_Frame, &QAction::triggered, this, [this] {
|
||||||
|
if (emulation_running) {
|
||||||
|
ui.action_Enable_Frame_Advancing->setChecked(true);
|
||||||
|
ui.action_Advance_Frame->setEnabled(true);
|
||||||
|
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Help
|
// Help
|
||||||
connect(ui.action_FAQ, &QAction::triggered,
|
connect(ui.action_FAQ, &QAction::triggered,
|
||||||
|
@ -803,6 +825,9 @@ void GMainWindow::ShutdownGame() {
|
||||||
// TODO(bunnei): This function is not thread safe, but it's being used as if it were
|
// TODO(bunnei): This function is not thread safe, but it's being used as if it were
|
||||||
Pica::g_debug_context->ClearBreakpoints();
|
Pica::g_debug_context->ClearBreakpoints();
|
||||||
|
|
||||||
|
// Frame advancing must be cancelled in order to release the emu thread from waiting
|
||||||
|
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(false);
|
||||||
|
|
||||||
emit EmulationStopping();
|
emit EmulationStopping();
|
||||||
|
|
||||||
// Wait for emulation thread to complete and delete it
|
// Wait for emulation thread to complete and delete it
|
||||||
|
@ -823,6 +848,9 @@ void GMainWindow::ShutdownGame() {
|
||||||
ui.action_Stop->setEnabled(false);
|
ui.action_Stop->setEnabled(false);
|
||||||
ui.action_Restart->setEnabled(false);
|
ui.action_Restart->setEnabled(false);
|
||||||
ui.action_Report_Compatibility->setEnabled(false);
|
ui.action_Report_Compatibility->setEnabled(false);
|
||||||
|
ui.action_Enable_Frame_Advancing->setEnabled(false);
|
||||||
|
ui.action_Enable_Frame_Advancing->setChecked(false);
|
||||||
|
ui.action_Advance_Frame->setEnabled(false);
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
if (game_list->isEmpty())
|
if (game_list->isEmpty())
|
||||||
game_list_placeholder->show();
|
game_list_placeholder->show();
|
||||||
|
@ -1110,6 +1138,7 @@ void GMainWindow::OnStartGame() {
|
||||||
ui.action_Stop->setEnabled(true);
|
ui.action_Stop->setEnabled(true);
|
||||||
ui.action_Restart->setEnabled(true);
|
ui.action_Restart->setEnabled(true);
|
||||||
ui.action_Report_Compatibility->setEnabled(true);
|
ui.action_Report_Compatibility->setEnabled(true);
|
||||||
|
ui.action_Enable_Frame_Advancing->setEnabled(true);
|
||||||
|
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,9 @@
|
||||||
<addaction name="action_Record_Movie"/>
|
<addaction name="action_Record_Movie"/>
|
||||||
<addaction name="action_Play_Movie"/>
|
<addaction name="action_Play_Movie"/>
|
||||||
<addaction name="action_Stop_Recording_Playback"/>
|
<addaction name="action_Stop_Recording_Playback"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Enable_Frame_Advancing"/>
|
||||||
|
<addaction name="action_Advance_Frame"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Multiplayer">
|
<widget class="QMenu" name="menu_Multiplayer">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
@ -276,6 +279,25 @@
|
||||||
<string>Stop Recording / Playback</string>
|
<string>Stop Recording / Playback</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Enable_Frame_Advancing">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable Frame Advancing</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Advance_Frame">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Advance Frame</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_View_Lobby">
|
<action name="action_View_Lobby">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
|
@ -74,6 +74,13 @@ double PerfStats::GetLastFrameTimeScale() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||||
|
if (frame_advancing_enabled) {
|
||||||
|
// Frame advancing is enabled: wait on event instead of doing framelimiting
|
||||||
|
frame_advance_event.Wait();
|
||||||
|
frame_advance_event.Reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Settings::values.use_frame_limit) {
|
if (!Settings::values.use_frame_limit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -104,4 +111,20 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||||
previous_walltime = now;
|
previous_walltime = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameLimiter::SetFrameAdvancing(bool value) {
|
||||||
|
const bool was_enabled = frame_advancing_enabled.exchange(value);
|
||||||
|
if (was_enabled && !value) {
|
||||||
|
// Set the event to let emulation continue
|
||||||
|
frame_advance_event.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameLimiter::AdvanceFrame() {
|
||||||
|
if (!frame_advancing_enabled) {
|
||||||
|
// Start frame advancing
|
||||||
|
frame_advancing_enabled = true;
|
||||||
|
}
|
||||||
|
frame_advance_event.Set();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/thread.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
@ -70,6 +72,14 @@ public:
|
||||||
|
|
||||||
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
|
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether frame advancing is enabled or not.
|
||||||
|
* Note: The frontend must cancel frame advancing before shutting down in order
|
||||||
|
* to resume the emu_thread.
|
||||||
|
*/
|
||||||
|
void SetFrameAdvancing(bool value);
|
||||||
|
void AdvanceFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Emulated system time (in microseconds) at the last limiter invocation
|
/// Emulated system time (in microseconds) at the last limiter invocation
|
||||||
std::chrono::microseconds previous_system_time_us{0};
|
std::chrono::microseconds previous_system_time_us{0};
|
||||||
|
@ -78,6 +88,12 @@ private:
|
||||||
|
|
||||||
/// Accumulated difference between walltime and emulated time
|
/// Accumulated difference between walltime and emulated time
|
||||||
std::chrono::microseconds frame_limiting_delta_err{0};
|
std::chrono::microseconds frame_limiting_delta_err{0};
|
||||||
|
|
||||||
|
/// Whether to use frame advancing (i.e. frame by frame)
|
||||||
|
std::atomic_bool frame_advancing_enabled;
|
||||||
|
|
||||||
|
/// Event to advance the frame when frame advancing is enabled
|
||||||
|
Common::Event frame_advance_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
Loading…
Reference in a new issue