Merge pull request #4282 from zhaowenlan1779/frame-advance

core, citra_qt: add frame advancing to framelimiter
This commit is contained in:
Pengfei Zhu 2018-10-15 21:25:41 +08:00 committed by GitHub
commit 0df32275a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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