2013-04-18 02:43:11 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 02:43:11 +00:00
|
|
|
// Refer to the license.txt file included.
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2014-03-12 19:33:41 +00:00
|
|
|
#include <cctype>
|
|
|
|
|
2009-01-16 02:58:34 +00:00
|
|
|
#ifdef _WIN32
|
2010-06-26 12:52:57 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "AudioCommon/AudioCommon.h"
|
|
|
|
|
|
|
|
#include "Common/Atomic.h"
|
|
|
|
#include "Common/CommonPaths.h"
|
2014-09-08 01:06:58 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Common/CPUDetect.h"
|
|
|
|
#include "Common/MathUtil.h"
|
|
|
|
#include "Common/MemoryUtil.h"
|
|
|
|
#include "Common/StringUtil.h"
|
|
|
|
#include "Common/Thread.h"
|
|
|
|
#include "Common/Timer.h"
|
2014-06-05 23:29:54 +00:00
|
|
|
#include "Common/Logging/LogManager.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
|
|
|
|
#include "Core/ConfigManager.h"
|
|
|
|
#include "Core/Core.h"
|
2015-05-08 21:28:03 +00:00
|
|
|
#include "Core/CoreParameter.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/CoreTiming.h"
|
|
|
|
#include "Core/DSPEmulator.h"
|
|
|
|
#include "Core/Host.h"
|
|
|
|
#include "Core/MemTools.h"
|
|
|
|
#include "Core/Movie.h"
|
|
|
|
#include "Core/NetPlayProto.h"
|
|
|
|
#include "Core/PatchEngine.h"
|
|
|
|
#include "Core/State.h"
|
|
|
|
#include "Core/Boot/Boot.h"
|
|
|
|
#include "Core/FifoPlayer/FifoPlayer.h"
|
|
|
|
#include "Core/HW/AudioInterface.h"
|
|
|
|
#include "Core/HW/CPU.h"
|
|
|
|
#include "Core/HW/DSP.h"
|
|
|
|
#include "Core/HW/EXI.h"
|
2015-01-16 22:36:05 +00:00
|
|
|
#include "Core/HW/GCKeyboard.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/HW/GCPad.h"
|
|
|
|
#include "Core/HW/GPFifo.h"
|
|
|
|
#include "Core/HW/HW.h"
|
|
|
|
#include "Core/HW/Memmap.h"
|
|
|
|
#include "Core/HW/ProcessorInterface.h"
|
|
|
|
#include "Core/HW/SystemTimers.h"
|
|
|
|
#include "Core/HW/VideoInterface.h"
|
|
|
|
#include "Core/HW/Wiimote.h"
|
|
|
|
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
2015-05-08 21:28:03 +00:00
|
|
|
#include "Core/IPC_HLE/WII_IPC_HLE_WiiMote.h"
|
2014-09-06 21:26:40 +00:00
|
|
|
#include "Core/IPC_HLE/WII_Socket.h"
|
2014-09-06 10:41:17 +00:00
|
|
|
#include "Core/PowerPC/JitInterface.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
2014-02-19 18:09:14 +00:00
|
|
|
|
2013-01-06 10:28:27 +00:00
|
|
|
#ifdef USE_GDBSTUB
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/PowerPC/GDBStub.h"
|
2013-01-06 10:28:27 +00:00
|
|
|
#endif
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2014-02-19 18:09:14 +00:00
|
|
|
#include "DiscIO/FileMonitor.h"
|
2015-05-08 21:28:03 +00:00
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2015-02-19 06:33:50 +00:00
|
|
|
// This can mostly be removed when we move to VS2015
|
|
|
|
// to use the thread_local keyword
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#define ThreadLocalStorage __declspec(thread)
|
|
|
|
#elif defined __ANDROID__ || defined __APPLE__
|
|
|
|
// This will most likely have to stay, to support android
|
|
|
|
#include <pthread.h>
|
|
|
|
#else // Everything besides VS and Android
|
|
|
|
#define ThreadLocalStorage __thread
|
|
|
|
#endif
|
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
// TODO: ugly, remove
|
|
|
|
bool g_aspect_wide;
|
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
namespace Core
|
|
|
|
{
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2014-09-06 21:26:40 +00:00
|
|
|
bool g_want_determinism;
|
|
|
|
|
2009-01-04 21:53:41 +00:00
|
|
|
// Declarations and definitions
|
2014-09-04 12:26:11 +00:00
|
|
|
static Common::Timer s_timer;
|
|
|
|
static volatile u32 s_drawn_frame = 0;
|
|
|
|
static u32 s_drawn_video = 0;
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2009-01-04 21:53:41 +00:00
|
|
|
// Function forwarding
|
2010-06-05 19:03:37 +00:00
|
|
|
void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size);
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2009-01-22 11:18:46 +00:00
|
|
|
// Function declarations
|
2011-01-27 20:47:58 +00:00
|
|
|
void EmuThread();
|
2009-01-29 08:28:15 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
static bool s_is_stopping = false;
|
|
|
|
static bool s_hardware_initialized = false;
|
|
|
|
static bool s_is_started = false;
|
|
|
|
static void* s_window_handle = nullptr;
|
|
|
|
static std::string s_state_filename;
|
|
|
|
static std::thread s_emu_thread;
|
|
|
|
static StoppedCallbackFunc s_on_stopped_callback = nullptr;
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
static std::thread s_cpu_thread;
|
|
|
|
static bool s_request_refresh_info = false;
|
|
|
|
static int s_pause_and_lock_depth = 0;
|
|
|
|
static bool s_is_framelimiter_temp_disabled = false;
|
2010-04-17 21:02:03 +00:00
|
|
|
|
2015-02-19 06:33:50 +00:00
|
|
|
#ifdef ThreadLocalStorage
|
2015-02-22 19:08:28 +00:00
|
|
|
static ThreadLocalStorage bool tls_is_cpu_thread = false;
|
2015-02-19 06:33:50 +00:00
|
|
|
#else
|
|
|
|
static pthread_key_t s_tls_is_cpu_key;
|
|
|
|
static pthread_once_t s_cpu_key_is_init = PTHREAD_ONCE_INIT;
|
|
|
|
static void InitIsCPUKey()
|
|
|
|
{
|
|
|
|
pthread_key_create(&s_tls_is_cpu_key, nullptr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-04-30 10:50:29 +00:00
|
|
|
bool GetIsFramelimiterTempDisabled()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
return s_is_framelimiter_temp_disabled;
|
2014-04-30 10:50:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetIsFramelimiterTempDisabled(bool disable)
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_framelimiter_temp_disabled = disable;
|
2014-04-30 10:50:29 +00:00
|
|
|
}
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
std::string GetStateFileName() { return s_state_filename; }
|
|
|
|
void SetStateFileName(std::string val) { s_state_filename = val; }
|
2010-04-17 21:02:03 +00:00
|
|
|
|
2009-01-07 02:59:19 +00:00
|
|
|
// Display messages and return values
|
2009-09-07 12:40:43 +00:00
|
|
|
|
|
|
|
// Formatted stop message
|
|
|
|
std::string StopMessage(bool bMainThread, std::string Message)
|
|
|
|
{
|
|
|
|
return StringFromFormat("Stop [%s %i]\t%s\t%s",
|
2011-01-27 20:47:58 +00:00
|
|
|
bMainThread ? "Main Thread" : "Video Thread", Common::CurrentThreadId(), MemUsage().c_str(), Message.c_str());
|
2009-09-07 12:40:43 +00:00
|
|
|
}
|
|
|
|
|
2014-03-12 19:33:41 +00:00
|
|
|
void DisplayMessage(const std::string& message, int time_in_ms)
|
2009-01-07 02:59:19 +00:00
|
|
|
{
|
2015-04-20 22:12:25 +00:00
|
|
|
if (!IsRunning())
|
|
|
|
return;
|
|
|
|
|
2012-03-19 17:37:15 +00:00
|
|
|
// Actually displaying non-ASCII could cause things to go pear-shaped
|
2014-03-12 19:33:41 +00:00
|
|
|
for (const char& c : message)
|
|
|
|
{
|
|
|
|
if (!std::isprint(c))
|
2012-03-20 06:16:01 +00:00
|
|
|
return;
|
2014-03-12 19:33:41 +00:00
|
|
|
}
|
2012-03-19 17:37:15 +00:00
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
g_video_backend->Video_AddMessage(message, time_in_ms);
|
2014-08-29 21:45:39 +00:00
|
|
|
Host_UpdateTitle(message);
|
2009-01-07 02:59:19 +00:00
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
bool IsRunning()
|
2009-06-26 22:36:44 +00:00
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
return (GetState() != CORE_UNINITIALIZED) || s_hardware_initialized;
|
2009-06-26 22:36:44 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2011-12-31 04:16:12 +00:00
|
|
|
bool IsRunningAndStarted()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
return s_is_started && !s_is_stopping;
|
2011-12-31 04:16:12 +00:00
|
|
|
}
|
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
bool IsRunningInCurrentThread()
|
|
|
|
{
|
2011-12-31 04:16:12 +00:00
|
|
|
return IsRunning() && IsCPUThread();
|
2010-04-17 21:02:03 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2011-02-15 09:07:55 +00:00
|
|
|
bool IsCPUThread()
|
|
|
|
{
|
2015-02-19 06:33:50 +00:00
|
|
|
#ifdef ThreadLocalStorage
|
|
|
|
return tls_is_cpu_thread;
|
|
|
|
#else
|
|
|
|
// Use pthread implementation for Android and Mac
|
|
|
|
// Make sure that s_tls_is_cpu_key is initialized
|
|
|
|
pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
|
|
|
|
return pthread_getspecific(s_tls_is_cpu_key);
|
|
|
|
#endif
|
2011-02-15 09:07:55 +00:00
|
|
|
}
|
|
|
|
|
2011-12-31 04:16:12 +00:00
|
|
|
bool IsGPUThread()
|
|
|
|
{
|
|
|
|
const SCoreStartupParameter& _CoreParameter =
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
|
|
|
if (_CoreParameter.bCPUThread)
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
return (s_emu_thread.joinable() && (s_emu_thread.get_id() == std::this_thread::get_id()));
|
2011-12-31 04:16:12 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return IsCPUThread();
|
|
|
|
}
|
|
|
|
}
|
2013-02-26 19:49:00 +00:00
|
|
|
|
2010-02-02 08:10:23 +00:00
|
|
|
// This is called from the GUI thread. See the booting call schedule in
|
|
|
|
// BootManager.cpp
|
2009-01-18 01:45:53 +00:00
|
|
|
bool Init()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2011-02-17 17:51:18 +00:00
|
|
|
const SCoreStartupParameter& _CoreParameter =
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
if (s_emu_thread.joinable())
|
2009-01-22 11:18:46 +00:00
|
|
|
{
|
2014-06-20 00:43:57 +00:00
|
|
|
if (IsRunning())
|
|
|
|
{
|
|
|
|
PanicAlertT("Emu Thread already running");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Emu Thread was stopped, synchronize with it.
|
2014-09-04 12:26:11 +00:00
|
|
|
s_emu_thread.join();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2014-09-06 21:26:40 +00:00
|
|
|
Core::UpdateWantDeterminism(/*initial*/ true);
|
|
|
|
|
2011-02-17 17:51:18 +00:00
|
|
|
INFO_LOG(OSREPORT, "Starting core = %s mode",
|
2014-09-11 05:55:43 +00:00
|
|
|
_CoreParameter.bWii ? "Wii" : "GameCube");
|
2011-02-17 17:51:18 +00:00
|
|
|
INFO_LOG(OSREPORT, "CPU Thread separate = %s",
|
2014-09-11 05:55:43 +00:00
|
|
|
_CoreParameter.bCPUThread ? "Yes" : "No");
|
2010-07-29 12:17:47 +00:00
|
|
|
|
2011-02-17 17:51:18 +00:00
|
|
|
Host_UpdateMainFrame(); // Disable any menus or buttons at boot
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2011-02-17 17:51:18 +00:00
|
|
|
g_aspect_wide = _CoreParameter.bWii;
|
2013-08-25 00:49:58 +00:00
|
|
|
if (g_aspect_wide)
|
2011-02-17 17:51:18 +00:00
|
|
|
{
|
2013-09-23 06:39:14 +00:00
|
|
|
IniFile gameIni = _CoreParameter.LoadGameIni();
|
2014-06-16 05:12:43 +00:00
|
|
|
gameIni.GetOrCreateSection("Wii")->Get("Widescreen", &g_aspect_wide,
|
|
|
|
!!SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.AR"));
|
2011-02-17 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 00:08:25 +00:00
|
|
|
s_window_handle = Host_GetRenderHandle();
|
2011-02-17 17:51:18 +00:00
|
|
|
|
2013-08-25 00:49:58 +00:00
|
|
|
// Start the emu thread
|
2014-09-04 12:26:11 +00:00
|
|
|
s_emu_thread = std::thread(EmuThread);
|
2010-02-02 08:10:23 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
2009-02-17 22:48:16 +00:00
|
|
|
|
2010-07-19 02:09:34 +00:00
|
|
|
// Called from GUI thread
|
2009-07-03 12:22:32 +00:00
|
|
|
void Stop() // - Hammertime!
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2014-06-20 00:43:57 +00:00
|
|
|
if (GetState() == CORE_STOPPING)
|
2011-03-15 23:09:12 +00:00
|
|
|
return;
|
|
|
|
|
2011-02-18 14:27:30 +00:00
|
|
|
const SCoreStartupParameter& _CoreParameter =
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
2011-03-15 23:09:12 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_stopping = true;
|
2009-09-07 12:40:43 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
g_video_backend->EmuStateChange(EMUSTATE_CHANGE_STOP);
|
2009-09-07 12:40:43 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----");
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2009-02-22 21:16:12 +00:00
|
|
|
// Stop the CPU
|
2011-02-06 01:56:45 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stop CPU").c_str());
|
2009-02-17 22:48:16 +00:00
|
|
|
PowerPC::Stop();
|
2011-03-31 05:36:54 +00:00
|
|
|
|
2011-02-17 19:33:50 +00:00
|
|
|
// Kick it if it's waiting (code stepping wait loop)
|
|
|
|
CCPU::StepOpcode();
|
2009-07-03 12:22:32 +00:00
|
|
|
|
2009-12-30 16:26:16 +00:00
|
|
|
if (_CoreParameter.bCPUThread)
|
|
|
|
{
|
2011-02-17 19:33:50 +00:00
|
|
|
// Video_EnterLoop() should now exit so that EmuThread()
|
|
|
|
// will continue concurrently with the rest of the commands
|
|
|
|
// in this function. We no longer rely on Postmessage.
|
2011-03-15 23:09:12 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Wait for Video Loop to exit ...").c_str());
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
g_video_backend->Video_ExitLoop();
|
2010-01-01 03:55:39 +00:00
|
|
|
}
|
Join the emu thread in Core::Stop. Get rid of Core::Shutdown which did that before.
Core::Shutdown was only called on app exit, yet the emu thread exits
whenever emulation stops; if you launched a new game it would just join
via the destructor when s_emu_thread was set to a new thread.
(Incidentally, the destructor also makes explicitly joining on app exit
rather pointless.)
Because the GUI thread wasn't waiting for the CPU thread to fully shut
down, Core::IsRunning would remain true briefly after CFrame::DoStop
which, given Dolphin's penchant for accessing variables belonging to
other threads, can only mean trouble... In my case, because the previous
commit caused UpdateGUI, which is called at the end of DoStop, to call
PauseAndLock, which checks IsRunning, pressing stop at the right time
would cause strange behavior.
2015-04-13 06:09:39 +00:00
|
|
|
if (s_emu_thread.joinable())
|
|
|
|
s_emu_thread.join();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-02-22 21:16:12 +00:00
|
|
|
|
2015-02-19 06:33:50 +00:00
|
|
|
static void DeclareAsCPUThread()
|
|
|
|
{
|
|
|
|
#ifdef ThreadLocalStorage
|
|
|
|
tls_is_cpu_thread = true;
|
|
|
|
#else
|
|
|
|
// Use pthread implementation for Android and Mac
|
|
|
|
// Make sure that s_tls_is_cpu_key is initialized
|
|
|
|
pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
|
|
|
|
pthread_setspecific(s_tls_is_cpu_key, (void*)true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UndeclareAsCPUThread()
|
|
|
|
{
|
|
|
|
#ifdef ThreadLocalStorage
|
|
|
|
tls_is_cpu_thread = false;
|
|
|
|
#else
|
|
|
|
// Use pthread implementation for Android and Mac
|
|
|
|
// Make sure that s_tls_is_cpu_key is initialized
|
|
|
|
pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
|
|
|
|
pthread_setspecific(s_tls_is_cpu_key, (void*)false);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-02-18 14:27:30 +00:00
|
|
|
// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
|
2014-07-08 12:29:26 +00:00
|
|
|
static void CpuThread()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2015-02-19 06:33:50 +00:00
|
|
|
DeclareAsCPUThread();
|
|
|
|
|
2011-02-18 14:27:30 +00:00
|
|
|
const SCoreStartupParameter& _CoreParameter =
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
2009-07-15 15:09:20 +00:00
|
|
|
|
2009-10-23 15:26:35 +00:00
|
|
|
if (_CoreParameter.bCPUThread)
|
2009-01-05 22:51:35 +00:00
|
|
|
{
|
2009-07-15 15:09:20 +00:00
|
|
|
Common::SetCurrentThreadName("CPU thread");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Common::SetCurrentThreadName("CPU-GPU thread");
|
2011-02-18 14:27:30 +00:00
|
|
|
g_video_backend->Video_Prepare();
|
2009-01-05 22:51:35 +00:00
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2014-07-07 03:30:06 +00:00
|
|
|
if (_CoreParameter.bFastmem)
|
2011-02-18 14:27:30 +00:00
|
|
|
EMM::InstallExceptionHandler(); // Let's run under memory watch
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
if (!s_state_filename.empty())
|
|
|
|
State::LoadAs(s_state_filename);
|
2011-02-15 09:07:55 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_started = true;
|
2011-12-31 04:16:12 +00:00
|
|
|
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2013-01-08 07:26:07 +00:00
|
|
|
#ifdef USE_GDBSTUB
|
2015-05-04 23:15:16 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
if (!_CoreParameter.gdb_socket.empty())
|
|
|
|
{
|
|
|
|
gdb_init_local(_CoreParameter.gdb_socket.data());
|
|
|
|
gdb_break();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2014-03-10 11:30:55 +00:00
|
|
|
if (_CoreParameter.iGDBPort > 0)
|
2013-01-08 07:26:07 +00:00
|
|
|
{
|
|
|
|
gdb_init(_CoreParameter.iGDBPort);
|
2013-01-08 07:56:26 +00:00
|
|
|
// break at next instruction (the first instruction)
|
|
|
|
gdb_break();
|
2013-01-08 07:26:07 +00:00
|
|
|
}
|
|
|
|
#endif
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2009-02-20 22:04:52 +00:00
|
|
|
// Enter CPU run loop. When we leave it - we are done.
|
|
|
|
CCPU::Run();
|
2009-07-15 15:09:20 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_started = false;
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2013-02-26 15:42:32 +00:00
|
|
|
if (!_CoreParameter.bCPUThread)
|
|
|
|
g_video_backend->Video_Cleanup();
|
2011-12-31 04:16:12 +00:00
|
|
|
|
2015-02-24 10:52:37 +00:00
|
|
|
if (_CoreParameter.bFastmem)
|
|
|
|
EMM::UninstallExceptionHandler();
|
2014-09-07 23:10:02 +00:00
|
|
|
|
2011-01-27 20:47:58 +00:00
|
|
|
return;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2014-07-08 12:29:26 +00:00
|
|
|
static void FifoPlayerThread()
|
2011-03-27 02:55:08 +00:00
|
|
|
{
|
|
|
|
const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
|
|
|
|
|
|
|
if (_CoreParameter.bCPUThread)
|
|
|
|
{
|
|
|
|
Common::SetCurrentThreadName("FIFO player thread");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_video_backend->Video_Prepare();
|
|
|
|
Common::SetCurrentThreadName("FIFO-GPU thread");
|
|
|
|
}
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_started = true;
|
2015-04-28 01:19:28 +00:00
|
|
|
DeclareAsCPUThread();
|
2011-12-31 04:16:12 +00:00
|
|
|
|
2011-03-27 02:55:08 +00:00
|
|
|
// Enter CPU run loop. When we leave it - we are done.
|
|
|
|
if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename))
|
|
|
|
{
|
|
|
|
FifoPlayer::GetInstance().Play();
|
|
|
|
FifoPlayer::GetInstance().Close();
|
|
|
|
}
|
|
|
|
|
2015-04-28 01:19:28 +00:00
|
|
|
UndeclareAsCPUThread();
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_started = false;
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2014-03-10 11:30:55 +00:00
|
|
|
if (!_CoreParameter.bCPUThread)
|
2013-02-26 15:42:32 +00:00
|
|
|
g_video_backend->Video_Cleanup();
|
2011-12-31 04:16:12 +00:00
|
|
|
|
2011-03-27 02:55:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
// Initialize and create emulation thread
|
|
|
|
// Call browser: Init():s_emu_thread().
|
2011-02-18 14:27:30 +00:00
|
|
|
// See the BootManager.cpp file description for a complete call schedule.
|
2011-01-27 20:47:58 +00:00
|
|
|
void EmuThread()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2014-09-27 19:54:07 +00:00
|
|
|
const SCoreStartupParameter& core_parameter =
|
2011-02-18 14:27:30 +00:00
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
Common::SetCurrentThreadName("Emuthread - Starting");
|
2009-01-18 01:45:53 +00:00
|
|
|
|
2014-12-31 18:24:14 +00:00
|
|
|
if (SConfig::GetInstance().m_OCEnable)
|
|
|
|
DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000);
|
2011-03-05 16:36:47 +00:00
|
|
|
DisplayMessage(cpu_info.brand_string, 8000);
|
|
|
|
DisplayMessage(cpu_info.Summarize(), 8000);
|
2014-09-27 19:54:07 +00:00
|
|
|
DisplayMessage(core_parameter.m_strFilename, 3000);
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2015-02-19 06:33:50 +00:00
|
|
|
// For a time this acts as the CPU thread...
|
|
|
|
DeclareAsCPUThread();
|
|
|
|
|
2012-01-02 10:20:22 +00:00
|
|
|
Movie::Init();
|
|
|
|
|
2013-08-25 00:49:58 +00:00
|
|
|
HW::Init();
|
2011-03-31 20:33:11 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
if (!g_video_backend->Initialize(s_window_handle))
|
2011-03-31 05:36:54 +00:00
|
|
|
{
|
|
|
|
PanicAlert("Failed to initialize video backend!");
|
2011-06-18 04:04:48 +00:00
|
|
|
Host_Message(WM_USER_STOP);
|
2011-03-31 05:36:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-23 23:44:16 +00:00
|
|
|
OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000);
|
2011-03-31 05:36:54 +00:00
|
|
|
|
2014-10-29 12:22:17 +00:00
|
|
|
if (cpu_info.HTT)
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 4;
|
|
|
|
else
|
|
|
|
SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 2;
|
|
|
|
|
2014-09-27 19:54:07 +00:00
|
|
|
if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread))
|
2011-03-31 05:36:54 +00:00
|
|
|
{
|
|
|
|
HW::Shutdown();
|
|
|
|
g_video_backend->Shutdown();
|
|
|
|
PanicAlert("Failed to initialize DSP emulator!");
|
2011-06-18 04:04:48 +00:00
|
|
|
Host_Message(WM_USER_STOP);
|
2011-03-31 05:36:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-04 07:05:22 +00:00
|
|
|
bool init_controllers = false;
|
|
|
|
if (!g_controller_interface.IsInit())
|
|
|
|
{
|
|
|
|
Pad::Initialize(s_window_handle);
|
|
|
|
Keyboard::Initialize(s_window_handle);
|
|
|
|
init_controllers = true;
|
|
|
|
}
|
2015-03-05 08:49:10 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Update references in case controllers were refreshed
|
|
|
|
Pad::LoadConfig();
|
|
|
|
Keyboard::LoadConfig();
|
|
|
|
}
|
2015-01-16 22:36:05 +00:00
|
|
|
|
2014-11-14 02:28:27 +00:00
|
|
|
// Load and Init Wiimotes - only if we are booting in Wii mode
|
2014-09-27 19:54:07 +00:00
|
|
|
if (core_parameter.bWii)
|
2011-03-31 05:36:54 +00:00
|
|
|
{
|
2015-03-05 08:49:10 +00:00
|
|
|
if (init_controllers)
|
|
|
|
Wiimote::Initialize(s_window_handle, !s_state_filename.empty());
|
|
|
|
else
|
|
|
|
Wiimote::LoadConfig();
|
2011-03-31 05:36:54 +00:00
|
|
|
|
2015-01-13 03:28:12 +00:00
|
|
|
// Activate Wiimotes which don't have source set to "None"
|
2013-05-18 08:31:37 +00:00
|
|
|
for (unsigned int i = 0; i != MAX_BBMOTES; ++i)
|
2011-03-31 05:36:54 +00:00
|
|
|
if (g_wiimote_sources[i])
|
2013-05-18 08:31:37 +00:00
|
|
|
GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true);
|
2015-03-28 00:08:25 +00:00
|
|
|
|
2011-03-31 05:36:54 +00:00
|
|
|
}
|
|
|
|
|
2014-08-31 03:36:00 +00:00
|
|
|
AudioCommon::InitSoundStream();
|
2014-03-28 00:56:05 +00:00
|
|
|
|
2011-03-31 05:36:54 +00:00
|
|
|
// The hardware is initialized.
|
2014-09-04 12:26:11 +00:00
|
|
|
s_hardware_initialized = true;
|
2011-03-31 05:36:54 +00:00
|
|
|
|
|
|
|
// Boot to pause or not
|
2014-09-27 19:54:07 +00:00
|
|
|
Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN);
|
2011-03-31 05:36:54 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
// Load GCM/DOL/ELF whatever ... we boot with the interpreter core
|
|
|
|
PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
|
2011-03-16 01:59:24 +00:00
|
|
|
|
2009-01-18 01:45:53 +00:00
|
|
|
CBoot::BootUp();
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2015-02-19 06:33:50 +00:00
|
|
|
// Thread is no longer acting as CPU Thread
|
|
|
|
UndeclareAsCPUThread();
|
|
|
|
|
2009-02-20 22:04:52 +00:00
|
|
|
// Setup our core, but can't use dynarec if we are compare server
|
2015-02-19 15:29:21 +00:00
|
|
|
if (core_parameter.iCPUCore != PowerPC::CORE_INTERPRETER
|
2014-09-27 19:54:07 +00:00
|
|
|
&& (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient))
|
|
|
|
{
|
2008-12-08 05:30:24 +00:00
|
|
|
PowerPC::SetMode(PowerPC::MODE_JIT);
|
2014-09-27 19:54:07 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
else
|
2014-09-27 19:54:07 +00:00
|
|
|
{
|
2008-12-08 05:30:24 +00:00
|
|
|
PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
|
2014-09-27 19:54:07 +00:00
|
|
|
}
|
2009-07-15 15:09:20 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
// Update the window again because all stuff is initialized
|
|
|
|
Host_UpdateDisasmDialog();
|
|
|
|
Host_UpdateMainFrame();
|
|
|
|
|
2014-11-14 02:28:27 +00:00
|
|
|
// Determine the CPU thread function
|
2011-03-27 02:55:08 +00:00
|
|
|
void (*cpuThreadFunc)(void);
|
2014-09-27 19:54:07 +00:00
|
|
|
if (core_parameter.m_BootType == SCoreStartupParameter::BOOT_DFF)
|
2011-03-27 02:55:08 +00:00
|
|
|
cpuThreadFunc = FifoPlayerThread;
|
|
|
|
else
|
|
|
|
cpuThreadFunc = CpuThread;
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
// ENTER THE VIDEO THREAD LOOP
|
2014-09-27 19:54:07 +00:00
|
|
|
if (core_parameter.bCPUThread)
|
2009-07-15 15:09:20 +00:00
|
|
|
{
|
2011-02-18 14:27:30 +00:00
|
|
|
// This thread, after creating the EmuWindow, spawns a CPU
|
|
|
|
// thread, and then takes over and becomes the video thread
|
2009-07-15 15:09:20 +00:00
|
|
|
Common::SetCurrentThreadName("Video thread");
|
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
g_video_backend->Video_Prepare();
|
|
|
|
|
|
|
|
// Spawn the CPU thread
|
2014-09-04 12:26:11 +00:00
|
|
|
s_cpu_thread = std::thread(cpuThreadFunc);
|
2010-01-18 12:10:51 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
// become the GPU thread
|
2011-01-31 01:28:32 +00:00
|
|
|
g_video_backend->Video_EnterLoop();
|
2011-03-15 23:09:12 +00:00
|
|
|
|
|
|
|
// We have now exited the Video Loop
|
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str());
|
2009-07-15 15:09:20 +00:00
|
|
|
}
|
|
|
|
else // SingleCore mode
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2011-02-18 14:27:30 +00:00
|
|
|
// The spawned CPU Thread also does the graphics.
|
|
|
|
// The EmuThread is thus an idle thread, which sleeps while
|
|
|
|
// waiting for the program to terminate. Without this extra
|
|
|
|
// thread, the video backend window hangs in single core mode
|
2015-01-13 03:28:12 +00:00
|
|
|
// because no one is pumping messages.
|
2009-07-15 15:09:20 +00:00
|
|
|
Common::SetCurrentThreadName("Emuthread - Idle");
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
// Spawn the CPU+GPU thread
|
2014-09-04 12:26:11 +00:00
|
|
|
s_cpu_thread = std::thread(cpuThreadFunc);
|
2010-01-18 12:10:51 +00:00
|
|
|
|
2014-07-07 03:30:06 +00:00
|
|
|
while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN)
|
2009-02-20 22:04:52 +00:00
|
|
|
{
|
2011-01-31 01:28:32 +00:00
|
|
|
g_video_backend->PeekMessages();
|
2009-02-20 22:04:52 +00:00
|
|
|
Common::SleepCurrentThread(20);
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-02-22 21:16:12 +00:00
|
|
|
|
2014-06-20 00:43:57 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
// Wait for s_cpu_thread to exit
|
2011-03-15 23:09:12 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str());
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2013-01-06 10:28:27 +00:00
|
|
|
#ifdef USE_GDBSTUB
|
2013-01-08 07:26:07 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str());
|
|
|
|
gdb_deinit();
|
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str());
|
2013-01-06 10:28:27 +00:00
|
|
|
#endif
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_cpu_thread.join();
|
2010-01-01 03:55:39 +00:00
|
|
|
|
2013-01-08 07:26:07 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str());
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2014-09-27 19:54:07 +00:00
|
|
|
if (core_parameter.bCPUThread)
|
2013-02-26 15:42:32 +00:00
|
|
|
g_video_backend->Video_Cleanup();
|
2010-01-01 03:55:39 +00:00
|
|
|
|
2010-02-17 07:15:39 +00:00
|
|
|
FileMon::Close();
|
2011-03-31 05:36:54 +00:00
|
|
|
|
|
|
|
// Stop audio thread - Actually this does nothing when using HLE
|
|
|
|
// emulation, but stops the DSP Interpreter when using LLE emulation.
|
|
|
|
DSP::GetDSPEmulator()->DSP_StopSoundStream();
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2011-03-31 05:36:54 +00:00
|
|
|
// We must set up this flag before executing HW::Shutdown()
|
2014-09-04 12:26:11 +00:00
|
|
|
s_hardware_initialized = false;
|
2011-03-31 05:36:54 +00:00
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str());
|
|
|
|
HW::Shutdown();
|
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str());
|
2015-01-22 13:06:54 +00:00
|
|
|
|
2015-02-04 07:05:22 +00:00
|
|
|
if (init_controllers)
|
|
|
|
{
|
2015-03-28 00:08:25 +00:00
|
|
|
Wiimote::Shutdown();
|
2015-02-04 07:05:22 +00:00
|
|
|
Keyboard::Shutdown();
|
|
|
|
Pad::Shutdown();
|
|
|
|
init_controllers = false;
|
|
|
|
}
|
2015-01-22 13:06:54 +00:00
|
|
|
|
2011-03-31 05:36:54 +00:00
|
|
|
g_video_backend->Shutdown();
|
2014-03-28 00:56:05 +00:00
|
|
|
AudioCommon::ShutdownSoundStream();
|
2014-06-20 00:43:57 +00:00
|
|
|
|
|
|
|
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str());
|
|
|
|
|
|
|
|
// Clear on screen messages that haven't expired
|
|
|
|
g_video_backend->Video_ClearMessages();
|
|
|
|
|
|
|
|
// Reload sysconf file in order to see changes committed during emulation
|
2014-09-27 19:54:07 +00:00
|
|
|
if (core_parameter.bWii)
|
2014-06-20 00:43:57 +00:00
|
|
|
SConfig::GetInstance().m_SYSCONF->Reload();
|
|
|
|
|
|
|
|
INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----");
|
|
|
|
Movie::Shutdown();
|
|
|
|
PatchEngine::Shutdown();
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_is_stopping = false;
|
2014-06-20 00:43:57 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
if (s_on_stopped_callback)
|
|
|
|
s_on_stopped_callback();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2009-01-04 21:53:41 +00:00
|
|
|
// Set or get the running state
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2009-02-20 22:04:52 +00:00
|
|
|
void SetState(EState _State)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2009-02-20 22:04:52 +00:00
|
|
|
switch (_State)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
|
|
|
case CORE_PAUSE:
|
2009-02-17 23:07:10 +00:00
|
|
|
CCPU::EnableStepping(true); // Break
|
2013-05-31 05:12:59 +00:00
|
|
|
Wiimote::Pause();
|
2008-12-08 05:30:24 +00:00
|
|
|
break;
|
|
|
|
case CORE_RUN:
|
|
|
|
CCPU::EnableStepping(false);
|
2013-05-31 05:12:59 +00:00
|
|
|
Wiimote::Resume();
|
2008-12-08 05:30:24 +00:00
|
|
|
break;
|
|
|
|
default:
|
2011-01-13 02:05:58 +00:00
|
|
|
PanicAlertT("Invalid state");
|
2009-02-20 22:04:52 +00:00
|
|
|
break;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
EState GetState()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
if (s_is_stopping)
|
2014-06-20 01:26:06 +00:00
|
|
|
return CORE_STOPPING;
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
if (s_hardware_initialized)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
|
|
|
if (CCPU::IsStepping())
|
|
|
|
return CORE_PAUSE;
|
|
|
|
else
|
|
|
|
return CORE_RUN;
|
|
|
|
}
|
2014-06-20 01:26:06 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
return CORE_UNINITIALIZED;
|
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
static std::string GenerateScreenshotName()
|
2009-06-28 19:47:02 +00:00
|
|
|
{
|
2011-03-15 23:09:12 +00:00
|
|
|
const std::string& gameId = SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID();
|
|
|
|
std::string path = File::GetUserPath(D_SCREENSHOTS_IDX) + gameId + DIR_SEP_CHR;
|
2009-06-28 19:47:02 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
if (!File::CreateFullPath(path))
|
2011-03-01 03:06:14 +00:00
|
|
|
{
|
2011-03-15 23:09:12 +00:00
|
|
|
// fallback to old-style screenshots, without folder.
|
|
|
|
path = File::GetUserPath(D_SCREENSHOTS_IDX);
|
2010-01-15 16:11:06 +00:00
|
|
|
}
|
2009-06-28 19:47:02 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
//append gameId, path only contains the folder here.
|
|
|
|
path += gameId;
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
for (int i = 1; File::Exists(name = StringFromFormat("%s-%d.png", path.c_str(), i)); ++i)
|
2013-04-17 03:14:36 +00:00
|
|
|
{
|
|
|
|
// TODO?
|
|
|
|
}
|
2009-06-28 19:47:02 +00:00
|
|
|
|
|
|
|
return name;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-06-28 19:47:02 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
void SaveScreenShot()
|
2009-06-28 19:47:02 +00:00
|
|
|
{
|
2011-03-15 23:09:12 +00:00
|
|
|
const bool bPaused = (GetState() == CORE_PAUSE);
|
2009-06-28 19:47:02 +00:00
|
|
|
|
|
|
|
SetState(CORE_PAUSE);
|
2009-02-27 03:56:34 +00:00
|
|
|
|
2014-03-12 19:33:41 +00:00
|
|
|
g_video_backend->Video_Screenshot(GenerateScreenshotName());
|
2013-08-25 00:49:58 +00:00
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
if (!bPaused)
|
|
|
|
SetState(CORE_RUN);
|
2009-06-28 19:47:02 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2011-12-14 12:03:05 +00:00
|
|
|
void RequestRefreshInfo()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
s_request_refresh_info = true;
|
2011-12-14 12:03:05 +00:00
|
|
|
}
|
|
|
|
|
2011-12-31 04:16:12 +00:00
|
|
|
bool PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
|
|
|
{
|
2014-09-06 21:26:40 +00:00
|
|
|
if (!IsRunning())
|
|
|
|
return true;
|
|
|
|
|
2011-12-31 04:16:12 +00:00
|
|
|
// let's support recursive locking to simplify things on the caller's side,
|
|
|
|
// and let's do it at this outer level in case the individual systems don't support it.
|
2014-09-04 12:26:11 +00:00
|
|
|
if (doLock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth)
|
2011-12-31 04:16:12 +00:00
|
|
|
return true;
|
|
|
|
|
2014-11-14 02:28:27 +00:00
|
|
|
// first pause or unpause the CPU
|
2011-12-31 04:16:12 +00:00
|
|
|
bool wasUnpaused = CCPU::PauseAndLock(doLock, unpauseOnUnlock);
|
|
|
|
ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock);
|
|
|
|
|
2014-11-14 02:28:27 +00:00
|
|
|
// audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle).
|
2011-12-31 04:16:12 +00:00
|
|
|
AudioCommon::PauseAndLock(doLock, unpauseOnUnlock);
|
|
|
|
DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock);
|
|
|
|
|
2014-11-14 02:28:27 +00:00
|
|
|
// video has to come after CPU, because CPU thread can wait for video thread (s_efbAccessRequested).
|
2011-12-31 04:16:12 +00:00
|
|
|
g_video_backend->PauseAndLock(doLock, unpauseOnUnlock);
|
|
|
|
return wasUnpaused;
|
|
|
|
}
|
|
|
|
|
2010-01-07 20:01:41 +00:00
|
|
|
// Apply Frame Limit and Display FPS info
|
|
|
|
// This should only be called from VI
|
2010-01-08 11:25:51 +00:00
|
|
|
void VideoThrottle()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2010-01-07 20:01:41 +00:00
|
|
|
// Update info per second
|
2014-09-04 12:26:11 +00:00
|
|
|
u32 ElapseTime = (u32)s_timer.GetTimeDifference();
|
|
|
|
if ((ElapseTime >= 1000 && s_drawn_video > 0) || s_request_refresh_info)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2013-01-17 03:52:01 +00:00
|
|
|
UpdateTitle();
|
2009-08-01 18:16:12 +00:00
|
|
|
|
2010-01-08 11:25:51 +00:00
|
|
|
// Reset counter
|
2014-09-04 12:26:11 +00:00
|
|
|
s_timer.Update();
|
|
|
|
Common::AtomicStore(s_drawn_frame, 0);
|
|
|
|
s_drawn_video = 0;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2010-01-14 10:52:14 +00:00
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
s_drawn_video++;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-06-09 21:27:45 +00:00
|
|
|
|
2010-01-07 20:01:41 +00:00
|
|
|
// Executed from GPU thread
|
|
|
|
// reports if a frame should be skipped or not
|
|
|
|
// depending on the framelimit set
|
2011-03-15 23:09:12 +00:00
|
|
|
bool ShouldSkipFrame(int skipped)
|
2010-01-07 20:01:41 +00:00
|
|
|
{
|
2011-03-15 23:09:12 +00:00
|
|
|
const u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1)
|
2014-07-24 12:16:17 +00:00
|
|
|
? (SConfig::GetInstance().m_Framelimit - 1) * 5
|
2010-01-07 20:01:41 +00:00
|
|
|
: VideoInterface::TargetRefreshRate;
|
2014-09-04 12:26:11 +00:00
|
|
|
const u32 frames = Common::AtomicLoad(s_drawn_frame);
|
|
|
|
const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS);
|
2010-01-07 20:01:41 +00:00
|
|
|
|
|
|
|
return fps_slow;
|
|
|
|
}
|
|
|
|
|
2011-02-14 02:18:03 +00:00
|
|
|
// --- Callbacks for backends / engine ---
|
2010-01-07 20:01:41 +00:00
|
|
|
|
|
|
|
// Should be called from GPU thread when a frame is drawn
|
|
|
|
void Callback_VideoCopiedToXFB(bool video_update)
|
|
|
|
{
|
2014-03-10 11:30:55 +00:00
|
|
|
if (video_update)
|
2014-09-04 12:26:11 +00:00
|
|
|
Common::AtomicIncrement(s_drawn_frame);
|
2011-06-24 06:50:50 +00:00
|
|
|
Movie::FrameUpdate();
|
2010-01-07 20:01:41 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2013-01-17 03:52:01 +00:00
|
|
|
void UpdateTitle()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
u32 ElapseTime = (u32)s_timer.GetTimeDifference();
|
|
|
|
s_request_refresh_info = false;
|
2013-01-17 03:52:01 +00:00
|
|
|
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
|
|
|
|
|
|
|
if (ElapseTime == 0)
|
|
|
|
ElapseTime = 1;
|
|
|
|
|
2014-09-04 12:26:11 +00:00
|
|
|
float FPS = (float) (Common::AtomicLoad(s_drawn_frame) * 1000.0 / ElapseTime);
|
|
|
|
float VPS = (float) (s_drawn_video * 1000.0 / ElapseTime);
|
|
|
|
float Speed = (float) (s_drawn_video * (100 * 1000.0) / (VideoInterface::TargetRefreshRate * ElapseTime));
|
2013-01-17 03:52:01 +00:00
|
|
|
|
|
|
|
// Settings are shown the same for both extended and summary info
|
2014-02-17 04:51:41 +00:00
|
|
|
std::string SSettings = StringFromFormat("%s %s | %s | %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC",
|
2014-01-18 04:11:59 +00:00
|
|
|
g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE");
|
2013-01-17 03:52:01 +00:00
|
|
|
|
|
|
|
std::string SFPS;
|
2014-03-22 09:15:30 +00:00
|
|
|
|
2013-01-17 03:52:01 +00:00
|
|
|
if (Movie::IsPlayingInput())
|
2014-06-22 16:21:11 +00:00
|
|
|
SFPS = StringFromFormat("VI: %u/%u - Input: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed);
|
2013-01-17 03:52:01 +00:00
|
|
|
else if (Movie::IsRecordingInput())
|
2014-06-22 16:21:11 +00:00
|
|
|
SFPS = StringFromFormat("VI: %u - Input: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed);
|
2013-01-17 03:52:01 +00:00
|
|
|
else
|
2014-03-22 09:15:30 +00:00
|
|
|
{
|
2014-02-10 11:12:23 +00:00
|
|
|
SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed);
|
2014-03-22 09:15:30 +00:00
|
|
|
if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo)
|
|
|
|
{
|
|
|
|
// Use extended or summary information. The summary information does not print the ticks data,
|
|
|
|
// that's more of a debugging interest, it can always be optional of course if someone is interested.
|
|
|
|
static u64 ticks = 0;
|
|
|
|
static u64 idleTicks = 0;
|
|
|
|
u64 newTicks = CoreTiming::GetTicks();
|
|
|
|
u64 newIdleTicks = CoreTiming::GetIdleTicks();
|
|
|
|
|
|
|
|
u64 diff = (newTicks - ticks) / 1000000;
|
|
|
|
u64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
|
|
|
|
|
|
|
ticks = newTicks;
|
|
|
|
idleTicks = newIdleTicks;
|
|
|
|
|
|
|
|
float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100;
|
|
|
|
|
|
|
|
SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)",
|
|
|
|
_CoreParameter.bSkipIdle ? "~" : "",
|
|
|
|
(int)(diff),
|
|
|
|
(int)(diff - idleDiff),
|
|
|
|
(int)(idleDiff),
|
|
|
|
SystemTimers::GetTicksPerSecond() / 1000000,
|
|
|
|
_CoreParameter.bSkipIdle ? "~" : "",
|
|
|
|
TicksPercentage);
|
|
|
|
}
|
|
|
|
}
|
2013-01-17 03:52:01 +00:00
|
|
|
// This is our final "frame counter" string
|
2014-03-12 19:33:41 +00:00
|
|
|
std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str());
|
2013-01-17 03:52:01 +00:00
|
|
|
|
|
|
|
// Update the audio timestretcher with the current speed
|
2014-09-30 01:35:57 +00:00
|
|
|
if (g_sound_stream)
|
2013-01-17 03:52:01 +00:00
|
|
|
{
|
2014-09-30 01:35:57 +00:00
|
|
|
CMixer* pMixer = g_sound_stream->GetMixer();
|
2013-01-17 03:52:01 +00:00
|
|
|
pMixer->UpdateSpeed((float)Speed / 100);
|
|
|
|
}
|
|
|
|
|
2014-08-19 14:17:33 +00:00
|
|
|
Host_UpdateTitle(SMessage);
|
2014-06-20 00:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetOnStoppedCallback(StoppedCallbackFunc callback)
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
s_on_stopped_callback = callback;
|
2014-06-20 00:43:57 +00:00
|
|
|
}
|
|
|
|
|
2014-09-06 21:26:40 +00:00
|
|
|
void UpdateWantDeterminism(bool initial)
|
|
|
|
{
|
|
|
|
// For now, this value is not itself configurable. Instead, individual
|
|
|
|
// settings that depend on it, such as GPU determinism mode. should have
|
|
|
|
// override options for testing,
|
|
|
|
bool new_want_determinism =
|
|
|
|
Movie::IsPlayingInput() ||
|
|
|
|
Movie::IsRecordingInput() ||
|
|
|
|
NetPlay::IsNetPlayRunning();
|
|
|
|
if (new_want_determinism != g_want_determinism || initial)
|
|
|
|
{
|
|
|
|
WARN_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false");
|
|
|
|
|
|
|
|
bool was_unpaused = Core::PauseAndLock(true);
|
|
|
|
|
|
|
|
g_want_determinism = new_want_determinism;
|
|
|
|
WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
|
|
|
|
g_video_backend->UpdateWantDeterminism(new_want_determinism);
|
2014-09-06 10:41:17 +00:00
|
|
|
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use of FMA.
|
|
|
|
JitInterface::ClearCache();
|
2014-09-06 21:26:40 +00:00
|
|
|
|
|
|
|
Core::PauseAndLock(false, was_unpaused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 12:40:43 +00:00
|
|
|
} // Core
|