2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2016-11-05 02:34:06 -04:00
|
|
|
#include "Core/Core.h"
|
|
|
|
|
2019-12-30 10:48:11 +01:00
|
|
|
#include <algorithm>
|
2015-07-06 06:25:48 -04:00
|
|
|
#include <atomic>
|
2015-09-28 10:57:16 -05:00
|
|
|
#include <cstring>
|
2024-01-05 04:45:41 +01:00
|
|
|
#include <functional>
|
2016-05-12 01:18:30 +00:00
|
|
|
#include <mutex>
|
|
|
|
#include <queue>
|
|
|
|
#include <utility>
|
2017-05-27 15:43:40 +02:00
|
|
|
#include <variant>
|
2014-03-12 15:33:41 -04:00
|
|
|
|
2020-02-04 14:58:28 -05:00
|
|
|
#include <fmt/chrono.h>
|
2019-07-03 18:41:43 -04:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
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 05:18:15 -05:00
|
|
|
#include "AudioCommon/AudioCommon.h"
|
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
#include "Common/Assert.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/CPUDetect.h"
|
|
|
|
#include "Common/CommonPaths.h"
|
2014-09-07 20:06:58 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2019-06-29 18:18:24 +10:00
|
|
|
#include "Common/Event.h"
|
2021-06-10 19:54:07 +02:00
|
|
|
#include "Common/FPURoundMode.h"
|
2022-04-17 06:08:59 +02:00
|
|
|
#include "Common/FatFsUtil.h"
|
2017-01-15 21:46:32 +01:00
|
|
|
#include "Common/FileUtil.h"
|
2016-08-05 16:04:39 +02:00
|
|
|
#include "Common/Flag.h"
|
2019-07-03 18:41:43 -04:00
|
|
|
#include "Common/Logging/Log.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/MemoryUtil.h"
|
2017-03-02 12:15:02 -05:00
|
|
|
#include "Common/MsgHandler.h"
|
2017-05-27 18:48:40 +02:00
|
|
|
#include "Common/ScopeGuard.h"
|
2019-12-30 10:48:11 +01:00
|
|
|
#include "Common/StringUtil.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/Thread.h"
|
|
|
|
#include "Common/Timer.h"
|
2019-07-08 14:51:09 +02:00
|
|
|
#include "Common/Version.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
|
2023-04-11 00:04:08 -04:00
|
|
|
#include "Core/AchievementManager.h"
|
2019-04-28 04:23:13 -04:00
|
|
|
#include "Core/Boot/Boot.h"
|
2016-10-07 21:57:07 +02:00
|
|
|
#include "Core/BootManager.h"
|
2023-08-16 21:16:41 +02:00
|
|
|
#include "Core/CPUThreadConfigCallback.h"
|
2023-06-07 21:46:49 -04:00
|
|
|
#include "Core/Config/AchievementSettings.h"
|
2021-10-13 20:29:04 -04:00
|
|
|
#include "Core/Config/MainSettings.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/ConfigManager.h"
|
|
|
|
#include "Core/CoreTiming.h"
|
|
|
|
#include "Core/DSPEmulator.h"
|
2020-09-15 03:13:10 -07:00
|
|
|
#include "Core/DolphinAnalytics.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/FifoPlayer/FifoPlayer.h"
|
2020-06-12 00:25:06 -05:00
|
|
|
#include "Core/FreeLookManager.h"
|
2016-10-01 06:17:24 +00:00
|
|
|
#include "Core/HLE/HLE.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/HW/CPU.h"
|
|
|
|
#include "Core/HW/DSP.h"
|
2017-01-20 15:33:43 -05:00
|
|
|
#include "Core/HW/EXI/EXI.h"
|
2021-07-04 13:02:03 +02:00
|
|
|
#include "Core/HW/GBAPad.h"
|
2015-01-17 09:36:05 +11:00
|
|
|
#include "Core/HW/GCKeyboard.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/HW/GCPad.h"
|
|
|
|
#include "Core/HW/HW.h"
|
|
|
|
#include "Core/HW/SystemTimers.h"
|
|
|
|
#include "Core/HW/VideoInterface.h"
|
|
|
|
#include "Core/HW/Wiimote.h"
|
2019-04-28 04:23:13 -04:00
|
|
|
#include "Core/Host.h"
|
2017-04-29 16:11:22 +02:00
|
|
|
#include "Core/IOS/IOS.h"
|
2019-04-28 04:23:13 -04:00
|
|
|
#include "Core/MemTools.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/Movie.h"
|
2015-06-06 01:20:51 -04:00
|
|
|
#include "Core/NetPlayClient.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/NetPlayProto.h"
|
|
|
|
#include "Core/PatchEngine.h"
|
2021-10-01 09:56:26 -04:00
|
|
|
#include "Core/PowerPC/GDBStub.h"
|
2014-09-06 03:41:17 -07:00
|
|
|
#include "Core/PowerPC/JitInterface.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
|
|
#include "Core/State.h"
|
2022-01-07 03:50:18 +01:00
|
|
|
#include "Core/System.h"
|
2017-01-06 21:59:02 +01:00
|
|
|
#include "Core/WiiRoot.h"
|
2014-02-19 13:09:14 -05:00
|
|
|
|
2019-05-03 20:46:37 +01:00
|
|
|
#ifdef USE_MEMORYWATCHER
|
|
|
|
#include "Core/MemoryWatcher.h"
|
|
|
|
#endif
|
|
|
|
|
2021-10-03 06:23:33 +02:00
|
|
|
#include "DiscIO/RiivolutionPatcher.h"
|
|
|
|
|
2019-11-06 15:59:36 -06:00
|
|
|
#include "InputCommon/ControlReference/ControlReference.h"
|
2015-05-08 17:28:03 -04:00
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
2015-12-31 10:27:51 -06:00
|
|
|
#include "InputCommon/GCAdapter.h"
|
2017-02-12 13:02:57 +01:00
|
|
|
|
2023-03-20 01:23:51 -05:00
|
|
|
#include "VideoCommon/Assets/CustomAssetLoader.h"
|
2020-04-07 19:37:32 +02:00
|
|
|
#include "VideoCommon/AsyncRequests.h"
|
2016-01-12 09:55:29 +01:00
|
|
|
#include "VideoCommon/Fifo.h"
|
2023-01-27 17:03:15 +13:00
|
|
|
#include "VideoCommon/FrameDumper.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
2022-12-23 19:52:53 -05:00
|
|
|
#include "VideoCommon/PerformanceMetrics.h"
|
2023-01-27 17:03:15 +13:00
|
|
|
#include "VideoCommon/Present.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
2023-01-31 00:46:10 +13:00
|
|
|
#include "VideoCommon/VideoEvents.h"
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2008-07-12 17:40:22 +00:00
|
|
|
namespace Core
|
|
|
|
{
|
2017-04-03 13:30:58 -04:00
|
|
|
static bool s_wants_determinism;
|
2014-09-06 17:26:40 -04:00
|
|
|
|
2009-01-04 21:53:41 +00:00
|
|
|
// Declarations and definitions
|
2014-09-04 08:26:11 -04:00
|
|
|
static std::thread s_emu_thread;
|
2020-12-31 14:03:20 +02:00
|
|
|
static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2014-09-04 08:26:11 -04:00
|
|
|
static std::thread s_cpu_thread;
|
2015-12-16 00:10:47 +01:00
|
|
|
static bool s_is_throttler_temp_disabled = false;
|
2020-12-31 14:03:20 +02:00
|
|
|
static std::atomic<double> s_last_actual_emulation_speed{1.0};
|
2017-07-01 13:17:18 -07:00
|
|
|
static bool s_frame_step = false;
|
2020-04-07 19:37:32 +02:00
|
|
|
static std::atomic<bool> s_stop_frame_step;
|
2010-04-17 21:02:03 +00:00
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
// The value Paused is never stored in this variable. The core is considered to be in
|
|
|
|
// the Paused state if this variable is Running and the CPU reports that it's stepping.
|
|
|
|
static std::atomic<State> s_state = State::Uninitialized;
|
|
|
|
|
2017-02-25 20:58:33 -08:00
|
|
|
#ifdef USE_MEMORYWATCHER
|
|
|
|
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
|
|
|
#endif
|
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
struct HostJob
|
|
|
|
{
|
2024-03-18 01:35:42 -07:00
|
|
|
std::function<void(Core::System&)> job;
|
2016-05-12 01:18:30 +00:00
|
|
|
bool run_after_stop;
|
|
|
|
};
|
|
|
|
static std::mutex s_host_jobs_lock;
|
|
|
|
static std::queue<HostJob> s_host_jobs_queue;
|
2019-06-29 18:18:24 +10:00
|
|
|
static Common::Event s_cpu_thread_job_finished;
|
2016-05-12 01:18:30 +00:00
|
|
|
|
2018-04-17 19:00:34 -04:00
|
|
|
static thread_local bool tls_is_cpu_thread = false;
|
2020-04-25 00:26:51 +02:00
|
|
|
static thread_local bool tls_is_gpu_thread = false;
|
2023-06-02 18:00:51 +02:00
|
|
|
static thread_local bool tls_is_host_thread = false;
|
2015-02-19 00:33:50 -06:00
|
|
|
|
2024-01-05 04:45:41 +01:00
|
|
|
static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
|
|
|
|
WindowSystemInfo wsi);
|
2017-04-03 13:27:35 -04:00
|
|
|
|
2023-02-03 13:18:37 +13:00
|
|
|
static Common::EventHook s_frame_presented = AfterPresentEvent::Register(
|
2023-01-31 17:29:16 +13:00
|
|
|
[](auto& present_info) {
|
|
|
|
const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator();
|
|
|
|
// The denominator should always be > 0 but if it's not, just return 1
|
|
|
|
const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0;
|
2023-03-05 23:31:12 +13:00
|
|
|
|
|
|
|
if (present_info.reason != PresentInfo::PresentReason::VideoInterfaceDuplicate)
|
|
|
|
Core::Callback_FramePresented(last_speed);
|
2023-01-31 17:29:16 +13:00
|
|
|
},
|
|
|
|
"Core Frame Presented");
|
2023-01-31 00:46:10 +13:00
|
|
|
|
2015-12-16 00:10:47 +01:00
|
|
|
bool GetIsThrottlerTempDisabled()
|
2014-04-30 12:50:29 +02:00
|
|
|
{
|
2015-12-16 00:10:47 +01:00
|
|
|
return s_is_throttler_temp_disabled;
|
2014-04-30 12:50:29 +02:00
|
|
|
}
|
|
|
|
|
2015-12-16 00:10:47 +01:00
|
|
|
void SetIsThrottlerTempDisabled(bool disable)
|
2014-04-30 12:50:29 +02:00
|
|
|
{
|
2015-12-16 00:10:47 +01:00
|
|
|
s_is_throttler_temp_disabled = disable;
|
2014-04-30 12:50:29 +02:00
|
|
|
}
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2020-12-31 14:03:20 +02:00
|
|
|
double GetActualEmulationSpeed()
|
|
|
|
{
|
|
|
|
return s_last_actual_emulation_speed;
|
|
|
|
}
|
|
|
|
|
2015-06-06 01:20:51 -04:00
|
|
|
void FrameUpdateOnCPUThread()
|
|
|
|
{
|
|
|
|
if (NetPlay::IsNetPlayRunning())
|
2018-07-06 19:39:42 -04:00
|
|
|
NetPlay::NetPlayClient::SendTimeBase();
|
2015-06-06 01:20:51 -04:00
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void OnFrameEnd(Core::System& system)
|
2017-02-25 20:58:33 -08:00
|
|
|
{
|
|
|
|
#ifdef USE_MEMORYWATCHER
|
2019-07-10 20:43:15 +02:00
|
|
|
if (s_memory_watcher)
|
2023-02-12 11:07:11 +01:00
|
|
|
{
|
|
|
|
ASSERT(IsCPUThread());
|
2024-03-18 01:35:42 -07:00
|
|
|
const CPUThreadGuard guard(system);
|
2023-02-12 11:07:11 +01:00
|
|
|
|
|
|
|
s_memory_watcher->Step(guard);
|
|
|
|
}
|
2017-02-25 20:58:33 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-01-07 02:59:19 +00:00
|
|
|
// Display messages and return values
|
2009-09-07 12:40:43 +00:00
|
|
|
|
|
|
|
// Formatted stop message
|
2019-07-03 18:41:43 -04:00
|
|
|
std::string StopMessage(bool main_thread, std::string_view message)
|
2009-09-07 12:40:43 +00:00
|
|
|
{
|
2019-07-03 18:41:43 -04:00
|
|
|
return fmt::format("Stop [{} {}]\t{}", main_thread ? "Main Thread" : "Video Thread",
|
|
|
|
Common::CurrentThreadId(), message);
|
2009-09-07 12:40:43 +00:00
|
|
|
}
|
|
|
|
|
2019-07-28 22:46:08 -04:00
|
|
|
void DisplayMessage(std::string message, int time_in_ms)
|
2009-01-07 02:59:19 +00:00
|
|
|
{
|
2024-06-02 15:10:25 +02:00
|
|
|
if (!IsRunningOrStarting(Core::System::GetInstance()))
|
2015-04-20 18:12:25 -04:00
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-10-15 12:36:11 +02:00
|
|
|
// Actually displaying non-ASCII could cause things to go pear-shaped
|
2023-05-16 14:17:54 -04:00
|
|
|
if (!std::all_of(message.begin(), message.end(), Common::IsPrintableCharacter))
|
2019-12-30 10:48:11 +01:00
|
|
|
return;
|
2016-10-15 12:36:11 +02:00
|
|
|
|
2019-07-28 22:46:08 -04:00
|
|
|
OSD::AddMessage(std::move(message), time_in_ms);
|
2009-01-07 02:59:19 +00:00
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2024-04-08 20:33:55 -07:00
|
|
|
bool IsRunning(Core::System& system)
|
2009-06-26 22:36:44 +00:00
|
|
|
{
|
2024-06-02 15:10:25 +02:00
|
|
|
return s_state.load() == State::Running;
|
2009-06-26 22:36:44 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
bool IsRunningOrStarting(Core::System& system)
|
2011-12-30 20:16:12 -08:00
|
|
|
{
|
2024-06-02 15:10:25 +02:00
|
|
|
const State state = s_state.load();
|
|
|
|
return state == State::Running || state == State::Starting;
|
2011-12-30 20:16:12 -08:00
|
|
|
}
|
|
|
|
|
2011-02-15 09:07:55 +00:00
|
|
|
bool IsCPUThread()
|
|
|
|
{
|
2015-02-19 00:33:50 -06:00
|
|
|
return tls_is_cpu_thread;
|
2011-02-15 09:07:55 +00:00
|
|
|
}
|
|
|
|
|
2011-12-30 20:16:12 -08:00
|
|
|
bool IsGPUThread()
|
|
|
|
{
|
2020-04-25 00:26:51 +02:00
|
|
|
return tls_is_gpu_thread;
|
2011-12-30 20:16:12 -08:00
|
|
|
}
|
2013-02-26 13:49:00 -06:00
|
|
|
|
2023-06-02 18:00:51 +02:00
|
|
|
bool IsHostThread()
|
|
|
|
{
|
|
|
|
return tls_is_host_thread;
|
|
|
|
}
|
|
|
|
|
2017-04-03 13:30:58 -04:00
|
|
|
bool WantsDeterminism()
|
|
|
|
{
|
|
|
|
return s_wants_determinism;
|
|
|
|
}
|
|
|
|
|
2010-02-02 08:10:23 +00:00
|
|
|
// This is called from the GUI thread. See the booting call schedule in
|
|
|
|
// BootManager.cpp
|
2024-01-05 04:45:41 +01:00
|
|
|
bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2014-09-04 08:26:11 -04:00
|
|
|
if (s_emu_thread.joinable())
|
2009-01-22 11:18:46 +00:00
|
|
|
{
|
2024-06-26 20:34:16 +02:00
|
|
|
if (IsRunning(system))
|
2014-06-20 02:43:57 +02:00
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Emu Thread already running");
|
2014-06-20 02:43:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Emu Thread was stopped, synchronize with it.
|
2014-09-04 08:26:11 -04:00
|
|
|
s_emu_thread.join();
|
2008-07-27 20:51:02 +00:00
|
|
|
}
|
2009-02-20 22:04:52 +00:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
// Drain any left over jobs
|
2024-03-18 01:35:42 -07:00
|
|
|
HostDispatchJobs(system);
|
2016-05-12 01:18:30 +00:00
|
|
|
|
2024-01-31 02:56:56 +01:00
|
|
|
INFO_LOG_FMT(BOOT, "Starting core = {} mode", system.IsWii() ? "Wii" : "GameCube");
|
2024-01-05 04:45:41 +01:00
|
|
|
INFO_LOG_FMT(BOOT, "CPU Thread separate = {}", system.IsDualCoreMode() ? "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
|
|
|
|
2021-06-02 17:46:57 -04:00
|
|
|
// Manually reactivate the video backend in case a GameINI overrides the video backend setting.
|
2023-03-25 17:16:53 -07:00
|
|
|
VideoBackendBase::PopulateBackendInfo(wsi);
|
2021-06-02 17:46:57 -04:00
|
|
|
|
2018-11-07 05:10:51 -08:00
|
|
|
// Issue any API calls which must occur on the main thread for the graphics backend.
|
2020-03-11 23:10:28 +10:00
|
|
|
WindowSystemInfo prepared_wsi(wsi);
|
|
|
|
g_video_backend->PrepareWindow(prepared_wsi);
|
2018-11-07 05:10:51 -08:00
|
|
|
|
2013-08-25 02:49:58 +02:00
|
|
|
// Start the emu thread
|
2024-06-02 15:10:25 +02:00
|
|
|
s_state.store(State::Starting);
|
2024-01-05 04:45:41 +01:00
|
|
|
s_emu_thread = std::thread(EmuThread, std::ref(system), std::move(boot), prepared_wsi);
|
2008-07-12 17:40:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2009-02-17 22:48:16 +00:00
|
|
|
|
2018-03-25 22:23:46 -04:00
|
|
|
static void ResetRumble()
|
|
|
|
{
|
|
|
|
#if defined(__LIBUSB__)
|
|
|
|
GCAdapter::ResetRumble();
|
|
|
|
#endif
|
2018-03-27 16:24:36 +02:00
|
|
|
if (!Pad::IsInitialized())
|
|
|
|
return;
|
2018-03-25 22:23:46 -04:00
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
Pad::ResetRumble(i);
|
|
|
|
}
|
|
|
|
|
2010-07-19 02:09:34 +00:00
|
|
|
// Called from GUI thread
|
2024-03-18 01:35:42 -07:00
|
|
|
void Stop(Core::System& system) // - Hammertime!
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2024-06-02 15:10:25 +02:00
|
|
|
const State state = s_state.load();
|
|
|
|
if (state == State::Stopping || state == State::Uninitialized)
|
2011-03-15 23:09:12 +00:00
|
|
|
return;
|
|
|
|
|
2023-12-11 13:18:02 -05:00
|
|
|
AchievementManager::GetInstance().CloseGame();
|
2023-04-11 00:04:08 -04:00
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
s_state.store(State::Stopping);
|
2009-09-07 12:40:43 +00:00
|
|
|
|
2020-12-31 14:03:20 +02:00
|
|
|
CallOnStateChangedCallbacks(State::Stopping);
|
2018-11-30 01:20:22 -05:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
// Dump left over jobs
|
2024-03-18 01:35:42 -07:00
|
|
|
HostDispatchJobs(system);
|
2022-12-09 20:01:25 +01:00
|
|
|
|
|
|
|
system.GetFifo().EmulatorState(false);
|
2009-09-07 12:40:43 +00:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(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
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().Stop();
|
2009-07-03 12:22:32 +00:00
|
|
|
|
2022-12-09 20:01:25 +01:00
|
|
|
if (system.IsDualCoreMode())
|
2009-12-30 16:26:16 +00:00
|
|
|
{
|
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.
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Wait for Video Loop to exit ..."));
|
2013-08-25 02:49:58 +02:00
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
g_video_backend->Video_ExitLoop();
|
2010-01-01 03:55:39 +00:00
|
|
|
}
|
2020-12-31 14:03:20 +02:00
|
|
|
|
|
|
|
s_last_actual_emulation_speed = 1.0;
|
2008-07-12 17:40:22 +00:00
|
|
|
}
|
2009-02-22 21:16:12 +00:00
|
|
|
|
2015-06-06 01:41:26 -04:00
|
|
|
void DeclareAsCPUThread()
|
2015-02-19 00:33:50 -06:00
|
|
|
{
|
|
|
|
tls_is_cpu_thread = true;
|
|
|
|
}
|
|
|
|
|
2015-06-06 01:41:26 -04:00
|
|
|
void UndeclareAsCPUThread()
|
2015-02-19 00:33:50 -06:00
|
|
|
{
|
|
|
|
tls_is_cpu_thread = false;
|
|
|
|
}
|
|
|
|
|
2020-04-25 00:26:51 +02:00
|
|
|
void DeclareAsGPUThread()
|
|
|
|
{
|
|
|
|
tls_is_gpu_thread = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UndeclareAsGPUThread()
|
|
|
|
{
|
|
|
|
tls_is_gpu_thread = false;
|
|
|
|
}
|
|
|
|
|
2023-06-02 18:00:51 +02:00
|
|
|
void DeclareAsHostThread()
|
|
|
|
{
|
|
|
|
tls_is_host_thread = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UndeclareAsHostThread()
|
|
|
|
{
|
|
|
|
tls_is_host_thread = false;
|
|
|
|
}
|
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
// For the CPU Thread only.
|
2021-09-30 23:33:40 -04:00
|
|
|
static void CPUSetInitialExecutionState(bool force_paused = false)
|
2016-05-12 01:18:30 +00:00
|
|
|
{
|
2018-01-26 14:41:57 +10:00
|
|
|
// The CPU starts in stepping state, and will wait until a new state is set before executing.
|
|
|
|
// SetState must be called on the host thread, so we defer it for later.
|
2024-05-03 20:49:48 -07:00
|
|
|
QueueHostJob([force_paused](Core::System& system) {
|
2021-10-01 11:15:30 -04:00
|
|
|
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
|
2024-06-13 19:58:18 -04:00
|
|
|
SetState(system, paused ? State::Paused : State::Running, true, true);
|
2018-01-26 14:41:57 +10:00
|
|
|
Host_UpdateDisasmDialog();
|
2016-05-12 01:18:30 +00:00
|
|
|
Host_UpdateMainFrame();
|
2018-05-28 13:03:29 -04:00
|
|
|
Host_Message(HostMessageID::WMUserCreate);
|
2016-05-12 01:18:30 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2011-02-18 14:27:30 +00:00
|
|
|
// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
|
2024-01-05 04:45:41 +01:00
|
|
|
static void CpuThread(Core::System& system, const std::optional<std::string>& savestate_path,
|
|
|
|
bool delete_savestate)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2015-02-19 00:33:50 -06:00
|
|
|
DeclareAsCPUThread();
|
|
|
|
|
2024-01-05 04:45:41 +01:00
|
|
|
if (system.IsDualCoreMode())
|
2009-07-15 15:09:20 +00:00
|
|
|
Common::SetCurrentThreadName("CPU thread");
|
|
|
|
else
|
|
|
|
Common::SetCurrentThreadName("CPU-GPU thread");
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2016-06-18 02:43:59 +02:00
|
|
|
// This needs to be delayed until after the video backend is ready.
|
2019-06-23 19:26:07 +02:00
|
|
|
DolphinAnalytics::Instance().ReportGameStart();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-12-23 19:52:53 -05:00
|
|
|
// Clear performance data collected from previous threads.
|
|
|
|
g_perf_metrics.Reset();
|
|
|
|
|
2023-07-30 17:35:22 +02:00
|
|
|
// The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization.
|
|
|
|
const bool exception_handler = EMM::IsExceptionHandlerSupported();
|
|
|
|
if (exception_handler)
|
|
|
|
EMM::InstallExceptionHandler();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2019-05-03 20:46:37 +01:00
|
|
|
#ifdef USE_MEMORYWATCHER
|
2017-02-25 20:58:33 -08:00
|
|
|
s_memory_watcher = std::make_unique<MemoryWatcher>();
|
2019-05-03 20:46:37 +01:00
|
|
|
#endif
|
|
|
|
|
2018-01-26 14:41:57 +10:00
|
|
|
if (savestate_path)
|
|
|
|
{
|
2024-03-01 10:41:48 -08:00
|
|
|
::State::LoadAs(system, *savestate_path);
|
2018-01-26 14:41:57 +10:00
|
|
|
if (delete_savestate)
|
|
|
|
File::Delete(*savestate_path);
|
|
|
|
}
|
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
|
|
|
|
// by the host thread, don't change it.
|
|
|
|
State expected = State::Starting;
|
|
|
|
s_state.compare_exchange_strong(expected, State::Running);
|
|
|
|
|
2015-05-05 01:15:16 +02:00
|
|
|
{
|
2022-01-01 17:53:12 +01:00
|
|
|
#ifndef _WIN32
|
|
|
|
std::string gdb_socket = Config::Get(Config::MAIN_GDB_SOCKET);
|
2024-06-17 01:03:34 -04:00
|
|
|
if (!gdb_socket.empty() && !AchievementManager::GetInstance().IsHardcoreModeActive())
|
2022-01-01 17:53:12 +01:00
|
|
|
{
|
|
|
|
GDBStub::InitLocal(gdb_socket.data());
|
|
|
|
CPUSetInitialExecutionState(true);
|
|
|
|
}
|
|
|
|
else
|
2015-05-05 01:15:16 +02:00
|
|
|
#endif
|
2022-01-01 17:53:12 +01:00
|
|
|
{
|
|
|
|
int gdb_port = Config::Get(Config::MAIN_GDB_PORT);
|
2024-06-17 01:03:34 -04:00
|
|
|
if (gdb_port > 0 && !AchievementManager::GetInstance().IsHardcoreModeActive())
|
2022-01-01 17:53:12 +01:00
|
|
|
{
|
|
|
|
GDBStub::Init(gdb_port);
|
|
|
|
CPUSetInitialExecutionState(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CPUSetInitialExecutionState();
|
|
|
|
}
|
|
|
|
}
|
2013-01-08 20:26:07 +13:00
|
|
|
}
|
2013-08-25 02:49:58 +02:00
|
|
|
|
2009-02-20 22:04:52 +00:00
|
|
|
// Enter CPU run loop. When we leave it - we are done.
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().Run();
|
2009-07-15 15:09:20 +00:00
|
|
|
|
2019-12-09 20:06:07 +10:00
|
|
|
#ifdef USE_MEMORYWATCHER
|
|
|
|
s_memory_watcher.reset();
|
|
|
|
#endif
|
|
|
|
|
2023-07-30 17:35:22 +02:00
|
|
|
if (exception_handler)
|
2015-02-24 04:52:37 -06:00
|
|
|
EMM::UninstallExceptionHandler();
|
2021-10-01 01:57:40 -04:00
|
|
|
|
2021-10-01 11:15:30 -04:00
|
|
|
if (GDBStub::IsActive())
|
2021-10-01 01:57:40 -04:00
|
|
|
{
|
2021-10-01 11:15:30 -04:00
|
|
|
GDBStub::Deinit();
|
2021-10-01 01:57:40 -04:00
|
|
|
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
|
|
|
|
return;
|
|
|
|
}
|
2008-07-12 17:40:22 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2024-01-05 04:45:41 +01:00
|
|
|
static void FifoPlayerThread(Core::System& system, const std::optional<std::string>& savestate_path,
|
2017-12-28 10:13:53 +01:00
|
|
|
bool delete_savestate)
|
2011-03-27 02:55:08 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
DeclareAsCPUThread();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-28 20:26:52 +02:00
|
|
|
if (system.IsDualCoreMode())
|
2011-03-27 02:55:08 +00:00
|
|
|
Common::SetCurrentThreadName("FIFO player thread");
|
|
|
|
else
|
|
|
|
Common::SetCurrentThreadName("FIFO-GPU thread");
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2011-03-27 02:55:08 +00:00
|
|
|
// Enter CPU run loop. When we leave it - we are done.
|
2024-01-05 09:31:59 +01:00
|
|
|
if (auto cpu_core = system.GetFifoPlayer().GetCPUCore())
|
2011-03-27 02:55:08 +00:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().InjectExternalCPUCore(cpu_core.get());
|
2024-06-02 15:10:25 +02:00
|
|
|
|
|
|
|
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
|
|
|
|
// by the host thread, don't change it.
|
|
|
|
State expected = State::Starting;
|
|
|
|
s_state.compare_exchange_strong(expected, State::Running);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-05-27 15:43:40 +02:00
|
|
|
CPUSetInitialExecutionState();
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().Run();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().InjectExternalCPUCore(nullptr);
|
2024-01-05 09:31:59 +01:00
|
|
|
system.GetFifoPlayer().Close();
|
2011-03-27 02:55:08 +00:00
|
|
|
}
|
2018-01-26 14:41:57 +10:00
|
|
|
else
|
2016-05-12 09:17:17 +00:00
|
|
|
{
|
2018-01-26 14:41:57 +10:00
|
|
|
// FIFO log does not contain any frames, cannot continue.
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmt("FIFO file is invalid, cannot playback.");
|
2024-01-05 09:31:59 +01:00
|
|
|
system.GetFifoPlayer().Close();
|
2018-01-26 14:41:57 +10:00
|
|
|
return;
|
2016-05-12 09:17:17 +00:00
|
|
|
}
|
2011-03-27 02:55:08 +00:00
|
|
|
}
|
|
|
|
|
2014-09-04 08:26:11 -04: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.
|
2024-01-05 04:45:41 +01:00
|
|
|
static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
|
|
|
|
WindowSystemInfo wsi)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2020-12-31 14:03:20 +02:00
|
|
|
CallOnStateChangedCallbacks(State::Starting);
|
2017-05-27 18:48:40 +02:00
|
|
|
Common::ScopeGuard flag_guard{[] {
|
2024-06-02 15:10:25 +02:00
|
|
|
s_state.store(State::Uninitialized);
|
2017-05-27 18:48:40 +02:00
|
|
|
|
2020-12-31 14:03:20 +02:00
|
|
|
CallOnStateChangedCallbacks(State::Uninitialized);
|
2017-05-27 18:48:40 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "Stop\t\t---- Shutdown complete ----");
|
2017-05-27 18:48:40 +02:00
|
|
|
}};
|
|
|
|
|
2011-03-15 23:09:12 +00:00
|
|
|
Common::SetCurrentThreadName("Emuthread - Starting");
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-04-25 00:26:51 +02:00
|
|
|
DeclareAsGPUThread();
|
|
|
|
|
2015-02-19 00:33:50 -06:00
|
|
|
// For a time this acts as the CPU thread...
|
|
|
|
DeclareAsCPUThread();
|
2017-07-01 13:17:18 -07:00
|
|
|
s_frame_step = false;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-08-16 21:16:41 +02:00
|
|
|
// If settings have changed since the previous run, notify callbacks.
|
|
|
|
CPUThreadConfigCallback::CheckForConfigChanges();
|
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
// Switch the window used for inputs to the render window. This way, the cursor position
|
|
|
|
// is relative to the render window, instead of the main window.
|
|
|
|
ASSERT(g_controller_interface.IsInit());
|
|
|
|
g_controller_interface.ChangeWindow(wsi.render_window);
|
|
|
|
|
|
|
|
Pad::LoadConfig();
|
|
|
|
Pad::LoadGBAConfig();
|
|
|
|
Keyboard::LoadConfig();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-11-20 19:38:09 +01:00
|
|
|
BootSessionData boot_session_data = std::move(boot->boot_session_data);
|
|
|
|
const std::optional<std::string>& savestate_path = boot_session_data.GetSavestatePath();
|
|
|
|
const bool delete_savestate =
|
|
|
|
boot_session_data.GetDeleteSavestate() == DeleteSavestateAfterBoot::Yes;
|
2017-12-25 18:07:29 +01:00
|
|
|
|
2024-01-31 02:56:56 +01:00
|
|
|
bool sync_sd_folder = system.IsWii() && Config::Get(Config::MAIN_WII_SD_CARD) &&
|
2022-04-17 06:08:59 +02:00
|
|
|
Config::Get(Config::MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC);
|
|
|
|
if (sync_sd_folder)
|
2023-02-26 20:18:37 +01:00
|
|
|
{
|
|
|
|
sync_sd_folder =
|
|
|
|
Common::SyncSDFolderToSDImage([]() { return false; }, Core::WantsDeterminism());
|
|
|
|
}
|
2022-04-17 06:08:59 +02:00
|
|
|
|
|
|
|
Common::ScopeGuard sd_folder_sync_guard{[sync_sd_folder] {
|
|
|
|
if (sync_sd_folder && Config::Get(Config::MAIN_ALLOW_SD_WRITES))
|
2023-07-26 13:01:10 -06:00
|
|
|
{
|
|
|
|
const bool sync_ok = Common::SyncSDImageToSDFolder([]() { return false; });
|
|
|
|
if (!sync_ok)
|
|
|
|
{
|
|
|
|
PanicAlertFmtT(
|
|
|
|
"Failed to sync SD card with folder. All changes made this session will be "
|
|
|
|
"discarded on next boot if you do not manually re-issue a resync in Config > "
|
|
|
|
"Wii > SD Card Settings > Convert File to Folder Now!");
|
|
|
|
}
|
|
|
|
}
|
2022-04-17 06:08:59 +02:00
|
|
|
}};
|
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
// Load Wiimotes - only if we are booting in Wii mode
|
2024-01-31 02:56:56 +01:00
|
|
|
if (system.IsWii() && !Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
2011-03-31 05:36:54 +00:00
|
|
|
{
|
2022-06-25 17:31:31 +02:00
|
|
|
Wiimote::LoadConfig();
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
FreeLook::LoadInputConfig();
|
2017-05-27 18:48:40 +02:00
|
|
|
|
2023-03-20 01:23:51 -05:00
|
|
|
system.GetCustomAssetLoader().Init();
|
|
|
|
Common::ScopeGuard asset_loader_guard([&system] { system.GetCustomAssetLoader().Shutdown(); });
|
|
|
|
|
2024-01-13 13:14:48 +01:00
|
|
|
system.GetMovie().Init(*boot);
|
|
|
|
Common::ScopeGuard movie_guard([&system] { system.GetMovie().Shutdown(); });
|
2020-09-15 12:19:00 +02:00
|
|
|
|
2022-10-13 21:01:55 +02:00
|
|
|
AudioCommon::InitSoundStream(system);
|
|
|
|
Common::ScopeGuard audio_guard([&system] { AudioCommon::ShutdownSoundStream(system); });
|
2021-03-22 00:06:44 +01:00
|
|
|
|
2023-03-12 17:38:48 +01:00
|
|
|
HW::Init(system,
|
|
|
|
NetPlay::IsNetPlayRunning() ? &(boot_session_data.GetNetplaySettings()->sram) : nullptr);
|
2020-09-15 12:19:00 +02:00
|
|
|
|
2023-03-08 01:58:05 +01:00
|
|
|
Common::ScopeGuard hw_guard{[&system] {
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Shutting down HW"));
|
2023-03-12 17:38:48 +01:00
|
|
|
HW::Shutdown(system);
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "HW shutdown"));
|
2020-09-15 12:19:00 +02:00
|
|
|
|
|
|
|
// The config must be restored only after the whole HW has shut down,
|
|
|
|
// not when it is still running.
|
|
|
|
BootManager::RestoreConfig();
|
|
|
|
|
|
|
|
PatchEngine::Shutdown();
|
|
|
|
HLE::Clear();
|
2023-02-12 11:07:11 +01:00
|
|
|
|
2023-03-08 01:58:05 +01:00
|
|
|
CPUThreadGuard guard(system);
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().GetDebugInterface().Clear(guard);
|
2020-09-15 12:19:00 +02:00
|
|
|
}};
|
|
|
|
|
|
|
|
if (!g_video_backend->Initialize(wsi))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmt("Failed to initialize video backend!");
|
2020-09-15 12:19:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-10-17 01:42:22 +02:00
|
|
|
Common::ScopeGuard video_guard{[] {
|
|
|
|
// Clear on screen messages that haven't expired
|
|
|
|
OSD::ClearMessages();
|
|
|
|
|
|
|
|
g_video_backend->Shutdown();
|
|
|
|
}};
|
2020-09-15 12:19:00 +02:00
|
|
|
|
|
|
|
if (cpu_info.HTT)
|
2021-10-13 20:29:04 -04:00
|
|
|
Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4);
|
2020-09-15 12:19:00 +02:00
|
|
|
else
|
2021-10-13 20:29:04 -04:00
|
|
|
Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 2);
|
2020-09-15 12:19:00 +02:00
|
|
|
|
2024-01-31 02:56:56 +01:00
|
|
|
if (!system.GetDSP().GetDSPEmulator()->Initialize(system.IsWii(),
|
2023-03-09 02:36:43 +01:00
|
|
|
Config::Get(Config::MAIN_DSP_THREAD)))
|
2020-09-15 12:19:00 +02:00
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmt("Failed to initialize DSP emulation!");
|
2020-09-15 12:19:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-13 21:01:55 +02:00
|
|
|
AudioCommon::PostInitSoundStream(system);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-12 22:28:12 -05:00
|
|
|
// Set execution state to known values (CPU/FIFO/Audio Paused)
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().Break();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2011-03-31 05:36:54 +00:00
|
|
|
// Load GCM/DOL/ELF whatever ... we boot with the interpreter core
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().SetMode(PowerPC::CoreMode::Interpreter);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-05-27 15:43:40 +02:00
|
|
|
// Determine the CPU thread function
|
2024-01-05 04:45:41 +01:00
|
|
|
void (*cpuThreadFunc)(Core::System & system, const std::optional<std::string>& savestate_path,
|
|
|
|
bool delete_savestate);
|
2017-05-27 15:43:40 +02:00
|
|
|
if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
|
|
|
|
cpuThreadFunc = FifoPlayerThread;
|
|
|
|
else
|
|
|
|
cpuThreadFunc = CpuThread;
|
|
|
|
|
2021-10-03 06:23:33 +02:00
|
|
|
std::optional<DiscIO::Riivolution::SavegameRedirect> savegame_redirect = std::nullopt;
|
2024-01-31 02:56:56 +01:00
|
|
|
if (system.IsWii())
|
2021-10-03 06:23:33 +02:00
|
|
|
savegame_redirect = DiscIO::Riivolution::ExtractSavegameRedirect(boot->riivolution_patches);
|
|
|
|
|
2023-02-12 11:07:11 +01:00
|
|
|
{
|
|
|
|
ASSERT(IsCPUThread());
|
2023-03-08 01:58:05 +01:00
|
|
|
CPUThreadGuard guard(system);
|
2023-02-12 11:07:11 +01:00
|
|
|
if (!CBoot::BootUp(system, guard, std::move(boot)))
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-05-22 19:05:45 +02:00
|
|
|
// Initialise Wii filesystem contents.
|
2021-03-06 19:14:18 +01:00
|
|
|
// This is done here after Boot and not in BootManager to ensure that we operate
|
2018-05-22 19:05:45 +02:00
|
|
|
// with the correct title context since save copying requires title directories to exist.
|
2021-11-20 21:01:12 +01:00
|
|
|
Common::ScopeGuard wiifs_guard{[&boot_session_data] {
|
|
|
|
Core::CleanUpWiiFileSystemContents(boot_session_data);
|
|
|
|
boot_session_data.InvokeWiiSyncCleanup();
|
|
|
|
}};
|
2024-01-31 02:56:56 +01:00
|
|
|
if (system.IsWii())
|
2021-11-20 21:01:12 +01:00
|
|
|
Core::InitializeWiiFileSystemContents(savegame_redirect, boot_session_data);
|
2018-05-23 13:39:53 +02:00
|
|
|
else
|
|
|
|
wiifs_guard.Dismiss();
|
2018-05-22 19:05:45 +02:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
// This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block.
|
2023-12-18 21:31:32 -05:00
|
|
|
system.GetFifo().Prepare();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-01-05 21:26:17 +01:00
|
|
|
// Setup our core
|
|
|
|
if (Config::Get(Config::MAIN_CPU_CORE) != PowerPC::CPUCore::Interpreter)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().SetMode(PowerPC::CoreMode::JIT);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2008-07-12 17:40:22 +00:00
|
|
|
else
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
system.GetPowerPC().SetMode(PowerPC::CoreMode::Interpreter);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
UpdateTitle(system);
|
2023-01-21 00:44:25 +01:00
|
|
|
|
2015-02-19 00:33:50 -06:00
|
|
|
// ENTER THE VIDEO THREAD LOOP
|
2022-01-07 03:50:18 +01:00
|
|
|
if (system.IsDualCoreMode())
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2015-02-19 00:33:50 -06:00
|
|
|
// This thread, after creating the EmuWindow, spawns a CPU
|
2009-02-20 22:04:52 +00:00
|
|
|
// thread, and then takes over and becomes the video thread
|
2014-09-27 15:54:07 -04:00
|
|
|
Common::SetCurrentThreadName("Video thread");
|
2018-01-26 14:41:57 +10:00
|
|
|
UndeclareAsCPUThread();
|
2023-03-21 10:49:35 -04:00
|
|
|
Common::FPU::LoadDefaultSIMDState();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-01-26 14:41:57 +10:00
|
|
|
// Spawn the CPU thread. The CPU thread will signal the event that boot is complete.
|
2024-01-05 04:45:41 +01:00
|
|
|
s_cpu_thread =
|
|
|
|
std::thread(cpuThreadFunc, std::ref(system), std::ref(savestate_path), delete_savestate);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2008-07-12 17:40:22 +00:00
|
|
|
// become the GPU thread
|
2023-12-18 21:31:32 -05:00
|
|
|
system.GetFifo().RunGpuLoop();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2008-07-12 17:40:22 +00:00
|
|
|
// We have now exited the Video Loop
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Video Loop Ended"));
|
2018-01-26 12:44:23 +10:00
|
|
|
|
|
|
|
// Join with the CPU thread.
|
|
|
|
s_cpu_thread.join();
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "CPU thread stopped."));
|
2023-06-06 18:04:21 +02:00
|
|
|
|
|
|
|
// Redeclare this thread as the CPU thread, so that the code running in the scope guards doesn't
|
|
|
|
// think we're doing anything unsafe by doing stuff that could race with the CPU thread.
|
|
|
|
DeclareAsCPUThread();
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2014-09-27 15:54:07 -04:00
|
|
|
else // SingleCore mode
|
2009-07-15 15:09:20 +00:00
|
|
|
{
|
2018-01-26 12:44:23 +10:00
|
|
|
// Become the CPU thread
|
2024-01-05 04:45:41 +01:00
|
|
|
cpuThreadFunc(system, savestate_path, delete_savestate);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
|
2021-10-01 11:15:30 -04:00
|
|
|
GDBStub::Deinit();
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
|
2008-07-12 17:40:22 +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
|
|
|
|
2024-06-13 19:58:18 -04:00
|
|
|
void SetState(Core::System& system, State state, bool report_state_change,
|
|
|
|
bool initial_execution_state)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2016-05-12 01:18:30 +00:00
|
|
|
// State cannot be controlled until the CPU Thread is operational
|
2024-06-02 15:10:25 +02:00
|
|
|
if (s_state.load() != State::Running)
|
2016-05-12 01:18:30 +00:00
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
switch (state)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2017-02-05 07:39:58 -05:00
|
|
|
case State::Paused:
|
2024-06-13 19:58:18 -04:00
|
|
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
|
|
|
if (!initial_execution_state && !AchievementManager::GetInstance().CanPause())
|
|
|
|
return;
|
|
|
|
#endif // USE_RETRO_ACHIEVEMENTS
|
2017-02-05 07:39:58 -05:00
|
|
|
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
2016-05-12 09:17:17 +00:00
|
|
|
// stopped (including the CPU).
|
2024-06-15 11:15:23 +02:00
|
|
|
system.GetCPU().SetStepping(true); // Break
|
2013-05-31 07:12:59 +02:00
|
|
|
Wiimote::Pause();
|
2018-03-25 22:23:46 -04:00
|
|
|
ResetRumble();
|
2024-06-13 19:58:18 -04:00
|
|
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
|
|
|
AchievementManager::GetInstance().DoIdle();
|
|
|
|
#endif // USE_RETRO_ACHIEVEMENTS
|
2008-07-12 17:40:22 +00:00
|
|
|
break;
|
2017-02-05 07:39:58 -05:00
|
|
|
case State::Running:
|
2022-07-17 20:43:47 -07:00
|
|
|
{
|
2024-06-15 11:15:23 +02:00
|
|
|
system.GetCPU().SetStepping(false);
|
2013-05-31 07:12:59 +02:00
|
|
|
Wiimote::Resume();
|
2008-07-12 17:40:22 +00:00
|
|
|
break;
|
2022-07-17 20:43:47 -07:00
|
|
|
}
|
2008-07-12 17:40:22 +00:00
|
|
|
default:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmt("Invalid state");
|
2009-02-20 22:04:52 +00:00
|
|
|
break;
|
2008-07-12 17:40:22 +00:00
|
|
|
}
|
2017-09-04 10:57:42 -07:00
|
|
|
|
2023-11-16 11:07:37 -07:00
|
|
|
// Certain callers only change the state momentarily. Sending a callback for them causes
|
|
|
|
// unwanted updates, such as the Pause/Play button flickering between states on frame advance.
|
|
|
|
if (report_state_change)
|
2024-03-28 11:35:13 -07:00
|
|
|
CallOnStateChangedCallbacks(GetState(system));
|
2008-07-12 17:40:22 +00:00
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2024-03-28 11:35:13 -07:00
|
|
|
State GetState(Core::System& system)
|
2008-07-12 17:40:22 +00:00
|
|
|
{
|
2024-06-02 15:10:25 +02:00
|
|
|
const State state = s_state.load();
|
|
|
|
if (state == State::Running && system.GetCPU().IsStepping())
|
|
|
|
return State::Paused;
|
|
|
|
else
|
|
|
|
return state;
|
2019-11-04 17:15:13 +01:00
|
|
|
}
|
|
|
|
|
2015-06-23 23:08:10 -04:00
|
|
|
static std::string GenerateScreenshotFolderPath()
|
2009-06-28 19:47:02 +00:00
|
|
|
{
|
2016-10-29 14:42:43 +02:00
|
|
|
const std::string& gameId = SConfig::GetInstance().GetGameID();
|
2011-03-15 23:09:12 +00:00
|
|
|
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
|
|
|
|
2015-06-23 23:08:10 -04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string GenerateScreenshotName()
|
|
|
|
{
|
2011-03-15 23:09:12 +00:00
|
|
|
// append gameId, path only contains the folder here.
|
2019-08-26 19:31:12 +02:00
|
|
|
const std::string path_prefix =
|
|
|
|
GenerateScreenshotFolderPath() + SConfig::GetInstance().GetGameID();
|
|
|
|
|
|
|
|
const std::time_t cur_time = std::time(nullptr);
|
|
|
|
const std::string base_name =
|
2021-10-15 22:49:13 +02:00
|
|
|
fmt::format("{}_{:%Y-%m-%d_%H-%M-%S}", path_prefix, fmt::localtime(cur_time));
|
2011-03-15 23:09:12 +00:00
|
|
|
|
2019-08-26 19:31:12 +02:00
|
|
|
// First try a filename without any suffixes, if already exists then append increasing numbers
|
|
|
|
std::string name = fmt::format("{}.png", base_name);
|
|
|
|
if (File::Exists(name))
|
2011-03-15 23:09:12 +00:00
|
|
|
{
|
2019-08-26 19:31:12 +02:00
|
|
|
for (u32 i = 1; File::Exists(name = fmt::format("{}_{}.png", base_name, i)); ++i)
|
|
|
|
;
|
2011-03-15 23:09:12 +00:00
|
|
|
}
|
2009-06-28 19:47:02 +00:00
|
|
|
|
|
|
|
return name;
|
2008-08-26 23:40:18 +00:00
|
|
|
}
|
2009-06-28 19:47:02 +00:00
|
|
|
|
2020-08-06 21:57:12 +02:00
|
|
|
void SaveScreenShot()
|
2009-06-28 19:47:02 +00:00
|
|
|
{
|
2024-03-22 00:43:04 -07:00
|
|
|
const Core::CPUThreadGuard guard(Core::System::GetInstance());
|
|
|
|
g_frame_dumper->SaveScreenshot(GenerateScreenshotName());
|
2009-06-28 19:47:02 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2020-08-06 21:57:12 +02:00
|
|
|
void SaveScreenShot(std::string_view name)
|
2015-06-18 21:28:40 -04:00
|
|
|
{
|
2024-03-22 00:43:04 -07:00
|
|
|
const Core::CPUThreadGuard guard(Core::System::GetInstance());
|
|
|
|
g_frame_dumper->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name));
|
2015-06-18 21:28:40 -04:00
|
|
|
}
|
|
|
|
|
2023-03-07 04:02:48 +01:00
|
|
|
static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock)
|
2011-12-30 20:16:12 -08:00
|
|
|
{
|
2023-06-06 19:07:49 +02:00
|
|
|
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
|
2023-06-02 18:00:51 +02:00
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
if (!IsRunning(system))
|
2014-09-06 17:26:40 -04:00
|
|
|
return true;
|
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
bool was_unpaused = true;
|
|
|
|
if (do_lock)
|
|
|
|
{
|
|
|
|
// first pause the CPU
|
|
|
|
// This acquires a wrapper mutex and converts the current thread into
|
|
|
|
// a temporary replacement CPU Thread.
|
2023-03-07 04:02:48 +01:00
|
|
|
was_unpaused = system.GetCPU().PauseAndLock(true);
|
2016-05-12 09:17:17 +00:00
|
|
|
}
|
|
|
|
|
2014-11-13 21:28:27 -05:00
|
|
|
// audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle).
|
2023-06-01 16:56:22 -07:00
|
|
|
system.GetDSP().GetDSPEmulator()->PauseAndLock(do_lock);
|
2011-12-30 20:16:12 -08:00
|
|
|
|
2014-11-13 21:28:27 -05:00
|
|
|
// video has to come after CPU, because CPU thread can wait for video thread
|
|
|
|
// (s_efbAccessRequested).
|
2023-12-18 21:31:32 -05:00
|
|
|
system.GetFifo().PauseAndLock(do_lock, false);
|
2015-06-06 01:41:26 -04:00
|
|
|
|
2018-03-25 22:23:46 -04:00
|
|
|
ResetRumble();
|
2016-05-12 09:17:17 +00:00
|
|
|
|
|
|
|
// CPU is unlocked last because CPU::PauseAndLock contains the synchronization
|
|
|
|
// mechanism that prevents CPU::Break from racing.
|
|
|
|
if (!do_lock)
|
|
|
|
{
|
|
|
|
// The CPU is responsible for managing the Audio and FIFO state so we use its
|
|
|
|
// mechanism to unpause them. If we unpaused the systems above when releasing
|
|
|
|
// the locks then they could call CPU::Break which would require detecting it
|
2024-06-15 11:15:23 +02:00
|
|
|
// and re-pausing with CPU::SetStepping.
|
2023-03-07 04:02:48 +01:00
|
|
|
was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true);
|
2016-05-12 09:17:17 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
return was_unpaused;
|
2011-12-30 20:16:12 -08:00
|
|
|
}
|
|
|
|
|
2024-03-22 00:24:26 -07:00
|
|
|
void RunOnCPUThread(Core::System& system, std::function<void()> function, bool wait_for_completion)
|
2019-06-29 18:18:24 +10:00
|
|
|
{
|
|
|
|
// If the CPU thread is not running, assume there is no active CPU thread we can race against.
|
2024-04-08 20:33:55 -07:00
|
|
|
if (!IsRunning(system) || IsCPUThread())
|
2019-06-29 18:18:24 +10:00
|
|
|
{
|
|
|
|
function();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pause the CPU (set it to stepping mode).
|
2023-03-07 04:02:48 +01:00
|
|
|
const bool was_running = PauseAndLock(system, true, true);
|
2019-06-29 18:18:24 +10:00
|
|
|
|
|
|
|
// Queue the job function.
|
|
|
|
if (wait_for_completion)
|
|
|
|
{
|
|
|
|
// Trigger the event after executing the function.
|
|
|
|
s_cpu_thread_job_finished.Reset();
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().AddCPUThreadJob([&function]() {
|
2019-06-29 18:18:24 +10:00
|
|
|
function();
|
|
|
|
s_cpu_thread_job_finished.Set();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().AddCPUThreadJob(std::move(function));
|
2019-06-29 18:18:24 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// Release the CPU thread, and let it execute the callback.
|
2023-03-07 04:02:48 +01:00
|
|
|
PauseAndLock(system, false, was_running);
|
2019-06-29 18:18:24 +10:00
|
|
|
|
|
|
|
// If we're waiting for completion, block until the event fires.
|
|
|
|
if (wait_for_completion)
|
|
|
|
{
|
|
|
|
// Periodically yield to the UI thread, so we don't deadlock.
|
|
|
|
while (!s_cpu_thread_job_finished.WaitFor(std::chrono::milliseconds(10)))
|
|
|
|
Host_YieldToUI();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-14 02:18:03 +00:00
|
|
|
// --- Callbacks for backends / engine ---
|
2010-01-07 20:01:41 +00:00
|
|
|
|
2020-04-09 00:21:04 +02:00
|
|
|
// Called from Renderer::Swap (GPU thread) when a new (non-duplicate)
|
|
|
|
// frame is presented to the host screen
|
2020-12-31 14:03:20 +02:00
|
|
|
void Callback_FramePresented(double actual_emulation_speed)
|
2010-01-07 20:01:41 +00:00
|
|
|
{
|
2022-12-23 19:52:53 -05:00
|
|
|
g_perf_metrics.CountFrame();
|
2020-12-31 14:03:20 +02:00
|
|
|
|
2022-12-23 19:52:53 -05:00
|
|
|
s_last_actual_emulation_speed = actual_emulation_speed;
|
2020-04-07 19:37:32 +02:00
|
|
|
s_stop_frame_step.store(true);
|
2019-11-18 15:05:06 -06:00
|
|
|
}
|
2015-07-06 06:25:48 -04:00
|
|
|
|
2020-04-09 00:21:04 +02:00
|
|
|
// Called from VideoInterface::Update (CPU thread) at emulated field boundaries
|
2023-03-07 04:02:48 +01:00
|
|
|
void Callback_NewField(Core::System& system)
|
2019-11-18 15:05:06 -06:00
|
|
|
{
|
2017-07-01 13:17:18 -07:00
|
|
|
if (s_frame_step)
|
|
|
|
{
|
2020-04-07 19:37:32 +02:00
|
|
|
// To ensure that s_stop_frame_step is up to date, wait for the GPU thread queue to empty,
|
|
|
|
// since it is may contain a swap event (which will call Callback_FramePresented). This hurts
|
|
|
|
// the performance a little, but luckily, performance matters less when using frame stepping.
|
|
|
|
AsyncRequests::GetInstance()->WaitForEmptyQueue();
|
|
|
|
|
|
|
|
// Only stop the frame stepping if a new frame was displayed
|
|
|
|
// (as opposed to the previous frame being displayed for another frame).
|
|
|
|
if (s_stop_frame_step.load())
|
|
|
|
{
|
|
|
|
s_frame_step = false;
|
2023-03-07 04:02:48 +01:00
|
|
|
system.GetCPU().Break();
|
2024-03-28 11:35:13 -07:00
|
|
|
CallOnStateChangedCallbacks(Core::GetState(system));
|
2020-04-07 19:37:32 +02:00
|
|
|
}
|
2017-07-01 13:17:18 -07:00
|
|
|
}
|
2023-06-01 22:01:54 -04:00
|
|
|
|
2023-12-11 13:18:02 -05:00
|
|
|
AchievementManager::GetInstance().DoFrame();
|
2010-01-07 20:01:41 +00:00
|
|
|
}
|
2009-06-15 06:39:26 +00:00
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void UpdateTitle(Core::System& system)
|
2013-01-16 22:52:01 -05:00
|
|
|
{
|
|
|
|
// Settings are shown the same for both extended and summary info
|
2021-10-13 20:29:04 -04:00
|
|
|
const std::string SSettings = fmt::format(
|
2024-03-18 01:35:42 -07:00
|
|
|
"{} {} | {} | {}", system.GetPowerPC().GetCPUName(), system.IsDualCoreMode() ? "DC" : "SC",
|
|
|
|
g_video_backend->GetDisplayName(), Config::Get(Config::MAIN_DSP_HLE) ? "HLE" : "LLE");
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-01-21 00:44:25 +01:00
|
|
|
std::string message = fmt::format("{} | {}", Common::GetScmRevStr(), SSettings);
|
2021-12-31 03:00:39 +01:00
|
|
|
if (Config::Get(Config::MAIN_SHOW_ACTIVE_TITLE))
|
2017-05-15 11:17:51 +02:00
|
|
|
{
|
|
|
|
const std::string& title = SConfig::GetInstance().GetTitleDescription();
|
|
|
|
if (!title.empty())
|
|
|
|
message += " | " + title;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-05-15 11:17:51 +02:00
|
|
|
Host_UpdateTitle(message);
|
2014-06-20 02:43:57 +02:00
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void Shutdown(Core::System& system)
|
2015-06-06 14:52:09 +02:00
|
|
|
{
|
|
|
|
// During shutdown DXGI expects us to handle some messages on the UI thread.
|
|
|
|
// Therefore we can't immediately block and wait for the emu thread to shut
|
|
|
|
// down, so we join the emu thread as late as possible when the UI has already
|
|
|
|
// shut down.
|
|
|
|
// For more info read "DirectX Graphics Infrastructure (DXGI): Best Practices"
|
|
|
|
// on MSDN.
|
|
|
|
if (s_emu_thread.joinable())
|
|
|
|
s_emu_thread.join();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
// Make sure there's nothing left over in case we're about to exit.
|
2024-03-18 01:35:42 -07:00
|
|
|
HostDispatchJobs(system);
|
2015-06-06 14:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-12-31 14:03:20 +02:00
|
|
|
int AddOnStateChangedCallback(StateChangedCallbackFunc callback)
|
2014-06-20 02:43:57 +02:00
|
|
|
{
|
2020-12-31 14:03:20 +02:00
|
|
|
for (size_t i = 0; i < s_on_state_changed_callbacks.size(); ++i)
|
|
|
|
{
|
|
|
|
if (!s_on_state_changed_callbacks[i])
|
|
|
|
{
|
|
|
|
s_on_state_changed_callbacks[i] = std::move(callback);
|
|
|
|
return int(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s_on_state_changed_callbacks.emplace_back(std::move(callback));
|
|
|
|
return int(s_on_state_changed_callbacks.size()) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RemoveOnStateChangedCallback(int* handle)
|
|
|
|
{
|
2022-01-17 00:21:58 -08:00
|
|
|
if (handle && *handle >= 0 && s_on_state_changed_callbacks.size() > static_cast<size_t>(*handle))
|
2020-12-31 14:03:20 +02:00
|
|
|
{
|
|
|
|
s_on_state_changed_callbacks[*handle] = StateChangedCallbackFunc();
|
|
|
|
*handle = -1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CallOnStateChangedCallbacks(Core::State state)
|
|
|
|
{
|
|
|
|
for (const StateChangedCallbackFunc& on_state_changed_callback : s_on_state_changed_callbacks)
|
|
|
|
{
|
|
|
|
if (on_state_changed_callback)
|
|
|
|
on_state_changed_callback(state);
|
|
|
|
}
|
2014-06-20 02:43:57 +02:00
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void UpdateWantDeterminism(Core::System& system, bool initial)
|
2014-09-06 17:26:40 -04:00
|
|
|
{
|
|
|
|
// 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,
|
2024-01-13 13:14:48 +01:00
|
|
|
bool new_want_determinism = system.GetMovie().IsMovieActive() || NetPlay::IsNetPlayRunning();
|
2017-04-03 13:30:58 -04:00
|
|
|
if (new_want_determinism != s_wants_determinism || initial)
|
2014-09-06 17:26:40 -04:00
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false");
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2024-03-04 19:36:26 -08:00
|
|
|
const Core::CPUThreadGuard guard(system);
|
|
|
|
s_wants_determinism = new_want_determinism;
|
|
|
|
const auto ios = system.GetIOS();
|
|
|
|
if (ios)
|
|
|
|
ios->UpdateWantDeterminism(new_want_determinism);
|
2022-12-09 20:01:25 +01:00
|
|
|
|
2024-03-04 19:36:26 -08:00
|
|
|
system.GetFifo().UpdateWantDeterminism(new_want_determinism);
|
2022-12-09 20:01:25 +01:00
|
|
|
|
2024-03-04 19:36:26 -08:00
|
|
|
// We need to clear the cache because some parts of the JIT depend on want_determinism,
|
|
|
|
// e.g. use of FMA.
|
|
|
|
system.GetJitInterface().ClearCache(guard);
|
2014-09-06 17:26:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void QueueHostJob(std::function<void(Core::System&)> job, bool run_during_stop)
|
2016-05-12 01:18:30 +00:00
|
|
|
{
|
|
|
|
if (!job)
|
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
bool send_message = false;
|
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard guard(s_host_jobs_lock);
|
2016-05-12 01:18:30 +00:00
|
|
|
send_message = s_host_jobs_queue.empty();
|
|
|
|
s_host_jobs_queue.emplace(HostJob{std::move(job), run_during_stop});
|
|
|
|
}
|
|
|
|
// If the the queue was empty then kick the Host to come and get this job.
|
|
|
|
if (send_message)
|
2018-05-28 13:03:29 -04:00
|
|
|
Host_Message(HostMessageID::WMUserJobDispatch);
|
2016-05-12 01:18:30 +00:00
|
|
|
}
|
|
|
|
|
2024-03-18 01:35:42 -07:00
|
|
|
void HostDispatchJobs(Core::System& system)
|
2016-05-12 01:18:30 +00:00
|
|
|
{
|
|
|
|
// WARNING: This should only run on the Host Thread.
|
|
|
|
// NOTE: This function is potentially re-entrant. If a job calls
|
|
|
|
// Core::Stop for instance then we'll enter this a second time.
|
2020-12-29 16:01:12 -05:00
|
|
|
std::unique_lock guard(s_host_jobs_lock);
|
2016-05-12 01:18:30 +00:00
|
|
|
while (!s_host_jobs_queue.empty())
|
|
|
|
{
|
|
|
|
HostJob job = std::move(s_host_jobs_queue.front());
|
|
|
|
s_host_jobs_queue.pop();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2024-06-02 15:10:25 +02:00
|
|
|
if (!job.run_after_stop)
|
|
|
|
{
|
|
|
|
const State state = s_state.load();
|
|
|
|
if (state == State::Stopping || state == State::Uninitialized)
|
|
|
|
continue;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 01:18:30 +00:00
|
|
|
guard.unlock();
|
2024-03-18 01:35:42 -07:00
|
|
|
job.job(system);
|
2016-05-12 01:18:30 +00:00
|
|
|
guard.lock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-01 13:17:18 -07:00
|
|
|
// NOTE: Host Thread
|
2024-03-28 11:35:13 -07:00
|
|
|
void DoFrameStep(Core::System& system)
|
2017-07-01 13:17:18 -07:00
|
|
|
{
|
2023-12-11 13:18:02 -05:00
|
|
|
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
2023-06-07 21:46:49 -04:00
|
|
|
{
|
|
|
|
OSD::AddMessage("Frame stepping is disabled in RetroAchievements hardcore mode");
|
|
|
|
return;
|
|
|
|
}
|
2024-03-28 11:35:13 -07:00
|
|
|
if (GetState(system) == State::Paused)
|
2017-07-01 13:17:18 -07:00
|
|
|
{
|
|
|
|
// if already paused, frame advance for 1 frame
|
2020-04-07 19:37:32 +02:00
|
|
|
s_stop_frame_step = false;
|
2017-07-01 13:17:18 -07:00
|
|
|
s_frame_step = true;
|
2024-05-03 20:49:48 -07:00
|
|
|
SetState(system, State::Running, false);
|
2017-07-01 13:17:18 -07:00
|
|
|
}
|
|
|
|
else if (!s_frame_step)
|
|
|
|
{
|
|
|
|
// if not paused yet, pause immediately instead
|
2024-05-03 20:49:48 -07:00
|
|
|
SetState(system, State::Paused);
|
2017-07-01 13:17:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-09 13:28:04 +03:00
|
|
|
void UpdateInputGate(bool require_focus, bool require_full_focus)
|
2019-11-06 15:59:36 -06:00
|
|
|
{
|
2021-05-09 13:28:04 +03:00
|
|
|
// If the user accepts background input, controls should pass even if an on screen interface is on
|
|
|
|
const bool focus_passes =
|
2024-05-26 16:50:12 -07:00
|
|
|
!require_focus ||
|
|
|
|
((Host_RendererHasFocus() || Host_TASInputHasFocus()) && !Host_UIBlocksControllerState());
|
2021-05-09 13:28:04 +03:00
|
|
|
// Ignore full focus if we don't require basic focus
|
|
|
|
const bool full_focus_passes =
|
|
|
|
!require_focus || !require_full_focus || (focus_passes && Host_RendererHasFullFocus());
|
|
|
|
ControlReference::SetInputGate(focus_passes && full_focus_passes);
|
2019-11-06 15:59:36 -06:00
|
|
|
}
|
|
|
|
|
2023-03-08 01:58:05 +01:00
|
|
|
CPUThreadGuard::CPUThreadGuard(Core::System& system)
|
|
|
|
: m_system(system), m_was_cpu_thread(IsCPUThread())
|
2023-02-12 11:07:11 +01:00
|
|
|
{
|
|
|
|
if (!m_was_cpu_thread)
|
2023-03-08 01:58:05 +01:00
|
|
|
m_was_unpaused = PauseAndLock(system, true, true);
|
2023-02-12 11:07:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CPUThreadGuard::~CPUThreadGuard()
|
|
|
|
{
|
|
|
|
if (!m_was_cpu_thread)
|
2023-03-08 01:58:05 +01:00
|
|
|
PauseAndLock(m_system, false, m_was_unpaused);
|
2023-02-12 11:07:11 +01:00
|
|
|
}
|
|
|
|
|
2018-10-03 23:03:13 +10:00
|
|
|
} // namespace Core
|