Qt: Restructured to remove unnecessary shutdown event and various cleanups.
This commit is contained in:
parent
3dd2688785
commit
e4ea133717
|
@ -28,9 +28,8 @@
|
||||||
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
|
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
|
||||||
|
|
||||||
EmuThread::EmuThread(GRenderWindow* render_window) :
|
EmuThread::EmuThread(GRenderWindow* render_window) :
|
||||||
exec_cpu_step(false), cpu_running(false), stop_run(false), render_window(render_window) {
|
exec_step(false), running(false), stop_run(false), render_window(render_window) {
|
||||||
|
|
||||||
shutdown_event.Reset();
|
|
||||||
connect(this, SIGNAL(started()), render_window, SLOT(moveContext()));
|
connect(this, SIGNAL(started()), render_window, SLOT(moveContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,20 +41,20 @@ void EmuThread::run() {
|
||||||
// next execution step
|
// next execution step
|
||||||
bool was_active = false;
|
bool was_active = false;
|
||||||
while (!stop_run) {
|
while (!stop_run) {
|
||||||
if (cpu_running) {
|
if (running) {
|
||||||
if (!was_active)
|
if (!was_active)
|
||||||
emit DebugModeLeft();
|
emit DebugModeLeft();
|
||||||
|
|
||||||
Core::RunLoop();
|
Core::RunLoop();
|
||||||
|
|
||||||
was_active = cpu_running || exec_cpu_step;
|
was_active = running || exec_step;
|
||||||
if (!was_active)
|
if (!was_active)
|
||||||
emit DebugModeEntered();
|
emit DebugModeEntered();
|
||||||
} else if (exec_cpu_step) {
|
} else if (exec_step) {
|
||||||
if (!was_active)
|
if (!was_active)
|
||||||
emit DebugModeLeft();
|
emit DebugModeLeft();
|
||||||
|
|
||||||
exec_cpu_step = false;
|
exec_step = false;
|
||||||
Core::SingleStep();
|
Core::SingleStep();
|
||||||
emit DebugModeEntered();
|
emit DebugModeEntered();
|
||||||
yieldCurrentThread();
|
yieldCurrentThread();
|
||||||
|
@ -65,40 +64,8 @@ void EmuThread::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
render_window->moveContext();
|
render_window->moveContext();
|
||||||
|
|
||||||
shutdown_event.Set();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::Stop() {
|
|
||||||
if (!isRunning()) {
|
|
||||||
LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stop_run = true;
|
|
||||||
|
|
||||||
// Release emu threads from any breakpoints, so that this doesn't hang forever.
|
|
||||||
Pica::g_debug_context->ClearBreakpoints();
|
|
||||||
|
|
||||||
//core::g_state = core::SYS_DIE;
|
|
||||||
|
|
||||||
// TODO: Waiting here is just a bad workaround for retarded shutdown logic.
|
|
||||||
wait(1000);
|
|
||||||
if (isRunning()) {
|
|
||||||
LOG_WARNING(Frontend, "EmuThread still running, terminating...");
|
|
||||||
quit();
|
|
||||||
|
|
||||||
// TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
|
|
||||||
// queued... This should be fixed.
|
|
||||||
wait(50000);
|
|
||||||
if (isRunning()) {
|
|
||||||
LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_INFO(Frontend, "EmuThread stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
|
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
|
||||||
// The corresponding functionality is handled in EmuThread instead
|
// The corresponding functionality is handled in EmuThread instead
|
||||||
class GGLWidgetInternal : public QGLWidget
|
class GGLWidgetInternal : public QGLWidget
|
||||||
|
|
|
@ -25,66 +25,46 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start emulation (on new thread)
|
* Start emulation (on new thread)
|
||||||
*
|
|
||||||
* @warning Only call when not running!
|
* @warning Only call when not running!
|
||||||
*/
|
*/
|
||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow the CPU to process a single instruction (if cpu is not running)
|
* Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
|
||||||
*
|
|
||||||
* @note This function is thread-safe
|
* @note This function is thread-safe
|
||||||
*/
|
*/
|
||||||
void ExecStep() { exec_cpu_step = true; }
|
void ExecStep() { exec_step = true; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether the CPU is running
|
* Sets whether the emulation thread is running or not
|
||||||
*
|
* @param running Boolean value, set the emulation thread to running if true
|
||||||
* @note This function is thread-safe
|
* @note This function is thread-safe
|
||||||
*/
|
*/
|
||||||
void SetCpuRunning(bool running) { cpu_running = running; }
|
void SetRunning(bool running) { this->running = running; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow the CPU to continue processing instructions without interruption
|
* Check if the emulation thread is running or not
|
||||||
*
|
* @return True if the emulation thread is running, otherwise false
|
||||||
* @note This function is thread-safe
|
* @note This function is thread-safe
|
||||||
*/
|
*/
|
||||||
bool IsCpuRunning() { return cpu_running; }
|
bool IsRunning() { return running; }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown (permantently stops) the CPU
|
* Shutdown (permanently stops) the emulation thread
|
||||||
*/
|
*/
|
||||||
void ShutdownCpu() { stop_run = true; };
|
void Shutdown() { stop_run = true; };
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for the CPU shutdown to complete
|
|
||||||
*/
|
|
||||||
void WaitForCpuShutdown() { shutdown_event.Wait(); }
|
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
/**
|
|
||||||
* Stop emulation and wait for the thread to finish.
|
|
||||||
*
|
|
||||||
* @details: This function will wait a second for the thread to finish; if it hasn't finished until then, we'll terminate() it and wait another second, hoping that it will be terminated by then.
|
|
||||||
* @note: This function is thread-safe.
|
|
||||||
*/
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class GMainWindow;
|
friend class GMainWindow;
|
||||||
|
|
||||||
EmuThread(GRenderWindow* render_window);
|
EmuThread(GRenderWindow* render_window);
|
||||||
|
|
||||||
bool exec_cpu_step;
|
bool exec_step;
|
||||||
bool cpu_running;
|
bool running;
|
||||||
std::atomic<bool> stop_run;
|
std::atomic<bool> stop_run;
|
||||||
|
|
||||||
GRenderWindow* render_window;
|
GRenderWindow* render_window;
|
||||||
|
|
||||||
Common::Event shutdown_event;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* Emitted when the CPU has halted execution
|
* Emitted when the CPU has halted execution
|
||||||
|
|
|
@ -201,7 +201,7 @@ void DisassemblerWidget::Init()
|
||||||
|
|
||||||
void DisassemblerWidget::OnContinue()
|
void DisassemblerWidget::OnContinue()
|
||||||
{
|
{
|
||||||
main_window.GetEmuThread()->SetCpuRunning(true);
|
main_window.GetEmuThread()->SetRunning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerWidget::OnStep()
|
void DisassemblerWidget::OnStep()
|
||||||
|
@ -211,13 +211,13 @@ void DisassemblerWidget::OnStep()
|
||||||
|
|
||||||
void DisassemblerWidget::OnStepInto()
|
void DisassemblerWidget::OnStepInto()
|
||||||
{
|
{
|
||||||
main_window.GetEmuThread()->SetCpuRunning(false);
|
main_window.GetEmuThread()->SetRunning(false);
|
||||||
main_window.GetEmuThread()->ExecStep();
|
main_window.GetEmuThread()->ExecStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerWidget::OnPause()
|
void DisassemblerWidget::OnPause()
|
||||||
{
|
{
|
||||||
main_window.GetEmuThread()->SetCpuRunning(false);
|
main_window.GetEmuThread()->SetRunning(false);
|
||||||
|
|
||||||
// TODO: By now, the CPU might not have actually stopped...
|
// TODO: By now, the CPU might not have actually stopped...
|
||||||
if (Core::g_app_core) {
|
if (Core::g_app_core) {
|
||||||
|
@ -227,7 +227,7 @@ void DisassemblerWidget::OnPause()
|
||||||
|
|
||||||
void DisassemblerWidget::OnToggleStartStop()
|
void DisassemblerWidget::OnToggleStartStop()
|
||||||
{
|
{
|
||||||
main_window.GetEmuThread()->SetCpuRunning(!main_window.GetEmuThread()->IsCpuRunning());
|
main_window.GetEmuThread()->SetRunning(!main_window.GetEmuThread()->IsRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerWidget::OnDebugModeEntered()
|
void DisassemblerWidget::OnDebugModeEntered()
|
||||||
|
@ -235,7 +235,7 @@ void DisassemblerWidget::OnDebugModeEntered()
|
||||||
ARMword next_instr = Core::g_app_core->GetPC();
|
ARMword next_instr = Core::g_app_core->GetPC();
|
||||||
|
|
||||||
if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
|
if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
|
||||||
main_window.GetEmuThread()->SetCpuRunning(false);
|
main_window.GetEmuThread()->SetRunning(false);
|
||||||
|
|
||||||
model->SetNextInstruction(next_instr);
|
model->SetNextInstruction(next_instr);
|
||||||
|
|
||||||
|
|
|
@ -199,10 +199,6 @@ void GMainWindow::OnDisplayTitleBars(bool show)
|
||||||
void GMainWindow::BootGame(std::string filename) {
|
void GMainWindow::BootGame(std::string filename) {
|
||||||
LOG_INFO(Frontend, "Citra starting...\n");
|
LOG_INFO(Frontend, "Citra starting...\n");
|
||||||
|
|
||||||
// Shutdown previous session if the emu thread is still active...
|
|
||||||
if (emu_thread != nullptr)
|
|
||||||
ShutdownGame();
|
|
||||||
|
|
||||||
System::Init(render_window);
|
System::Init(render_window);
|
||||||
|
|
||||||
// Load a game or die...
|
// Load a game or die...
|
||||||
|
@ -222,29 +218,36 @@ void GMainWindow::BootGame(std::string filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::ShutdownGame() {
|
void GMainWindow::ShutdownGame() {
|
||||||
emu_thread->SetCpuRunning(false);
|
// Shutdown the emulation thread and wait for it to complete
|
||||||
|
emu_thread->SetRunning(false);
|
||||||
emu_thread->ShutdownCpu();
|
emu_thread->Shutdown();
|
||||||
emu_thread->WaitForCpuShutdown();
|
emu_thread->wait();
|
||||||
emu_thread->Stop();
|
|
||||||
|
|
||||||
delete emu_thread;
|
delete emu_thread;
|
||||||
emu_thread = nullptr;
|
emu_thread = nullptr;
|
||||||
|
|
||||||
|
// Release emu threads from any breakpoints
|
||||||
|
Pica::g_debug_context->ClearBreakpoints();
|
||||||
|
|
||||||
|
// Shutdown the core emulation
|
||||||
System::Shutdown();
|
System::Shutdown();
|
||||||
|
|
||||||
|
// Update the GUI
|
||||||
ui.action_Start->setEnabled(true);
|
ui.action_Start->setEnabled(true);
|
||||||
ui.action_Pause->setEnabled(false);
|
ui.action_Pause->setEnabled(false);
|
||||||
ui.action_Stop->setEnabled(false);
|
ui.action_Stop->setEnabled(false);
|
||||||
|
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnMenuLoadFile()
|
void GMainWindow::OnMenuLoadFile()
|
||||||
{
|
{
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
|
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
|
||||||
if (filename.size())
|
if (filename.size()) {
|
||||||
|
// Shutdown previous session if the emu thread is still active...
|
||||||
|
if (emu_thread != nullptr)
|
||||||
|
ShutdownGame();
|
||||||
|
|
||||||
BootGame(filename.toLatin1().data());
|
BootGame(filename.toLatin1().data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnMenuLoadSymbolMap() {
|
void GMainWindow::OnMenuLoadSymbolMap() {
|
||||||
|
@ -255,7 +258,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
|
||||||
|
|
||||||
void GMainWindow::OnStartGame()
|
void GMainWindow::OnStartGame()
|
||||||
{
|
{
|
||||||
emu_thread->SetCpuRunning(true);
|
emu_thread->SetRunning(true);
|
||||||
|
|
||||||
ui.action_Start->setEnabled(false);
|
ui.action_Start->setEnabled(false);
|
||||||
ui.action_Pause->setEnabled(true);
|
ui.action_Pause->setEnabled(true);
|
||||||
|
@ -264,7 +267,7 @@ void GMainWindow::OnStartGame()
|
||||||
|
|
||||||
void GMainWindow::OnPauseGame()
|
void GMainWindow::OnPauseGame()
|
||||||
{
|
{
|
||||||
emu_thread->SetCpuRunning(false);
|
emu_thread->SetRunning(false);
|
||||||
|
|
||||||
ui.action_Start->setEnabled(true);
|
ui.action_Start->setEnabled(true);
|
||||||
ui.action_Pause->setEnabled(false);
|
ui.action_Pause->setEnabled(false);
|
||||||
|
|
Reference in New Issue