2013-04-18 02:43:11 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// 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"
|
|
|
|
#include "Core/CoreTiming.h"
|
|
|
|
#include "Core/DSPEmulator.h"
|
|
|
|
#include "Core/Host.h"
|
2015-03-21 10:56:19 +00:00
|
|
|
#include "Core/HotkeyManager.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/MemTools.h"
|
|
|
|
#include "Core/Movie.h"
|
|
|
|
#include "Core/NetPlayProto.h"
|
|
|
|
#include "Core/PatchEngine.h"
|
|
|
|
#include "Core/State.h"
|
|
|
|
#include "Core/VolumeHandler.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"
|
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"
|
|
|
|
|
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;
|
2015-03-21 10:56:19 +00:00
|
|
|
static bool s_window_handle_changed = false;
|
2014-09-04 12:26:11 +00:00
|
|
|
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
|
|
|
{
|
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-21 10:56:19 +00:00
|
|
|
s_window_handle_changed = false;
|
|
|
|
if (s_window_handle != Host_GetRenderHandle())
|
|
|
|
{
|
|
|
|
s_window_handle = Host_GetRenderHandle();
|
|
|
|
s_window_handle_changed = true;
|
|
|
|
}
|
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
|
|
|
}
|
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
|
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;
|
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();
|
|
|
|
}
|
|
|
|
|
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
|
2015-03-21 10:56:19 +00:00
|
|
|
if (s_window_handle_changed)
|
|
|
|
{
|
|
|
|
g_controller_interface.Initialize(s_window_handle);
|
|
|
|
HotkeyManagerEmu::LoadConfig();
|
|
|
|
}
|
2015-03-05 08:49:10 +00:00
|
|
|
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)
|
2015-03-19 10:31:05 +00:00
|
|
|
{
|
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-19 10:31:05 +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
|
|
|
VolumeHandler::EjectVolume();
|
|
|
|
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-19 10:31:05 +00:00
|
|
|
if (core_parameter.bWii)
|
|
|
|
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-03-12 19:33:41 +00:00
|
|
|
}
|
2013-01-17 03:52:01 +00:00
|
|
|
|
2014-06-20 00:43:57 +00:00
|
|
|
void Shutdown()
|
|
|
|
{
|
2014-09-04 12:26:11 +00:00
|
|
|
if (s_emu_thread.joinable())
|
|
|
|
s_emu_thread.join();
|
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
|