Duckstation/src/core/system.cpp

5595 lines
169 KiB
C++
Raw Normal View History

// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
2019-09-09 07:01:26 +00:00
#include "system.h"
#include "IconsFontAwesome5.h"
#include "achievements.h"
#include "bios.h"
2019-09-12 02:53:04 +00:00
#include "bus.h"
2019-09-17 14:22:41 +00:00
#include "cdrom.h"
#include "cheats.h"
#include "controller.h"
#include "cpu_code_cache.h"
2019-09-12 02:53:04 +00:00
#include "cpu_core.h"
2023-12-14 07:24:45 +00:00
#include "cpu_pgxp.h"
2019-09-12 02:53:04 +00:00
#include "dma.h"
2023-08-23 12:06:48 +00:00
#include "fullscreen_ui.h"
#include "game_database.h"
2023-08-23 12:06:48 +00:00
#include "game_list.h"
2019-09-12 02:53:04 +00:00
#include "gpu.h"
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
#include "gte.h"
#include "host.h"
#include "host_interface_progress_callback.h"
2023-08-23 12:06:48 +00:00
#include "imgui_overlays.h"
2019-09-17 06:26:00 +00:00
#include "interrupt_controller.h"
2019-09-29 02:51:34 +00:00
#include "mdec.h"
2019-10-27 06:45:23 +00:00
#include "memory_card.h"
2021-01-21 07:59:40 +00:00
#include "multitap.h"
#include "pad.h"
#include "pcdrv.h"
#include "psf_loader.h"
#include "save_state_version.h"
#include "sio.h"
#include "spu.h"
#include "texture_replacements.h"
2019-09-20 13:40:19 +00:00
#include "timers.h"
#include "util/audio_stream.h"
2023-01-09 09:34:40 +00:00
#include "util/cd_image.h"
#include "util/gpu_device.h"
2023-08-30 10:34:48 +00:00
#include "util/imgui_manager.h"
#include "util/ini_settings_interface.h"
2023-08-23 12:06:48 +00:00
#include "util/input_manager.h"
#include "util/iso_reader.h"
2023-08-23 12:06:48 +00:00
#include "util/platform_misc.h"
#include "util/postprocessing.h"
2024-05-25 13:49:19 +00:00
#include "util/sockets.h"
#include "util/state_wrapper.h"
#include "common/align.h"
2024-07-04 04:40:16 +00:00
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "common/threading.h"
#include "cpuinfo.h"
2023-08-23 12:06:48 +00:00
#include "fmt/chrono.h"
#include "fmt/format.h"
2023-08-30 10:34:48 +00:00
#include "imgui.h"
#include "xxhash.h"
2020-09-03 02:25:59 +00:00
#include <cctype>
#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <deque>
#include <fstream>
2020-07-22 16:36:05 +00:00
#include <limits>
#include <thread>
2019-09-22 15:28:00 +00:00
Log_SetChannel(System);
2019-09-09 07:01:26 +00:00
#ifdef _WIN32
#include "common/windows_headers.h"
#include <mmsystem.h>
2024-04-25 02:56:02 +00:00
#include <objbase.h>
#endif
2024-05-25 13:49:19 +00:00
#ifndef __ANDROID__
2024-07-04 04:40:16 +00:00
#define ENABLE_DISCORD_PRESENCE 1
2024-05-25 13:49:19 +00:00
#define ENABLE_PINE_SERVER 1
2024-05-26 14:10:39 +00:00
#define ENABLE_GDB_SERVER 1
2024-05-25 13:49:19 +00:00
#define ENABLE_SOCKET_MULTIPLEXER 1
2024-05-26 14:10:39 +00:00
#include "gdb_server.h"
2024-05-25 13:49:19 +00:00
#include "pine_server.h"
#endif
// #define PROFILE_MEMORY_SAVE_STATES 1
SystemBootParameters::SystemBootParameters() = default;
SystemBootParameters::SystemBootParameters(const SystemBootParameters&) = default;
SystemBootParameters::SystemBootParameters(SystemBootParameters&& other) = default;
SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std::move(filename_))
{
}
SystemBootParameters::~SystemBootParameters() = default;
namespace System {
static void CheckCacheLineSize();
static std::optional<ExtendedSaveStateInfo> InternalGetExtendedSaveStateInfo(ByteStream* stream);
2021-01-23 16:52:52 +00:00
static void LoadInputBindings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
static bool LoadEXE(const char* filename);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
2023-11-29 10:43:39 +00:00
static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories);
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
std::vector<u8>* out_executable_data);
static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
2024-05-14 03:57:29 +00:00
static bool LoadBIOS(Error* error);
static void InternalReset();
static void ClearRunningGame();
static void DestroySystem();
static std::string GetMediaPathFromSaveState(const char* path);
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
static bool CreateGPU(GPURenderer renderer, bool is_switching, Error* error);
static bool SaveUndoLoadState();
2023-12-14 07:24:45 +00:00
static void WarnAboutUnsafeSettings();
static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
2021-01-23 16:52:52 +00:00
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
static void Throttle(Common::Timer::Value current_time);
static void UpdatePerformanceCounters();
static void AccumulatePreFrameSleepTime();
static void UpdatePreFrameSleepTime();
2024-05-23 15:59:35 +00:00
static void UpdateDisplayVSync();
static void SetRewinding(bool enabled);
2021-01-23 16:52:52 +00:00
static bool SaveRewindState();
static void DoRewind();
2021-01-23 16:52:52 +00:00
static void SaveRunaheadState();
static bool DoRunahead();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static bool Initialize(bool force_software_renderer, Error* error);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static bool UpdateGameSettingsLayer();
static void UpdateRunningGame(const char* path, CDImage* image, bool booting);
static bool CheckForSBIFile(CDImage* image, Error* error);
static std::unique_ptr<MemoryCard> GetMemoryCardForSlot(u32 slot, MemoryCardType type);
2023-08-23 12:06:48 +00:00
static void UpdateSessionTime(const std::string& prev_serial);
static void SetTimerResolutionIncreased(bool enabled);
2023-08-23 12:06:48 +00:00
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
static void InitializeDiscordPresence();
static void ShutdownDiscordPresence();
static void PollDiscordPresence();
#endif
} // namespace System
static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAME_COUNT = 2; // 20fps minimum
static constexpr u32 MAX_SKIPPED_TIMEOUT_FRAME_COUNT = 1; // 30fps minimum
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
static std::string s_input_profile_name;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static System::State s_state = System::State::Shutdown;
static std::atomic_bool s_startup_cancelled{false};
static bool s_keep_gpu_device_on_shutdown = false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
TickCount System::g_ticks_per_second = System::MASTER_CLOCK;
static TickCount s_max_slice_ticks = System::MASTER_CLOCK / 10;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static u32 s_frame_number = 1;
static u32 s_internal_frame_number = 1;
static const BIOS::ImageInfo* s_bios_image_info = nullptr;
static BIOS::ImageInfo::Hash s_bios_hash = {};
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static std::string s_running_game_path;
2022-10-05 08:29:08 +00:00
static std::string s_running_game_serial;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static std::string s_running_game_title;
static const GameDatabase::Entry* s_running_game_entry = nullptr;
static System::GameHash s_running_game_hash;
static bool s_running_game_custom_title = false;
static bool s_was_fast_booted = false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static bool s_system_executing = false;
static bool s_system_interrupted = false;
static bool s_frame_step_request = false;
static bool s_fast_forward_enabled = false;
static bool s_turbo_enabled = false;
static bool s_throttler_enabled = false;
static bool s_optimal_frame_pacing = false;
static bool s_pre_frame_sleep = false;
static bool s_can_sync_to_host = false;
static bool s_syncing_to_host = false;
static bool s_syncing_to_host_with_vsync = false;
static bool s_skip_presenting_duplicate_frames = false;
static u32 s_skipped_frame_count = 0;
static u32 s_last_presented_internal_frame_number = 0;
static float s_throttle_frequency = 0.0f;
static float s_target_speed = 0.0f;
static Common::Timer::Value s_frame_period = 0;
static Common::Timer::Value s_next_frame_time = 0;
static Common::Timer::Value s_frame_start_time = 0;
static Common::Timer::Value s_last_active_frame_time = 0;
static Common::Timer::Value s_pre_frame_sleep_time = 0;
static Common::Timer::Value s_max_active_frame_time = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static float s_average_frame_time_accumulator = 0.0f;
2023-01-07 03:09:20 +00:00
static float s_minimum_frame_time_accumulator = 0.0f;
static float s_maximum_frame_time_accumulator = 0.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static float s_vps = 0.0f;
static float s_fps = 0.0f;
static float s_speed = 0.0f;
2023-01-07 03:09:20 +00:00
static float s_minimum_frame_time = 0.0f;
static float s_maximum_frame_time = 0.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static float s_average_frame_time = 0.0f;
static float s_cpu_thread_usage = 0.0f;
static float s_cpu_thread_time = 0.0f;
static float s_sw_thread_usage = 0.0f;
static float s_sw_thread_time = 0.0f;
2022-09-03 04:15:15 +00:00
static float s_average_gpu_time = 0.0f;
static float s_accumulated_gpu_time = 0.0f;
static float s_gpu_usage = 0.0f;
2023-01-07 03:09:20 +00:00
static System::FrameTimeHistory s_frame_time_history;
static u32 s_frame_time_history_pos = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static u32 s_last_frame_number = 0;
static u32 s_last_internal_frame_number = 0;
static u32 s_last_global_tick_counter = 0;
static u64 s_last_cpu_time = 0;
static u64 s_last_sw_time = 0;
2022-09-03 04:15:15 +00:00
static u32 s_presents_since_last_update = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static Common::Timer s_fps_timer;
static Common::Timer s_frame_timer;
static Threading::ThreadHandle s_cpu_thread_handle;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
static std::unique_ptr<CheatList> s_cheat_list;
// temporary save state, created when loading, used to undo load state
static std::unique_ptr<ByteStream> m_undo_load_state;
static bool s_memory_saves_enabled = false;
2023-08-30 07:38:17 +00:00
static std::deque<System::MemorySaveState> s_rewind_states;
static s32 s_rewind_load_frequency = -1;
static s32 s_rewind_load_counter = -1;
static s32 s_rewind_save_frequency = -1;
static s32 s_rewind_save_counter = -1;
static bool s_rewinding_first_save = false;
2023-08-30 07:38:17 +00:00
static std::deque<System::MemorySaveState> s_runahead_states;
2021-01-23 16:52:52 +00:00
static bool s_runahead_replay_pending = false;
static u32 s_runahead_frames = 0;
static u32 s_runahead_replay_frames = 0;
2021-01-23 16:52:52 +00:00
2023-08-23 12:06:48 +00:00
// Used to track play time. We use a monotonic timer here, in case of clock changes.
static u64 s_session_start_time = 0;
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_SOCKET_MULTIPLEXER
static std::unique_ptr<SocketMultiplexer> s_socket_multiplexer;
#endif
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
static bool s_discord_presence_active = false;
static time_t s_discord_presence_time_epoch;
2023-08-23 12:06:48 +00:00
#endif
static TinyString GetTimestampStringForFileName()
{
2024-03-15 05:21:06 +00:00
return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr)));
}
bool System::Internal::PerformEarlyHardwareChecks(Error* error)
{
// This shouldn't fail... if it does, just hope for the best.
cpuinfo_initialize();
#ifdef CPU_ARCH_X64
if (!cpuinfo_has_x86_sse4_1())
{
Error::SetStringFmt(error, "Your CPU does not support the SSE4.1 instruction set.\n"
"A CPU from 2008 or newer is required to run DuckStation.");
return false;
}
#endif
// Check page size. If it doesn't match, it is a fatal error.
const size_t runtime_host_page_size = PlatformMisc::GetRuntimePageSize();
if (runtime_host_page_size == 0)
{
Error::SetStringFmt(error, "Cannot determine size of page. Continuing with expectation of {} byte pages.",
runtime_host_page_size);
}
else if (HOST_PAGE_SIZE != runtime_host_page_size)
{
Error::SetStringFmt(
error, "Page size mismatch. This build was compiled with {} byte pages, but the system has {} byte pages.",
HOST_PAGE_SIZE, runtime_host_page_size);
CPUThreadShutdown();
return false;
}
return true;
}
void System::CheckCacheLineSize()
{
u32 max_line_size = 0;
if (cpuinfo_initialize())
{
const u32 num_l1is = cpuinfo_get_l1i_caches_count();
const u32 num_l1ds = cpuinfo_get_l1d_caches_count();
const u32 num_l2s = cpuinfo_get_l2_caches_count();
for (u32 i = 0; i < num_l1is; i++)
{
const cpuinfo_cache* cache = cpuinfo_get_l1i_cache(i);
if (cache)
max_line_size = std::max(max_line_size, cache->line_size);
}
for (u32 i = 0; i < num_l1ds; i++)
{
const cpuinfo_cache* cache = cpuinfo_get_l1d_cache(i);
if (cache)
max_line_size = std::max(max_line_size, cache->line_size);
}
for (u32 i = 0; i < num_l2s; i++)
{
const cpuinfo_cache* cache = cpuinfo_get_l2_cache(i);
if (cache)
max_line_size = std::max(max_line_size, cache->line_size);
}
}
if (max_line_size == 0)
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Cannot determine size of cache line. Continuing with expectation of {} byte lines.",
HOST_CACHE_LINE_SIZE);
}
else if (HOST_CACHE_LINE_SIZE != max_line_size)
{
// Not fatal, but does have performance implications.
2024-05-23 10:55:28 +00:00
WARNING_LOG(
"Cache line size mismatch. This build was compiled with {} byte lines, but the system has {} byte lines.",
HOST_CACHE_LINE_SIZE, max_line_size);
}
}
bool System::Internal::ProcessStartup(Error* error)
{
Common::Timer timer;
// Allocate JIT memory as soon as possible.
if (!CPU::CodeCache::ProcessStartup(error))
return false;
// Fastmem alloc *must* come after JIT alloc, otherwise it tends to eat the 4GB region after the executable on MacOS.
if (!Bus::AllocateMemory(error))
{
CPU::CodeCache::ProcessShutdown();
return false;
}
VERBOSE_LOG("Memory allocation took {} ms.", timer.GetTimeMilliseconds());
CheckCacheLineSize();
return true;
}
void System::Internal::ProcessShutdown()
{
Bus::ReleaseMemory();
CPU::CodeCache::ProcessShutdown();
}
bool System::Internal::CPUThreadInitialize(Error* error)
2023-08-23 12:06:48 +00:00
{
2024-04-25 02:56:02 +00:00
#ifdef _WIN32
// On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc).
// We need to initialize COM first, before anything else does, because otherwise they might
// initialize it in single-threaded/apartment mode, which can't be changed to multithreaded.
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr))
{
Error::SetHResult(error, "CoInitializeEx() failed: ", hr);
2024-04-25 02:56:02 +00:00
return false;
}
#endif
2023-08-23 12:06:48 +00:00
// This will call back to Host::LoadSettings() -> ReloadSources().
LoadSettings(false);
#ifdef ENABLE_RAINTEGRATION
2023-08-23 12:06:48 +00:00
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
Achievements::SwitchToRAIntegration();
#endif
if (g_settings.achievements_enabled)
Achievements::Initialize();
2023-10-29 12:46:02 +00:00
2023-10-31 15:32:29 +00:00
#ifdef ENABLE_DISCORD_PRESENCE
2023-10-29 12:46:02 +00:00
if (g_settings.enable_discord_presence)
InitializeDiscordPresence();
2023-10-31 15:32:29 +00:00
#endif
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_PINE_SERVER
if (g_settings.pine_enable)
PINEServer::Initialize(g_settings.pine_slot);
#endif
return true;
2023-08-23 12:06:48 +00:00
}
2024-04-25 02:56:02 +00:00
void System::Internal::CPUThreadShutdown()
2023-08-23 12:06:48 +00:00
{
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_PINE_SERVER
PINEServer::Shutdown();
#endif
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
ShutdownDiscordPresence();
#endif
2023-09-07 10:13:48 +00:00
Achievements::Shutdown(false);
2023-08-23 12:06:48 +00:00
InputManager::CloseSources();
2024-04-25 02:56:02 +00:00
#ifdef _WIN32
CoUninitialize();
#endif
2023-08-23 12:06:48 +00:00
}
void System::Internal::IdlePollUpdate()
{
InputManager::PollSources();
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
PollDiscordPresence();
#endif
2023-09-07 10:13:48 +00:00
Achievements::IdleUpdate();
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_SOCKET_MULTIPLEXER
if (s_socket_multiplexer)
s_socket_multiplexer->PollEventsWithTimeout(0);
#endif
2023-08-23 12:06:48 +00:00
}
System::State System::GetState()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_state;
}
void System::SetState(State new_state)
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
if (s_state == new_state)
return;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
Assert(s_state == State::Paused || s_state == State::Running);
Assert(new_state == State::Paused || new_state == State::Running);
s_state = new_state;
}
bool System::IsRunning()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_state == State::Running;
}
bool System::IsExecutionInterrupted()
{
return s_state != State::Running || s_system_interrupted;
}
bool System::IsPaused()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_state == State::Paused;
}
bool System::IsShutdown()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_state == State::Shutdown;
}
bool System::IsValid()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_state == State::Running || s_state == State::Paused;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
}
2023-10-29 12:46:02 +00:00
bool System::IsValidOrInitializing()
{
return s_state == State::Starting || s_state == State::Running || s_state == State::Paused;
}
bool System::IsExecuting()
{
DebugAssert(s_state != State::Shutdown);
return s_system_executing;
}
bool System::IsStartupCancelled()
{
return s_startup_cancelled.load();
}
void System::CancelPendingStartup()
{
if (s_state == State::Starting)
s_startup_cancelled.store(true);
}
2023-08-30 12:18:01 +00:00
void System::InterruptExecution()
{
if (s_system_executing)
s_system_interrupted = true;
}
ConsoleRegion System::GetRegion()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_region;
}
DiscRegion System::GetDiscRegion()
{
2023-01-09 09:34:40 +00:00
return CDROM::GetDiscRegion();
}
bool System::IsPALRegion()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_region == ConsoleRegion::PAL;
}
TickCount System::GetMaxSliceTicks()
{
return s_max_slice_ticks;
}
void System::UpdateOverclock()
{
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
2022-08-01 13:39:41 +00:00
SPU::CPUClockChanged();
2023-01-09 09:34:40 +00:00
CDROM::CPUClockChanged();
g_gpu->CPUClockChanged();
2023-01-11 08:58:25 +00:00
Timers::CPUClocksChanged();
UpdateThrottlePeriod();
}
u32 System::GetGlobalTickCounter()
{
// When running events, the counter actually goes backwards, because the pending ticks are added in chunks.
// So, we need to return the counter with all pending ticks added in such cases.
return TimingEvents::IsRunningEvents() ? TimingEvents::GetEventRunTickCounter() :
(TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks());
}
u32 System::GetFrameNumber()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_frame_number;
}
u32 System::GetInternalFrameNumber()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_internal_frame_number;
}
const std::string& System::GetDiscPath()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_running_game_path;
}
const std::string& System::GetGameSerial()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
2022-10-05 08:29:08 +00:00
return s_running_game_serial;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
}
const std::string& System::GetGameTitle()
2019-09-12 02:53:04 +00:00
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
return s_running_game_title;
2019-09-12 02:53:04 +00:00
}
2019-09-09 07:01:26 +00:00
2023-08-23 08:12:10 +00:00
const GameDatabase::Entry* System::GetGameDatabaseEntry()
{
return s_running_game_entry;
}
System::GameHash System::GetGameHash()
{
return s_running_game_hash;
}
bool System::IsRunningUnknownGame()
{
return !s_running_game_entry;
}
bool System::WasFastBooted()
{
return s_was_fast_booted;
}
const BIOS::ImageInfo* System::GetBIOSImageInfo()
{
return s_bios_image_info;
}
float System::GetFPS()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_fps;
}
float System::GetVPS()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_vps;
}
float System::GetEmulationSpeed()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_speed;
}
float System::GetAverageFrameTime()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_average_frame_time;
}
2023-01-07 03:09:20 +00:00
float System::GetMinimumFrameTime()
{
2023-01-07 03:09:20 +00:00
return s_minimum_frame_time;
}
float System::GetMaximumFrameTime()
{
return s_maximum_frame_time;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
}
float System::GetThrottleFrequency()
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
return s_throttle_frequency;
}
float System::GetCPUThreadUsage()
{
return s_cpu_thread_usage;
}
float System::GetCPUThreadAverageTime()
{
return s_cpu_thread_time;
}
float System::GetSWThreadUsage()
{
return s_sw_thread_usage;
}
float System::GetSWThreadAverageTime()
{
return s_sw_thread_time;
}
2022-09-03 04:15:15 +00:00
float System::GetGPUUsage()
{
return s_gpu_usage;
}
float System::GetGPUAverageTime()
{
return s_average_gpu_time;
}
2023-01-07 03:09:20 +00:00
const System::FrameTimeHistory& System::GetFrameTimeHistory()
{
return s_frame_time_history;
}
u32 System::GetFrameTimeHistoryPos()
{
return s_frame_time_history_pos;
}
2019-09-09 07:01:26 +00:00
2024-05-05 10:21:54 +00:00
bool System::IsExeFileName(std::string_view path)
{
return (StringUtil::EndsWithNoCase(path, ".exe") || StringUtil::EndsWithNoCase(path, ".psexe") ||
StringUtil::EndsWithNoCase(path, ".ps-exe"));
}
2024-05-05 10:21:54 +00:00
bool System::IsPsfFileName(std::string_view path)
{
return (StringUtil::EndsWithNoCase(path, ".psf") || StringUtil::EndsWithNoCase(path, ".minipsf"));
}
2024-05-05 10:21:54 +00:00
bool System::IsLoadableFilename(std::string_view path)
{
static constexpr const std::array extensions = {
".bin", ".cue", ".img", ".iso", ".chd", ".ecm", ".mds", // discs
".exe", ".psexe", ".ps-exe", // exes
".psf", ".minipsf", // psf
".m3u", // playlists
".pbp",
};
for (const char* test_extension : extensions)
{
if (StringUtil::EndsWithNoCase(path, test_extension))
return true;
}
return false;
}
2024-05-05 10:21:54 +00:00
bool System::IsSaveStateFilename(std::string_view path)
{
return StringUtil::EndsWithNoCase(path, ".sav");
}
ConsoleRegion System::GetConsoleRegionForDiscRegion(DiscRegion region)
{
switch (region)
{
case DiscRegion::NTSC_J:
return ConsoleRegion::NTSC_J;
case DiscRegion::NTSC_U:
case DiscRegion::Other:
case DiscRegion::NonPS1:
default:
return ConsoleRegion::NTSC_U;
case DiscRegion::PAL:
return ConsoleRegion::PAL;
}
}
std::string System::GetGameHashId(GameHash hash)
{
return fmt::format("HASH-{:X}", hash);
}
bool System::GetGameDetailsFromImage(CDImage* cdi, std::string* out_id, GameHash* out_hash)
{
2023-11-29 10:43:39 +00:00
IsoReader iso;
if (!iso.Open(cdi, 1))
{
if (out_id)
out_id->clear();
if (out_hash)
*out_hash = 0;
return false;
}
std::string id;
std::string exe_name;
std::vector<u8> exe_buffer;
if (!ReadExecutableFromImage(iso, &exe_name, &exe_buffer))
{
if (out_id)
out_id->clear();
if (out_hash)
*out_hash = 0;
return false;
}
// Always compute the hash.
const GameHash hash = GetGameHashFromBuffer(exe_name, exe_buffer, iso.GetPVD(), cdi->GetTrackLength(1));
2024-05-23 10:55:28 +00:00
DEV_LOG("Hash for '{}' - {:016X}", exe_name, hash);
if (exe_name != FALLBACK_EXE_NAME)
{
// Strip off any subdirectories.
const std::string::size_type slash = exe_name.rfind('\\');
if (slash != std::string::npos)
id = std::string_view(exe_name).substr(slash + 1);
else
id = exe_name;
// SCES_123.45 -> SCES-12345
for (std::string::size_type pos = 0; pos < id.size();)
{
if (id[pos] == '.')
{
id.erase(pos, 1);
continue;
}
if (id[pos] == '_')
id[pos] = '-';
else
id[pos] = static_cast<char>(std::toupper(id[pos]));
pos++;
}
}
if (out_id)
{
if (id.empty())
*out_id = GetGameHashId(hash);
else
*out_id = std::move(id);
}
if (out_hash)
*out_hash = hash;
return true;
}
System::GameHash System::GetGameHashFromFile(const char* path)
{
const std::optional<std::vector<u8>> data = FileSystem::ReadBinaryFile(path);
if (!data)
return 0;
const std::string display_name = FileSystem::GetDisplayNameFromPath(path);
return GetGameHashFromBuffer(display_name, data.value(), IsoReader::ISOPrimaryVolumeDescriptor{}, 0);
}
2023-11-29 10:43:39 +00:00
std::string System::GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories)
{
// Read SYSTEM.CNF
std::vector<u8> system_cnf_data;
if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data))
return FALLBACK_EXE_NAME;
// Parse lines
std::vector<std::pair<std::string, std::string>> lines;
std::pair<std::string, std::string> current_line;
bool reading_value = false;
for (size_t pos = 0; pos < system_cnf_data.size(); pos++)
{
const char ch = static_cast<char>(system_cnf_data[pos]);
if (ch == '\r' || ch == '\n')
{
if (!current_line.first.empty())
{
lines.push_back(std::move(current_line));
current_line = {};
reading_value = false;
}
}
else if (ch == ' ' || (ch >= 0x09 && ch <= 0x0D))
{
continue;
}
else if (ch == '=' && !reading_value)
{
reading_value = true;
}
else
{
if (reading_value)
current_line.second.push_back(ch);
else
current_line.first.push_back(ch);
}
}
if (!current_line.first.empty())
lines.push_back(std::move(current_line));
// Find the BOOT line
auto iter = std::find_if(lines.begin(), lines.end(),
[](const auto& it) { return StringUtil::Strcasecmp(it.first.c_str(), "boot") == 0; });
if (iter == lines.end())
{
// Fallback to PSX.EXE
return FALLBACK_EXE_NAME;
}
std::string code = iter->second;
std::string::size_type pos;
if (strip_subdirectories)
{
// cdrom:\SCES_123.45;1
pos = code.rfind('\\');
if (pos != std::string::npos)
{
code.erase(0, pos + 1);
}
else
{
// cdrom:SCES_123.45;1
pos = code.rfind(':');
if (pos != std::string::npos)
code.erase(0, pos + 1);
}
}
else
{
if (code.compare(0, 6, "cdrom:") == 0)
code.erase(0, 6);
else
2024-05-23 10:55:28 +00:00
WARNING_LOG("Unknown prefix in executable path: '{}'", code);
// remove leading slashes
while (code[0] == '/' || code[0] == '\\')
code.erase(0, 1);
}
// strip off ; or version number
pos = code.rfind(';');
if (pos != std::string::npos)
code.erase(pos);
return code;
}
std::string System::GetExecutableNameForImage(CDImage* cdi, bool strip_subdirectories)
{
2023-11-29 10:43:39 +00:00
IsoReader iso;
if (!iso.Open(cdi, 1))
return {};
return GetExecutableNameForImage(iso, strip_subdirectories);
}
bool System::ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name,
std::vector<u8>* out_executable_data)
{
2023-11-29 10:43:39 +00:00
IsoReader iso;
if (!iso.Open(cdi, 1))
return false;
return ReadExecutableFromImage(iso, out_executable_name, out_executable_data);
}
2023-11-29 10:43:39 +00:00
bool System::ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
std::vector<u8>* out_executable_data)
{
const std::string executable_path = GetExecutableNameForImage(iso, false);
2024-05-23 10:55:28 +00:00
DEV_LOG("Executable path: '{}'", executable_path);
if (!executable_path.empty() && out_executable_data)
{
if (!iso.ReadFile(executable_path.c_str(), out_executable_data))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to read executable '{}' from disc", executable_path);
return false;
}
}
if (out_executable_name)
*out_executable_name = std::move(executable_path);
return true;
}
System::GameHash System::GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length)
{
XXH64_state_t* state = XXH64_createState();
XXH64_reset(state, 0x4242D00C);
XXH64_update(state, exe_name.data(), exe_name.size());
XXH64_update(state, exe_buffer.data(), exe_buffer.size());
XXH64_update(state, &iso_pvd, sizeof(IsoReader::ISOPrimaryVolumeDescriptor));
XXH64_update(state, &track_1_length, sizeof(track_1_length));
const GameHash hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
2022-10-05 08:29:08 +00:00
DiscRegion System::GetRegionForSerial(std::string_view serial)
{
std::string prefix;
2022-10-05 08:29:08 +00:00
for (size_t pos = 0; pos < serial.length(); pos++)
{
2022-10-05 08:29:08 +00:00
const int ch = std::tolower(serial[pos]);
if (ch < 'a' || ch > 'z')
break;
prefix.push_back(static_cast<char>(ch));
}
if (prefix == "sces" || prefix == "sced" || prefix == "sles" || prefix == "sled")
return DiscRegion::PAL;
else if (prefix == "scps" || prefix == "slps" || prefix == "slpm" || prefix == "sczs" || prefix == "papx")
return DiscRegion::NTSC_J;
else if (prefix == "scus" || prefix == "slus")
return DiscRegion::NTSC_U;
else
return DiscRegion::Other;
}
DiscRegion System::GetRegionFromSystemArea(CDImage* cdi)
{
// The license code is on sector 4 of the disc.
u8 sector[CDImage::DATA_SECTOR_SIZE];
if (!cdi->Seek(1, 4) || cdi->Read(CDImage::ReadMode::DataOnly, 1, sector) != 1)
return DiscRegion::Other;
static constexpr char ntsc_u_string[] = " Licensed by Sony Computer Entertainment Amer ica ";
static constexpr char ntsc_j_string[] = " Licensed by Sony Computer Entertainment Inc.";
static constexpr char pal_string[] = " Licensed by Sony Computer Entertainment Euro pe";
// subtract one for the terminating null
if (std::equal(ntsc_u_string, ntsc_u_string + countof(ntsc_u_string) - 1, sector))
return DiscRegion::NTSC_U;
else if (std::equal(ntsc_j_string, ntsc_j_string + countof(ntsc_j_string) - 1, sector))
return DiscRegion::NTSC_J;
else if (std::equal(pal_string, pal_string + countof(pal_string) - 1, sector))
return DiscRegion::PAL;
else
return DiscRegion::Other;
}
DiscRegion System::GetRegionForImage(CDImage* cdi)
{
const DiscRegion system_area_region = GetRegionFromSystemArea(cdi);
if (system_area_region != DiscRegion::Other)
return system_area_region;
2023-11-29 10:43:39 +00:00
IsoReader iso;
if (!iso.Open(cdi, 1))
return DiscRegion::NonPS1;
// The executable must exist, because this just returns PSX.EXE if it doesn't.
const std::string exename = GetExecutableNameForImage(iso, false);
if (exename.empty() || !iso.FileExists(exename.c_str()))
return DiscRegion::NonPS1;
// Strip off any subdirectories.
const std::string::size_type slash = exename.rfind('\\');
if (slash != std::string::npos)
return GetRegionForSerial(std::string_view(exename).substr(slash + 1));
else
return GetRegionForSerial(exename);
}
DiscRegion System::GetRegionForExe(const char* path)
{
auto fp = FileSystem::OpenManagedCFile(path, "rb");
if (!fp)
return DiscRegion::Other;
BIOS::PSEXEHeader header;
if (std::fread(&header, sizeof(header), 1, fp.get()) != 1)
return DiscRegion::Other;
return BIOS::GetPSExeDiscRegion(header);
}
DiscRegion System::GetRegionForPsf(const char* path)
{
PSFLoader::File psf;
if (!psf.Load(path))
return DiscRegion::Other;
return psf.GetRegion();
}
2024-05-05 10:21:54 +00:00
std::string System::GetGameSettingsPath(std::string_view game_serial)
{
// multi-disc games => always use the first disc
const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(game_serial);
const std::string_view serial_for_path =
(entry && !entry->disc_set_serials.empty()) ? entry->disc_set_serials.front() : game_serial;
return Path::Combine(EmuFolders::GameSettings, fmt::format("{}.ini", Path::SanitizeFileName(serial_for_path)));
}
2024-05-05 10:21:54 +00:00
std::string System::GetInputProfilePath(std::string_view name)
{
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
}
bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool update_display /* = true*/)
{
ClearMemorySaveStates();
g_gpu->RestoreDeviceContext();
// save current state
std::unique_ptr<ByteStream> state_stream = ByteStream::CreateGrowableMemoryStream();
2020-10-12 10:46:59 +00:00
StateWrapper sw(state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
const bool state_valid = g_gpu->DoState(sw, nullptr, false) && TimingEvents::DoState(sw);
if (!state_valid)
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to save old GPU state when switching renderers");
// create new renderer
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_gpu.reset();
if (force_recreate_device)
{
PostProcessing::Shutdown();
Host::ReleaseGPUDevice();
}
Error error;
if (!CreateGPU(renderer, true, &error))
{
if (!IsStartupCancelled())
Host::ReportErrorAsync("Error", error.GetDescription());
DestroySystem();
return false;
}
if (state_valid)
{
state_stream->SeekAbsolute(0);
sw.SetMode(StateWrapper::Mode::Read);
g_gpu->RestoreDeviceContext();
g_gpu->DoState(sw, nullptr, update_display);
TimingEvents::DoState(sw);
}
// fix up vsync etc
UpdateSpeedLimiterState();
return true;
}
void System::LoadSettings(bool display_osd_messages)
{
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface& si = *Host::GetSettingsInterface();
g_settings.Load(si);
2023-08-23 12:06:48 +00:00
g_settings.UpdateLogSettings();
Host::LoadSettings(si, lock);
2023-08-23 12:06:48 +00:00
InputManager::ReloadSources(si, lock);
LoadInputBindings(si, lock);
WarnAboutUnsafeSettings();
// apply compatibility settings
if (g_settings.apply_compatibility_settings && !s_running_game_serial.empty())
{
const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(s_running_game_serial);
if (entry)
entry->ApplySettings(g_settings, display_osd_messages);
}
g_settings.FixIncompatibleSettings(display_osd_messages);
}
void System::ReloadInputSources()
{
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
InputManager::ReloadSources(*si, lock);
// skip loading bindings if we're not running, since it'll get done on startup anyway
if (IsValid())
LoadInputBindings(*si, lock);
}
void System::ReloadInputBindings()
{
// skip loading bindings if we're not running, since it'll get done on startup anyway
if (!IsValid())
return;
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
LoadInputBindings(*si, lock);
}
void System::LoadInputBindings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
{
// Hotkeys use the base configuration, except if the custom hotkeys option is enabled.
if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer())
{
const bool use_profile_hotkeys = isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
if (use_profile_hotkeys)
{
InputManager::ReloadBindings(si, *isi, *isi);
}
else
{
// Temporarily disable the input profile layer, so it doesn't take precedence.
Host::Internal::SetInputSettingsLayer(nullptr, lock);
InputManager::ReloadBindings(si, *isi, si);
Host::Internal::SetInputSettingsLayer(s_input_settings_interface.get(), lock);
}
}
else if (SettingsInterface* gsi = Host::Internal::GetGameSettingsLayer();
gsi && gsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
{
InputManager::ReloadBindings(si, *gsi, si);
}
else
{
InputManager::ReloadBindings(si, si, si);
}
}
void System::SetDefaultSettings(SettingsInterface& si)
{
Settings temp;
// we don't want to reset some things (e.g. OSD)
temp.display_show_osd_messages = g_settings.display_show_osd_messages;
temp.display_show_fps = g_settings.display_show_fps;
temp.display_show_speed = g_settings.display_show_speed;
2024-01-21 09:37:29 +00:00
temp.display_show_gpu_stats = g_settings.display_show_gpu_stats;
temp.display_show_resolution = g_settings.display_show_resolution;
2024-01-21 09:37:29 +00:00
temp.display_show_cpu_usage = g_settings.display_show_cpu_usage;
temp.display_show_gpu_usage = g_settings.display_show_gpu_usage;
2023-01-07 03:09:20 +00:00
temp.display_show_frame_times = g_settings.display_show_frame_times;
// keep controller, we reset it elsewhere
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
temp.controller_types[i] = g_settings.controller_types[i];
2024-03-20 14:41:54 +00:00
temp.Save(si, false);
}
void System::ApplySettings(bool display_osd_messages)
{
2024-05-23 10:55:28 +00:00
DEV_LOG("Applying settings...");
const Settings old_config(std::move(g_settings));
g_settings = Settings();
LoadSettings(display_osd_messages);
2022-07-22 14:13:55 +00:00
// If we've disabled/enabled game settings, we need to reload without it.
if (g_settings.apply_game_settings != old_config.apply_game_settings)
{
UpdateGameSettingsLayer();
LoadSettings(display_osd_messages);
}
CheckForSettingsChanges(old_config);
Host::CheckForSettingsChanges(old_config);
if (IsValid())
{
ResetPerformanceCounters();
InterruptExecution();
}
}
bool System::ReloadGameSettings(bool display_osd_messages)
{
if (!IsValid() || !UpdateGameSettingsLayer())
return false;
ApplySettings(display_osd_messages);
return true;
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
bool System::UpdateGameSettingsLayer()
{
std::unique_ptr<INISettingsInterface> new_interface;
2022-10-05 08:29:08 +00:00
if (g_settings.apply_game_settings && !s_running_game_serial.empty())
2020-08-15 10:38:17 +00:00
{
2022-10-05 08:29:08 +00:00
std::string filename(GetGameSettingsPath(s_running_game_serial));
if (FileSystem::FileExists(filename.c_str()))
2020-08-15 10:38:17 +00:00
{
2024-05-24 11:57:51 +00:00
INFO_LOG("Loading game settings from '{}'...", Path::GetFileName(filename));
new_interface = std::make_unique<INISettingsInterface>(std::move(filename));
if (!new_interface->Load())
{
2024-05-24 11:57:51 +00:00
ERROR_LOG("Failed to parse game settings ini '{}'", new_interface->GetFileName());
new_interface.reset();
}
2020-08-15 10:38:17 +00:00
}
else
{
2024-05-24 11:57:51 +00:00
INFO_LOG("No game settings found (tried '{}')", Path::GetFileName(filename));
}
}
2020-08-15 10:38:17 +00:00
std::string input_profile_name;
if (new_interface)
{
if (!new_interface->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
new_interface->GetStringValue("ControllerPorts", "InputProfileName", &input_profile_name);
}
if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name)
return false;
auto lock = Host::GetSettingsLock();
Host::Internal::SetGameSettingsLayer(new_interface.get(), lock);
s_game_settings_interface = std::move(new_interface);
std::unique_ptr<INISettingsInterface> input_interface;
if (!input_profile_name.empty())
{
const std::string filename(GetInputProfilePath(input_profile_name));
if (FileSystem::FileExists(filename.c_str()))
{
2024-05-23 10:55:28 +00:00
INFO_LOG("Loading input profile from '{}'...", Path::GetFileName(filename));
input_interface = std::make_unique<INISettingsInterface>(std::move(filename));
if (!input_interface->Load())
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to parse input profile ini '{}'", Path::GetFileName(input_interface->GetFileName()));
input_interface.reset();
input_profile_name = {};
}
}
else
{
2024-05-23 10:55:28 +00:00
WARNING_LOG("No input profile found (tried '{}')", Path::GetFileName(filename));
input_profile_name = {};
}
}
Host::Internal::SetInputSettingsLayer(input_interface.get(), lock);
s_input_settings_interface = std::move(input_interface);
s_input_profile_name = std::move(input_profile_name);
return true;
}
void System::ResetSystem()
{
if (!IsValid())
return;
if (!Achievements::ConfirmSystemReset())
return;
2022-10-08 10:59:18 +00:00
if (Achievements::ResetHardcoreMode(false))
2024-02-06 13:24:33 +00:00
{
// Make sure a pre-existing cheat file hasn't been loaded when resetting
// after enabling HC mode.
s_cheat_list.reset();
2022-10-08 10:59:18 +00:00
ApplySettings(false);
2024-02-06 13:24:33 +00:00
}
InternalReset();
ResetPerformanceCounters();
ResetThrottler();
Host::AddIconOSDMessage("system_reset", ICON_FA_POWER_OFF, TRANSLATE_STR("OSDMessage", "System reset."),
Host::OSD_QUICK_DURATION);
}
void System::PauseSystem(bool paused)
{
if (paused == IsPaused() || !IsValid())
return;
SetState(paused ? State::Paused : State::Running);
2022-08-01 13:39:41 +00:00
SPU::GetOutputStream()->SetPaused(paused);
if (paused)
{
// Make sure the GPU is flushed, otherwise the VB might still be mapped.
g_gpu->FlushRender();
2023-08-23 12:06:48 +00:00
FullscreenUI::OnSystemPaused();
InputManager::PauseVibration();
Achievements::OnSystemPaused(true);
if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver();
2024-05-26 14:10:39 +00:00
#ifdef ENABLE_GDB_SERVER
GDBServer::OnSystemPaused();
#endif
Host::OnSystemPaused();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
UpdateDisplayVSync();
2023-08-30 10:34:48 +00:00
InvalidateDisplay();
}
else
{
2023-08-23 12:06:48 +00:00
FullscreenUI::OnSystemResumed();
Achievements::OnSystemPaused(false);
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
2024-05-26 14:10:39 +00:00
#ifdef ENABLE_GDB_SERVER
GDBServer::OnSystemResumed();
#endif
2023-08-23 12:06:48 +00:00
Host::OnSystemResumed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
2023-08-23 12:06:48 +00:00
UpdateDisplayVSync();
ResetPerformanceCounters();
ResetThrottler();
}
}
bool System::LoadState(const char* filename, Error* error)
{
if (!IsValid())
{
Error::SetStringView(error, "System is not booted.");
return false;
}
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("Achievements", "Loading state"),
[filename = std::string(filename)](bool approved) {
if (approved)
LoadState(filename.c_str(), nullptr);
});
return true;
}
Common::Timer load_timer;
std::unique_ptr<ByteStream> stream =
ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, error);
if (!stream)
{
Error::AddPrefixFmt(error, "Failed to open '{}': ", Path::GetFileName(filename));
return false;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Loading state from '{}'...", filename);
{
const std::string display_name(FileSystem::GetDisplayNameFromPath(filename));
Host::AddIconOSDMessage(
"load_state", ICON_FA_FOLDER_OPEN,
fmt::format(TRANSLATE_FS("OSDMessage", "Loading state from '{}'..."), Path::GetFileName(display_name)), 5.0f);
}
SaveUndoLoadState();
if (!LoadStateFromStream(stream.get(), error, true))
{
if (m_undo_load_state)
UndoLoadState();
return false;
}
ResetPerformanceCounters();
ResetThrottler();
if (IsPaused())
InvalidateDisplay();
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Loading state took {:.2f} msec", load_timer.GetTimeMilliseconds());
return true;
}
bool System::SaveState(const char* filename, Error* error, bool backup_existing_save)
{
if (IsSavingMemoryCards())
{
Error::SetStringView(error, TRANSLATE_SV("System", "Cannot save state while memory card is being saved."));
return false;
}
if (backup_existing_save && FileSystem::FileExists(filename))
{
Error backup_error;
const std::string backup_filename = Path::ReplaceExtension(filename, "bak");
if (!FileSystem::RenamePath(filename, backup_filename.c_str(), &backup_error))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to rename save state backup '{}': {}", Path::GetFileName(backup_filename),
backup_error.GetDescription());
}
}
Common::Timer save_timer;
std::unique_ptr<ByteStream> stream =
ByteStream::OpenFile(filename,
BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE |
BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED,
error);
if (!stream)
{
Error::AddPrefixFmt(error, "Cannot open '{}': ", Path::GetFileName(filename));
return false;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Saving state to '{}'...", filename);
const u32 screenshot_size = 256;
const bool result = SaveStateToStream(stream.get(), error, screenshot_size,
g_settings.compress_save_states ? SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD :
SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
if (!result)
{
stream->Discard();
}
else
{
const std::string display_name(FileSystem::GetDisplayNameFromPath(filename));
Host::AddIconOSDMessage(
"save_state", ICON_FA_SAVE,
fmt::format(TRANSLATE_FS("OSDMessage", "State saved to '{}'."), Path::GetFileName(display_name)), 5.0f);
stream->Commit();
}
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Saving state took {:.2f} msec", save_timer.GetTimeMilliseconds());
return result;
}
bool System::SaveResumeState(Error* error)
{
2022-10-05 08:29:08 +00:00
if (s_running_game_serial.empty())
{
Error::SetStringView(error, "Cannot save resume state without serial.");
return false;
}
2022-10-05 08:29:08 +00:00
const std::string path(GetGameSaveStateFileName(s_running_game_serial, -1));
return SaveState(path.c_str(), error, false);
}
bool System::BootSystem(SystemBootParameters parameters, Error* error)
{
if (!parameters.save_state.empty())
{
// loading a state, so pull the media path from the save state to avoid a double change
std::string state_media(GetMediaPathFromSaveState(parameters.save_state.c_str()));
if (FileSystem::FileExists(state_media.c_str()))
parameters.filename = std::move(state_media);
2020-08-15 10:38:17 +00:00
}
if (parameters.filename.empty())
2024-05-23 10:55:28 +00:00
INFO_LOG("Boot Filename: <BIOS/Shell>");
else
2024-05-23 10:55:28 +00:00
INFO_LOG("Boot Filename: {}", parameters.filename);
Assert(s_state == State::Shutdown);
s_state = State::Starting;
s_startup_cancelled.store(false);
s_keep_gpu_device_on_shutdown = static_cast<bool>(g_gpu_device);
s_region = g_settings.region;
Host::OnSystemStarting();
// Load CD image up and detect region.
std::unique_ptr<CDImage> disc;
DiscRegion disc_region = DiscRegion::NonPS1;
bool do_exe_boot = false;
bool do_psf_boot = false;
if (!parameters.filename.empty())
{
do_exe_boot = IsExeFileName(parameters.filename);
do_psf_boot = (!do_exe_boot && IsPsfFileName(parameters.filename));
if (do_exe_boot || do_psf_boot)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (s_region == ConsoleRegion::Auto)
{
const DiscRegion file_region =
(do_exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str()));
2024-05-23 10:55:28 +00:00
INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region));
s_region = GetConsoleRegionForDiscRegion(file_region);
}
}
else
{
2024-05-23 10:55:28 +00:00
INFO_LOG("Loading CD image '{}'...", Path::GetFileName(parameters.filename));
disc = CDImage::Open(parameters.filename.c_str(), g_settings.cdrom_load_image_patches, error);
if (!disc)
{
Error::AddPrefixFmt(error, "Failed to open CD image '{}':\n", Path::GetFileName(parameters.filename));
s_state = State::Shutdown;
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
}
disc_region = GameList::GetCustomRegionForPath(parameters.filename).value_or(GetRegionForImage(disc.get()));
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (s_region == ConsoleRegion::Auto)
{
if (disc_region != DiscRegion::Other)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_region = GetConsoleRegionForDiscRegion(disc_region);
2024-05-23 10:55:28 +00:00
INFO_LOG("Auto-detected console {} region for '{}' (region {})", Settings::GetConsoleRegionName(s_region),
parameters.filename, Settings::GetDiscRegionName(disc_region));
}
else
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_region = ConsoleRegion::NTSC_U;
2024-05-23 10:55:28 +00:00
WARNING_LOG("Could not determine console region for disc region {}. Defaulting to {}.",
Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(s_region));
}
}
}
}
else
{
// Default to NTSC for BIOS boot.
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (s_region == ConsoleRegion::Auto)
s_region = ConsoleRegion::NTSC_U;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(s_region));
// Switch subimage.
if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, error))
{
Error::AddPrefixFmt(error, "Failed to switch to subimage {} in '{}':\n", parameters.media_playlist_index,
Path::GetFileName(parameters.filename));
s_state = State::Shutdown;
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
}
// Update running game, this will apply settings as well.
UpdateRunningGame(disc ? disc->GetFileName().c_str() : parameters.filename.c_str(), disc.get(), true);
// Get boot EXE override.
std::string exe_boot;
if (!parameters.override_exe.empty())
{
if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe))
{
Error::SetStringFmt(error, "File '{}' is not a valid executable to boot.",
Path::GetFileName(parameters.override_exe));
s_state = State::Shutdown;
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Overriding boot executable: '{}'", parameters.override_exe);
exe_boot = std::move(parameters.override_exe);
}
else if (do_exe_boot)
{
exe_boot = std::move(parameters.filename);
}
// Check for SBI.
if (!CheckForSBIFile(disc.get(), error))
{
s_state = State::Shutdown;
ClearRunningGame();
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
}
// Check for resuming with hardcore mode.
if (parameters.disable_achievements_hardcore_mode)
Achievements::DisableHardcoreMode();
if (!parameters.save_state.empty() && Achievements::IsHardcoreModeActive())
{
bool cancelled;
if (FullscreenUI::IsInitialized())
{
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("Achievements", "Resuming state"),
[parameters = std::move(parameters)](bool approved) mutable {
if (approved)
{
parameters.disable_achievements_hardcore_mode = true;
BootSystem(std::move(parameters), nullptr);
}
});
cancelled = true;
}
else
{
cancelled = !Achievements::ConfirmHardcoreModeDisable(TRANSLATE("Achievements", "Resuming state"));
}
if (cancelled)
{
s_state = State::Shutdown;
ClearRunningGame();
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
// Technically a failure, but user-initiated. Returning false here would try to display a non-existent error.
return true;
}
}
// Load BIOS image.
2024-05-14 03:57:29 +00:00
if (!LoadBIOS(error))
{
s_state = State::Shutdown;
ClearRunningGame();
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
}
// Component setup.
if (!Initialize(parameters.force_software_renderer, error))
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
{
s_state = State::Shutdown;
ClearRunningGame();
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
return false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
}
// Insert disc.
if (disc)
CDROM::InsertMedia(std::move(disc), disc_region);
UpdateControllers();
UpdateMemoryCardTypes();
2021-01-21 07:59:40 +00:00
UpdateMultitaps();
InternalReset();
// Load EXE late after BIOS.
if (!exe_boot.empty() && !LoadEXE(exe_boot.c_str()))
{
Error::SetStringFmt(error, "Failed to load EXE file '{}'", Path::GetFileName(exe_boot));
DestroySystem();
return false;
}
else if (do_psf_boot && !PSFLoader::Load(parameters.filename.c_str()))
{
Error::SetStringFmt(error, "Failed to load PSF file '{}'", Path::GetFileName(parameters.filename));
DestroySystem();
return false;
}
// Apply fastboot patch if enabled.
2023-01-09 09:34:40 +00:00
if (CDROM::HasMedia() && (parameters.override_fast_boot.has_value() ? parameters.override_fast_boot.value() :
g_settings.bios_patch_fast_boot))
{
2024-04-21 15:52:02 +00:00
if (!CDROM::IsMediaPS1Disc())
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Not fast booting non-PS1 disc.");
}
2024-04-21 15:52:02 +00:00
else if (!s_bios_image_info || !s_bios_image_info->patch_compatible)
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Not patching fast boot, as BIOS is not patch compatible.");
}
2022-11-15 11:22:42 +00:00
else
{
2024-04-21 15:52:02 +00:00
// TODO: Fast boot without patches...
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE);
s_was_fast_booted = true;
}
}
// Texture replacement preloading.
// TODO: Move this and everything else below OnSystemStarted().
2024-06-02 14:45:36 +00:00
TextureReplacements::SetGameID(s_running_game_serial);
// Good to go.
2023-08-23 12:06:48 +00:00
s_state = State::Running;
2022-08-01 13:39:41 +00:00
SPU::GetOutputStream()->SetPaused(false);
2023-08-23 12:06:48 +00:00
FullscreenUI::OnSystemStarted();
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
2024-05-26 14:10:39 +00:00
#ifdef ENABLE_GDB_SERVER
if (g_settings.debugging.enable_gdb_server)
GDBServer::Initialize(g_settings.debugging.gdb_server_port);
#endif
2023-08-23 12:06:48 +00:00
Host::OnSystemStarted();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
// try to load the state, if it fails, bail out
if (!parameters.save_state.empty())
{
std::unique_ptr<ByteStream> stream =
ByteStream::OpenFile(parameters.save_state.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, error);
if (!stream)
{
Error::AddPrefixFmt(error, "Failed to load save state file '{}' for booting:\n",
Path::GetFileName(parameters.save_state));
DestroySystem();
return false;
}
if (!LoadStateFromStream(stream.get(), error, true))
{
DestroySystem();
return false;
}
}
if (parameters.load_image_to_ram || g_settings.cdrom_load_image_to_ram)
2023-01-09 09:34:40 +00:00
CDROM::PrecacheMedia();
if (parameters.start_audio_dump)
2022-11-21 02:55:24 +00:00
StartDumpingAudio();
2023-08-23 12:06:48 +00:00
if (g_settings.start_paused || parameters.override_start_paused.value_or(false))
PauseSystem(true);
UpdateSpeedLimiterState();
ResetPerformanceCounters();
return true;
}
bool System::Initialize(bool force_software_renderer, Error* error)
2019-09-09 07:01:26 +00:00
{
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_frame_number = 1;
s_internal_frame_number = 1;
s_target_speed = g_settings.emulation_speed;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_throttle_frequency = 60.0f;
s_frame_period = 0;
s_next_frame_time = 0;
s_turbo_enabled = false;
s_fast_forward_enabled = false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_rewind_load_frequency = -1;
s_rewind_load_counter = -1;
s_rewinding_first_save = true;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_average_frame_time_accumulator = 0.0f;
2023-01-07 03:09:20 +00:00
s_minimum_frame_time_accumulator = 0.0f;
s_maximum_frame_time_accumulator = 0.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_vps = 0.0f;
s_fps = 0.0f;
s_speed = 0.0f;
2023-01-07 03:09:20 +00:00
s_minimum_frame_time = 0.0f;
s_maximum_frame_time = 0.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_average_frame_time = 0.0f;
s_cpu_thread_usage = 0.0f;
s_cpu_thread_time = 0.0f;
s_sw_thread_usage = 0.0f;
s_sw_thread_time = 0.0f;
2022-09-03 04:15:15 +00:00
s_average_gpu_time = 0.0f;
s_accumulated_gpu_time = 0.0f;
s_gpu_usage = 0.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_last_frame_number = 0;
s_last_internal_frame_number = 0;
s_last_global_tick_counter = 0;
2022-09-03 04:15:15 +00:00
s_presents_since_last_update = 0;
s_last_cpu_time = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_fps_timer.Reset();
s_frame_timer.Reset();
2023-01-07 03:09:20 +00:00
s_frame_time_history.fill(0.0f);
s_frame_time_history_pos = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
TimingEvents::Initialize();
CPU::Initialize();
2020-10-18 04:43:55 +00:00
if (!Bus::Initialize())
{
CPU::Shutdown();
2020-10-18 04:43:55 +00:00
return false;
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::CodeCache::Initialize();
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, error))
{
Bus::Shutdown();
CPU::Shutdown();
return false;
}
GTE::UpdateAspectRatio();
if (g_settings.gpu_pgxp_enable)
CPU::PGXP::Initialize();
// Was startup cancelled? (e.g. shading compilers took too long and the user closed the application)
if (IsStartupCancelled())
{
g_gpu.reset();
if (!s_keep_gpu_device_on_shutdown)
{
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
}
if (g_settings.gpu_pgxp_enable)
CPU::PGXP::Shutdown();
CPU::Shutdown();
Bus::Shutdown();
return false;
}
2023-01-11 08:51:38 +00:00
DMA::Initialize();
2023-01-09 09:34:40 +00:00
CDROM::Initialize();
2023-01-11 09:10:21 +00:00
Pad::Initialize();
2023-01-11 08:58:25 +00:00
Timers::Initialize();
2022-08-01 13:39:41 +00:00
SPU::Initialize();
2022-12-20 10:45:42 +00:00
MDEC::Initialize();
2023-01-11 09:03:07 +00:00
SIO::Initialize();
PCDrv::Initialize();
PostProcessing::Initialize();
2020-07-17 14:25:08 +00:00
s_cpu_thread_handle = Threading::ThreadHandle::GetForCallingThread();
UpdateThrottlePeriod();
UpdateMemorySaveStateSettings();
return true;
2019-09-09 07:01:26 +00:00
}
void System::DestroySystem()
{
DebugAssert(!s_system_executing);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (s_state == State::Shutdown)
return;
2024-05-26 14:10:39 +00:00
#ifdef ENABLE_GDB_SERVER
GDBServer::Shutdown();
#endif
2023-08-23 12:06:48 +00:00
Host::ClearOSDMessages();
PostProcessing::Shutdown();
SaveStateSelectorUI::Clear();
2023-08-23 12:06:48 +00:00
FullscreenUI::OnSystemDestroyed();
InputManager::PauseVibration();
if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver();
SetTimerResolutionIncreased(false);
s_cpu_thread_usage = {};
ClearMemorySaveStates();
2024-06-02 14:45:36 +00:00
TextureReplacements::Shutdown();
PCDrv::Shutdown();
2023-01-11 09:03:07 +00:00
SIO::Shutdown();
2022-12-20 10:45:42 +00:00
MDEC::Shutdown();
2022-08-01 13:39:41 +00:00
SPU::Shutdown();
2023-01-11 08:58:25 +00:00
Timers::Shutdown();
2023-01-11 09:10:21 +00:00
Pad::Shutdown();
2023-01-09 09:34:40 +00:00
CDROM::Shutdown();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_gpu.reset();
2023-01-11 08:51:38 +00:00
DMA::Shutdown();
CPU::PGXP::Shutdown();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::CodeCache::Shutdown();
Bus::Shutdown();
CPU::Shutdown();
TimingEvents::Shutdown();
ClearRunningGame();
// Restore present-all-frames behavior.
if (s_keep_gpu_device_on_shutdown && g_gpu_device)
{
2024-05-23 15:59:35 +00:00
UpdateDisplayVSync();
}
else
{
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
}
s_bios_hash = {};
s_bios_image_info = nullptr;
s_was_fast_booted = false;
s_cheat_list.reset();
s_state = State::Shutdown;
Host::OnSystemDestroyed();
2023-12-14 06:07:57 +00:00
Host::OnIdleStateChanged();
}
void System::ClearRunningGame()
{
2023-08-23 12:06:48 +00:00
UpdateSessionTime(s_running_game_serial);
2022-10-05 08:29:08 +00:00
s_running_game_serial.clear();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_running_game_path.clear();
s_running_game_title.clear();
s_running_game_entry = nullptr;
s_running_game_hash = 0;
2022-10-05 08:29:08 +00:00
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
Achievements::GameChanged(s_running_game_path, nullptr);
2023-08-23 12:06:48 +00:00
2024-07-04 04:40:16 +00:00
UpdateRichPresence(true);
}
void System::Execute()
{
for (;;)
{
switch (s_state)
{
case State::Running:
{
s_system_executing = true;
// TODO: Purge reset/restore
g_gpu->RestoreDeviceContext();
TimingEvents::UpdateCPUDowncount();
if (s_rewind_load_counter >= 0)
DoRewind();
else
CPU::Execute();
s_system_executing = false;
continue;
}
case State::Stopping:
{
DestroySystem();
return;
}
case State::Paused:
default:
return;
}
}
}
void System::FrameDone()
{
s_frame_number++;
// Vertex buffer is shared, need to flush what we have.
g_gpu->FlushRender();
// Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
2023-08-23 12:06:48 +00:00
// TODO: when running ahead, we can skip this (and the flush above)
SPU::GeneratePendingSamples();
if (s_cheat_list)
s_cheat_list->Apply();
2023-08-30 10:34:48 +00:00
if (Achievements::IsActive())
Achievements::FrameUpdate();
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-30 10:34:48 +00:00
PollDiscordPresence();
#endif
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_SOCKET_MULTIPLEXER
if (s_socket_multiplexer)
s_socket_multiplexer->PollEventsWithTimeout(0);
#endif
2024-05-25 05:58:41 +00:00
Host::FrameDone();
if (s_frame_step_request)
{
s_frame_step_request = false;
PauseSystem(true);
}
// Save states for rewind and runahead.
if (s_rewind_save_counter >= 0)
{
if (s_rewind_save_counter == 0)
{
SaveRewindState();
s_rewind_save_counter = s_rewind_save_frequency;
}
else
{
s_rewind_save_counter--;
}
}
else if (s_runahead_frames > 0)
{
// We don't want to poll during replay, because otherwise we'll lose frames.
if (s_runahead_replay_frames == 0)
{
// For runahead, poll input early, that way we can use the remainder of this frame to replay.
// *technically* this means higher input latency (by less than a frame), but runahead itself
// counter-acts that.
Host::PumpMessagesOnCPUThread();
2023-08-23 12:06:48 +00:00
InputManager::PollSources();
g_gpu->RestoreDeviceContext();
2023-08-23 12:06:48 +00:00
if (IsExecutionInterrupted())
{
s_system_interrupted = false;
CPU::ExitExecution();
return;
}
}
if (DoRunahead())
{
// running ahead, get it done as soon as possible
return;
}
SaveRunaheadState();
}
Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
// pre-frame sleep accounting (input lag reduction)
const Common::Timer::Value pre_frame_sleep_until = s_next_frame_time + s_pre_frame_sleep_time;
s_last_active_frame_time = current_time - s_frame_start_time;
if (s_pre_frame_sleep)
AccumulatePreFrameSleepTime();
// explicit present (frame pacing)
const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
s_last_presented_internal_frame_number = s_internal_frame_number;
const bool skip_this_frame = (((s_skip_presenting_duplicate_frames && !is_unique_frame &&
s_skipped_frame_count < MAX_SKIPPED_DUPLICATE_FRAME_COUNT) ||
(!s_optimal_frame_pacing && current_time > s_next_frame_time &&
s_skipped_frame_count < MAX_SKIPPED_TIMEOUT_FRAME_COUNT) ||
g_gpu_device->ShouldSkipPresentingFrame()) &&
!s_syncing_to_host_with_vsync && !IsExecutionInterrupted());
if (!skip_this_frame)
{
s_skipped_frame_count = 0;
const bool throttle_before_present = (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted());
const bool explicit_present = (throttle_before_present && g_gpu_device->GetFeatures().explicit_present);
if (explicit_present)
{
const bool do_present = PresentDisplay(false, true);
Throttle(current_time);
if (do_present)
g_gpu_device->SubmitPresent();
}
else
{
if (throttle_before_present)
Throttle(current_time);
PresentDisplay(false, false);
if (!throttle_before_present && s_throttler_enabled && !IsExecutionInterrupted())
Throttle(current_time);
}
}
else
{
2024-05-23 10:55:28 +00:00
DEBUG_LOG("Skipping displaying frame");
s_skipped_frame_count++;
if (s_throttler_enabled)
Throttle(current_time);
}
// pre-frame sleep (input lag reduction)
current_time = Common::Timer::GetCurrentValue();
if (s_pre_frame_sleep)
{
// don't sleep if it's under 1ms, because we're just going to overshoot (or spin).
if (pre_frame_sleep_until > current_time &&
Common::Timer::ConvertValueToMilliseconds(pre_frame_sleep_until - current_time) >= 1)
{
Common::Timer::SleepUntil(pre_frame_sleep_until, true);
current_time = Common::Timer::GetCurrentValue();
}
}
s_frame_start_time = current_time;
// Input poll already done above
if (s_runahead_frames == 0)
{
Host::PumpMessagesOnCPUThread();
2023-08-23 12:06:48 +00:00
InputManager::PollSources();
if (IsExecutionInterrupted())
{
s_system_interrupted = false;
CPU::ExitExecution();
return;
}
}
g_gpu->RestoreDeviceContext();
// Update perf counters *after* throttling, we want to measure from start-of-frame
// to start-of-frame, not end-of-frame to end-of-frame (will be noisy due to different
// amounts of computation happening in each frame).
System::UpdatePerformanceCounters();
}
void System::SetThrottleFrequency(float frequency)
{
if (s_throttle_frequency == frequency)
return;
s_throttle_frequency = frequency;
UpdateThrottlePeriod();
}
void System::UpdateThrottlePeriod()
{
if (s_target_speed > std::numeric_limits<double>::epsilon())
{
const double target_speed = std::max(static_cast<double>(s_target_speed), std::numeric_limits<double>::epsilon());
s_frame_period =
Common::Timer::ConvertSecondsToValue(1.0 / (static_cast<double>(s_throttle_frequency) * target_speed));
}
else
{
s_frame_period = 1;
}
ResetThrottler();
}
void System::ResetThrottler()
{
s_next_frame_time = Common::Timer::GetCurrentValue() + s_frame_period;
s_pre_frame_sleep_time = 0;
}
void System::Throttle(Common::Timer::Value current_time)
{
// If we're running too slow, advance the next frame time based on the time we lost. Effectively skips
// running those frames at the intended time, because otherwise if we pause in the debugger, we'll run
// hundreds of frames when we resume.
if (current_time > s_next_frame_time)
{
const Common::Timer::Value diff = static_cast<s64>(current_time) - static_cast<s64>(s_next_frame_time);
s_next_frame_time += (diff / s_frame_period) * s_frame_period + s_frame_period;
return;
}
#ifdef ENABLE_SOCKET_MULTIPLEXER
// If we are using the socket multiplier, and have clients, then use it to sleep instead.
// That way in a query->response->query->response chain, we don't process only one message per frame.
if (s_socket_multiplexer && s_socket_multiplexer->HasAnyClientSockets())
{
Common::Timer::Value poll_start_time = current_time;
for (;;)
{
const u32 sleep_ms =
static_cast<u32>(Common::Timer::ConvertValueToMilliseconds(s_next_frame_time - poll_start_time));
s_socket_multiplexer->PollEventsWithTimeout(sleep_ms);
poll_start_time = Common::Timer::GetCurrentValue();
if (poll_start_time >= s_next_frame_time || (!g_settings.display_optimal_frame_pacing && sleep_ms == 0))
break;
}
}
else
{
// Use a spinwait if we undersleep for all platforms except android.. don't want to burn battery.
// Linux also seems to do a much better job of waking up at the requested time.
#if !defined(__linux__)
Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_optimal_frame_pacing);
#else
Common::Timer::SleepUntil(s_next_frame_time, false);
#endif
}
#else
// No spinwait on Android, see above.
Common::Timer::SleepUntil(s_next_frame_time, false);
#endif
2023-09-10 04:14:27 +00:00
#if 0
DEV_LOG("Asked for {:.2f} ms, slept for {:.2f} ms, {:.2f} ms late",
Common::Timer::ConvertValueToMilliseconds(s_next_frame_time - current_time),
Common::Timer::ConvertValueToMilliseconds(Common::Timer::GetCurrentValue() - current_time),
Common::Timer::ConvertValueToMilliseconds(Common::Timer::GetCurrentValue() - s_next_frame_time));
2023-09-10 04:14:27 +00:00
#endif
s_next_frame_time += s_frame_period;
}
void System::SingleStepCPU()
{
CPU::SetSingleStepFlag();
2023-08-30 10:34:48 +00:00
// If this gets called when the system is executing, we're not going to end up here..
if (IsPaused())
PauseSystem(false);
}
void System::IncrementInternalFrameNumber()
{
s_internal_frame_number++;
}
bool System::CreateGPU(GPURenderer renderer, bool is_switching, Error* error)
{
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
2020-06-18 14:18:17 +00:00
if (!g_gpu_device ||
(renderer != GPURenderer::Software && !GPUDevice::IsSameRenderAPI(g_gpu_device->GetRenderAPI(), api)))
{
if (g_gpu_device)
{
2024-05-23 10:55:28 +00:00
WARNING_LOG("Recreating GPU device, expecting {} got {}", GPUDevice::RenderAPIToString(api),
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
PostProcessing::Shutdown();
}
Host::ReleaseGPUDevice();
if (!Host::CreateGPUDevice(api, error))
{
Host::ReleaseRenderWindow();
return false;
}
if (is_switching)
PostProcessing::Initialize();
}
if (renderer == GPURenderer::Software)
g_gpu = GPU::CreateSoftwareRenderer();
else
g_gpu = GPU::CreateHardwareRenderer();
if (!g_gpu)
2019-10-26 02:57:35 +00:00
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to initialize {} renderer, falling back to software renderer",
Settings::GetRendererName(renderer));
2024-05-24 11:57:51 +00:00
Host::AddOSDMessage(
fmt::format(TRANSLATE_FS("System", "Failed to initialize {} renderer, falling back to software renderer."),
Settings::GetRendererName(renderer)),
Host::OSD_CRITICAL_ERROR_DURATION);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_gpu.reset();
g_gpu = GPU::CreateSoftwareRenderer();
if (!g_gpu)
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to create fallback software renderer.");
if (!s_keep_gpu_device_on_shutdown)
{
PostProcessing::Shutdown();
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
}
2019-10-26 02:57:35 +00:00
return false;
}
2019-10-26 02:57:35 +00:00
}
return true;
}
bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state)
2019-09-14 10:28:47 +00:00
{
2019-10-04 10:48:29 +00:00
if (!sw.DoMarker("System"))
return false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
sw.Do(&s_region);
sw.Do(&s_frame_number);
sw.Do(&s_internal_frame_number);
2019-10-04 10:48:29 +00:00
// Don't bother checking this at all for memory states, since they won't have a different BIOS...
if (!is_memory_state)
{
BIOS::ImageInfo::Hash bios_hash = s_bios_hash;
sw.DoBytesEx(bios_hash.data(), BIOS::ImageInfo::HASH_SIZE, 58, s_bios_hash.data());
if (bios_hash != s_bios_hash)
{
WARNING_LOG("BIOS hash mismatch: System: {} | State: {}", BIOS::ImageInfo::GetHashString(s_bios_hash),
BIOS::ImageInfo::GetHashString(bios_hash));
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"StateBIOSMismatch", ICON_FA_EXCLAMATION_TRIANGLE,
TRANSLATE_STR("System", "This save state was created with a different BIOS. This may cause stability issues."),
Host::OSD_WARNING_DURATION);
}
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (!sw.DoMarker("CPU") || !CPU::DoState(sw))
2019-09-14 10:28:47 +00:00
return false;
if (sw.IsReading())
{
if (is_memory_state)
CPU::CodeCache::InvalidateAllRAMBlocks();
else
CPU::CodeCache::Reset();
}
// only reset pgxp if we're not runahead-rollbacking. the value checks will save us from broken rendering, and it
// saves using imprecise values for a frame in 30fps games.
if (sw.IsReading() && g_settings.gpu_pgxp_enable && !is_memory_state)
CPU::PGXP::Reset();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (!sw.DoMarker("Bus") || !Bus::DoState(sw))
2019-09-14 10:28:47 +00:00
return false;
2023-01-11 08:51:38 +00:00
if (!sw.DoMarker("DMA") || !DMA::DoState(sw))
2019-09-14 10:28:47 +00:00
return false;
if (!sw.DoMarker("InterruptController") || !InterruptController::DoState(sw))
2019-09-17 06:26:00 +00:00
return false;
g_gpu->RestoreDeviceContext();
if (!sw.DoMarker("GPU") || !g_gpu->DoState(sw, host_texture, update_display))
2019-09-14 10:28:47 +00:00
return false;
2023-01-09 09:34:40 +00:00
if (!sw.DoMarker("CDROM") || !CDROM::DoState(sw))
2019-09-17 09:22:39 +00:00
return false;
if (!sw.DoMarker("Pad") || !Pad::DoState(sw, is_memory_state))
return false;
2023-01-11 08:58:25 +00:00
if (!sw.DoMarker("Timers") || !Timers::DoState(sw))
2019-09-20 13:40:19 +00:00
return false;
2022-08-01 13:39:41 +00:00
if (!sw.DoMarker("SPU") || !SPU::DoState(sw))
return false;
2022-12-20 10:45:42 +00:00
if (!sw.DoMarker("MDEC") || !MDEC::DoState(sw))
2019-09-29 02:51:34 +00:00
return false;
2023-01-11 09:03:07 +00:00
if (!sw.DoMarker("SIO") || !SIO::DoState(sw))
2019-12-09 14:28:58 +00:00
return false;
if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw))
return false;
if (!sw.DoMarker("Overclock"))
return false;
bool cpu_overclock_active = g_settings.cpu_overclock_active;
u32 cpu_overclock_numerator = g_settings.cpu_overclock_numerator;
u32 cpu_overclock_denominator = g_settings.cpu_overclock_denominator;
sw.Do(&cpu_overclock_active);
sw.Do(&cpu_overclock_numerator);
sw.Do(&cpu_overclock_denominator);
if (sw.IsReading() && (cpu_overclock_active != g_settings.cpu_overclock_active ||
(cpu_overclock_active && (g_settings.cpu_overclock_numerator != cpu_overclock_numerator ||
g_settings.cpu_overclock_denominator != cpu_overclock_denominator))))
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"state_overclock_difference", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "WARNING: CPU overclock ({}%) was different in save state ({}%)."),
g_settings.cpu_overclock_enable ? g_settings.GetCPUOverclockPercent() : 100u,
cpu_overclock_active ?
Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) :
100u),
Host::OSD_WARNING_DURATION);
UpdateOverclock();
}
if (!is_memory_state)
{
if (sw.GetVersion() >= 56) [[unlikely]]
{
if (!sw.DoMarker("Cheevos"))
return false;
if (!Achievements::DoState(sw))
return false;
}
else
{
// loading an old state without cheevos, so reset the runtime
2023-09-07 10:13:48 +00:00
Achievements::ResetClient();
}
}
2019-09-14 10:28:47 +00:00
return !sw.HasError();
}
2024-05-14 03:57:29 +00:00
bool System::LoadBIOS(Error* error)
{
2024-05-14 03:57:29 +00:00
std::optional<BIOS::Image> bios_image = BIOS::GetBIOSImage(s_region, error);
if (!bios_image.has_value())
return false;
s_bios_image_info = bios_image->info;
s_bios_hash = bios_image->hash;
if (s_bios_image_info)
2024-05-23 10:55:28 +00:00
INFO_LOG("Using BIOS: {}", s_bios_image_info->description);
else
WARNING_LOG("Using an unknown BIOS: {}", BIOS::ImageInfo::GetHashString(s_bios_hash));
std::memcpy(Bus::g_bios, bios_image->data.data(), Bus::BIOS_SIZE);
return true;
}
void System::InternalReset()
2019-09-09 07:01:26 +00:00
{
if (IsShutdown())
return;
2024-04-21 14:19:44 +00:00
TimingEvents::Reset();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::Reset();
CPU::CodeCache::Reset();
if (g_settings.gpu_pgxp_enable)
CPU::PGXP::Initialize();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
Bus::Reset();
2023-01-11 08:51:38 +00:00
DMA::Reset();
InterruptController::Reset();
g_gpu->Reset(true);
2023-01-09 09:34:40 +00:00
CDROM::Reset();
2023-01-11 09:10:21 +00:00
Pad::Reset();
2023-01-11 08:58:25 +00:00
Timers::Reset();
2022-08-01 13:39:41 +00:00
SPU::Reset();
2022-12-20 10:45:42 +00:00
MDEC::Reset();
2023-01-11 09:03:07 +00:00
SIO::Reset();
PCDrv::Reset();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_frame_number = 1;
s_internal_frame_number = 0;
InterruptExecution();
ResetPerformanceCounters();
2023-09-07 10:13:48 +00:00
Achievements::ResetClient();
2019-09-09 07:01:26 +00:00
}
std::string System::GetMediaPathFromSaveState(const char* path)
{
std::string ret;
std::unique_ptr<ByteStream> stream(ByteStream::OpenFile(path, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE));
if (stream)
{
SAVE_STATE_HEADER header;
if (stream->Read2(&header, sizeof(header)) && header.magic == SAVE_STATE_MAGIC &&
header.version >= SAVE_STATE_MINIMUM_VERSION && header.version <= SAVE_STATE_VERSION)
{
if (header.media_filename_length > 0)
{
ret.resize(header.media_filename_length);
if (!stream->SeekAbsolute(header.offset_to_media_filename) ||
!stream->Read2(ret.data(), header.media_filename_length))
{
ret = {};
}
}
}
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
return ret;
}
bool System::LoadStateFromStream(ByteStream* state, Error* error, bool update_display, bool ignore_media)
2019-09-14 10:28:47 +00:00
{
Assert(IsValid());
SAVE_STATE_HEADER header;
if (!state->Read2(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC)
{
Error::SetStringView(error, "Incorrect file format.");
return false;
}
if (header.version < SAVE_STATE_MINIMUM_VERSION)
{
Error::SetStringFmt(
error, TRANSLATE_FS("System", "Save state is incompatible: minimum version is {0} but state is version {1}."),
SAVE_STATE_MINIMUM_VERSION, header.version);
return false;
}
if (header.version > SAVE_STATE_VERSION)
{
Error::SetStringFmt(
error, TRANSLATE_FS("System", "Save state is incompatible: maximum version is {0} but state is version {1}."),
SAVE_STATE_VERSION, header.version);
return false;
}
2023-08-30 07:38:17 +00:00
if (!ignore_media)
{
2023-08-30 07:38:17 +00:00
std::string media_filename;
std::unique_ptr<CDImage> media;
if (header.media_filename_length > 0)
{
2023-08-30 07:38:17 +00:00
media_filename.resize(header.media_filename_length);
if (!state->SeekAbsolute(header.offset_to_media_filename) ||
!state->Read2(media_filename.data(), header.media_filename_length))
2023-08-30 07:38:17 +00:00
{
return false;
}
2023-08-30 07:38:17 +00:00
std::unique_ptr<CDImage> old_media = CDROM::RemoveMedia(false);
if (old_media && old_media->GetFileName() == media_filename)
{
2024-05-23 10:55:28 +00:00
INFO_LOG("Re-using same media '{}'", media_filename);
2023-08-30 07:38:17 +00:00
media = std::move(old_media);
}
else
{
Error local_error;
media =
CDImage::Open(media_filename.c_str(), g_settings.cdrom_load_image_patches, error ? error : &local_error);
2023-08-30 07:38:17 +00:00
if (!media)
{
2023-08-30 07:38:17 +00:00
if (old_media)
{
2023-09-16 14:51:28 +00:00
Host::AddOSDMessage(
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open CD image from save state '{}': {}.\nUsing "
"existing image '{}', this may result in instability."),
media_filename, error ? error->GetDescription() : local_error.GetDescription(),
old_media->GetFileName()),
2023-09-16 14:51:28 +00:00
Host::OSD_CRITICAL_ERROR_DURATION);
2023-08-30 07:38:17 +00:00
media = std::move(old_media);
header.media_subimage_index = media->GetCurrentSubImage();
}
else
{
Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to open CD image '{}' used by save state:\n"),
Path::GetFileName(media_filename));
2023-08-30 07:38:17 +00:00
return false;
}
}
}
}
else
{
// Skip updating media if there is none, and none in the state. That way we don't wipe out EXE boot.
ignore_media = !CDROM::HasMedia();
}
if (!ignore_media)
UpdateRunningGame(media_filename.c_str(), media.get(), false);
2023-08-30 07:38:17 +00:00
if (media && header.version >= 51)
{
const u32 num_subimages = media->HasSubImages() ? media->GetSubImageCount() : 1;
if (header.media_subimage_index >= num_subimages ||
(media->HasSubImages() && media->GetCurrentSubImage() != header.media_subimage_index &&
!media->SwitchSubImage(header.media_subimage_index, error)))
2023-08-30 07:38:17 +00:00
{
Error::AddPrefixFmt(
error, TRANSLATE_FS("System", "Failed to switch to subimage {} in CD image '{}' used by save state:\n"),
header.media_subimage_index + 1u, Path::GetFileName(media_filename));
2023-08-30 07:38:17 +00:00
return false;
}
else
{
2024-05-23 10:55:28 +00:00
INFO_LOG("Switched to subimage {} in '{}'", header.media_subimage_index, media_filename.c_str());
2023-08-30 07:38:17 +00:00
}
}
CDROM::Reset();
if (media)
2020-08-15 10:38:17 +00:00
{
const DiscRegion region =
GameList::GetCustomRegionForPath(media_filename).value_or(GetRegionForImage(media.get()));
2023-08-30 07:38:17 +00:00
CDROM::InsertMedia(std::move(media), region);
if (g_settings.cdrom_load_image_to_ram)
CDROM::PrecacheMedia();
2020-08-15 10:38:17 +00:00
}
else
2020-08-15 10:38:17 +00:00
{
2023-08-30 07:38:17 +00:00
CDROM::RemoveMedia(false);
2020-08-15 10:38:17 +00:00
}
2023-08-30 07:38:17 +00:00
// ensure the correct card is loaded
if (g_settings.HasAnyPerGameMemoryCards())
UpdatePerGameMemoryCards();
}
2023-08-30 07:38:17 +00:00
ClearMemorySaveStates();
// Updating game/loading settings can turn on hardcore mode. Catch this.
Achievements::DisableHardcoreMode();
if (!state->SeekAbsolute(header.offset_to_data))
return false;
if (header.data_compression_type == SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE)
{
StateWrapper sw(state, StateWrapper::Mode::Read, header.version);
if (!DoState(sw, nullptr, update_display, false))
{
Error::SetStringView(error, "Save state stream is corrupted.");
return false;
}
}
else if (header.data_compression_type == SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD)
{
std::unique_ptr<ByteStream> dstream(ByteStream::CreateZstdDecompressStream(state, header.data_compressed_size));
StateWrapper sw(dstream.get(), StateWrapper::Mode::Read, header.version);
if (!DoState(sw, nullptr, update_display, false))
{
Error::SetStringView(error, "Save state stream is corrupted.");
return false;
}
}
else
{
Error::SetStringFmt(error, "Unknown save state compression type {}", header.data_compression_type);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
return false;
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (s_state == State::Starting)
s_state = State::Running;
InterruptExecution();
ResetPerformanceCounters();
ResetThrottler();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
return true;
2019-09-14 10:28:47 +00:00
}
bool System::SaveStateToStream(ByteStream* state, Error* error, u32 screenshot_size /* = 256 */,
2023-08-30 07:38:17 +00:00
u32 compression_method /* = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE*/,
bool ignore_media /* = false*/)
2019-09-14 10:28:47 +00:00
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (IsShutdown())
return false;
SAVE_STATE_HEADER header = {};
const u64 header_position = state->GetPosition();
if (!state->Write2(&header, sizeof(header)))
return false;
// fill in header
header.magic = SAVE_STATE_MAGIC;
header.version = SAVE_STATE_VERSION;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
StringUtil::Strlcpy(header.title, s_running_game_title.c_str(), sizeof(header.title));
2022-10-05 08:29:08 +00:00
StringUtil::Strlcpy(header.serial, s_running_game_serial.c_str(), sizeof(header.serial));
2023-08-30 07:38:17 +00:00
if (CDROM::HasMedia() && !ignore_media)
2020-07-22 16:36:05 +00:00
{
2023-01-09 09:34:40 +00:00
const std::string& media_filename = CDROM::GetMediaFileName();
2020-07-22 16:36:05 +00:00
header.offset_to_media_filename = static_cast<u32>(state->GetPosition());
header.media_filename_length = static_cast<u32>(media_filename.length());
2023-01-09 09:34:40 +00:00
header.media_subimage_index = CDROM::GetMedia()->HasSubImages() ? CDROM::GetMedia()->GetCurrentSubImage() : 0;
2020-07-22 16:36:05 +00:00
if (!media_filename.empty() && !state->Write2(media_filename.data(), header.media_filename_length))
return false;
}
// save screenshot
if (screenshot_size > 0)
{
// assume this size is the width
const float display_aspect_ratio = g_gpu->ComputeDisplayAspectRatio();
const u32 screenshot_width = screenshot_size;
const u32 screenshot_height =
std::max(1u, static_cast<u32>(static_cast<float>(screenshot_width) /
((display_aspect_ratio > 0.0f) ? display_aspect_ratio : 1.0f)));
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Saving {}x{} screenshot for state", screenshot_width, screenshot_height);
std::vector<u32> screenshot_buffer;
u32 screenshot_stride;
GPUTexture::Format screenshot_format;
2023-08-27 08:13:50 +00:00
if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height,
GSVector4i(0, 0, screenshot_width, screenshot_height), false,
&screenshot_buffer, &screenshot_stride, &screenshot_format) &&
GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
screenshot_format))
{
if (screenshot_stride != (screenshot_width * sizeof(u32)))
{
2024-05-24 11:57:51 +00:00
WARNING_LOG("Failed to save {}x{} screenshot for save state due to incorrect stride({})", screenshot_width,
2024-05-23 10:55:28 +00:00
screenshot_height, screenshot_stride);
}
else
{
if (g_gpu_device->UsesLowerLeftOrigin())
{
GPUTexture::FlipTextureDataRGBA8(screenshot_width, screenshot_height,
reinterpret_cast<u8*>(screenshot_buffer.data()), screenshot_stride);
}
header.offset_to_screenshot = static_cast<u32>(state->GetPosition());
header.screenshot_width = screenshot_width;
header.screenshot_height = screenshot_height;
header.screenshot_size = static_cast<u32>(screenshot_buffer.size() * sizeof(u32));
if (!state->Write2(screenshot_buffer.data(), header.screenshot_size))
return false;
}
}
else
{
2024-05-23 10:55:28 +00:00
WARNING_LOG("Failed to save {}x{} screenshot for save state due to render/conversion failure", screenshot_width,
screenshot_height);
}
}
// write data
{
header.offset_to_data = static_cast<u32>(state->GetPosition());
g_gpu->RestoreDeviceContext();
header.data_compression_type = compression_method;
bool result = false;
if (compression_method == SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE)
{
StateWrapper sw(state, StateWrapper::Mode::Write, SAVE_STATE_VERSION);
result = DoState(sw, nullptr, false, false);
header.data_uncompressed_size = static_cast<u32>(state->GetPosition() - header.offset_to_data);
}
else if (compression_method == SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD)
{
std::unique_ptr<ByteStream> cstream(ByteStream::CreateZstdCompressStream(state, 0));
StateWrapper sw(cstream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
result = DoState(sw, nullptr, false, false) && cstream->Commit();
header.data_uncompressed_size = static_cast<u32>(cstream->GetPosition());
header.data_compressed_size = static_cast<u32>(state->GetPosition() - header.offset_to_data);
}
if (!result)
return false;
}
// re-write header
const u64 end_position = state->GetPosition();
if (!state->SeekAbsolute(header_position) || !state->Write2(&header, sizeof(header)) ||
!state->SeekAbsolute(end_position))
{
return false;
}
return true;
2019-09-14 10:28:47 +00:00
}
float System::GetTargetSpeed()
{
return s_target_speed;
}
float System::GetAudioNominalRate()
{
2024-05-23 15:59:35 +00:00
return (s_throttler_enabled || s_syncing_to_host_with_vsync) ? s_target_speed : 1.0f;
}
void System::UpdatePerformanceCounters()
{
2023-01-07 03:09:20 +00:00
const float frame_time = static_cast<float>(s_frame_timer.GetTimeMillisecondsAndReset());
2023-01-09 09:34:40 +00:00
s_minimum_frame_time_accumulator =
(s_minimum_frame_time_accumulator == 0.0f) ? frame_time : std::min(s_minimum_frame_time_accumulator, frame_time);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_average_frame_time_accumulator += frame_time;
2023-01-07 03:09:20 +00:00
s_maximum_frame_time_accumulator = std::max(s_maximum_frame_time_accumulator, frame_time);
s_frame_time_history[s_frame_time_history_pos] = frame_time;
s_frame_time_history_pos = (s_frame_time_history_pos + 1) % NUM_FRAME_TIME_SAMPLES;
// update fps counter
const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue();
const Common::Timer::Value ticks_diff = now_ticks - s_fps_timer.GetStartValue();
const float time = static_cast<float>(Common::Timer::ConvertValueToSeconds(ticks_diff));
if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL)
return;
2024-01-21 09:37:29 +00:00
const u32 frames_run = s_frame_number - s_last_frame_number;
const float frames_runf = static_cast<float>(frames_run);
const u32 global_tick_counter = GetGlobalTickCounter();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
// TODO: Make the math here less rubbish
const double pct_divider =
100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) /
Common::Timer::GetFrequency() / 1000000000.0));
const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) *
2024-01-21 09:37:29 +00:00
(1.0 / static_cast<double>(frames_runf));
2023-01-07 03:09:20 +00:00
s_minimum_frame_time = std::exchange(s_minimum_frame_time_accumulator, 0.0f);
2024-01-21 09:37:29 +00:00
s_average_frame_time = std::exchange(s_average_frame_time_accumulator, 0.0f) / frames_runf;
2023-01-07 03:09:20 +00:00
s_maximum_frame_time = std::exchange(s_maximum_frame_time_accumulator, 0.0f);
2024-01-21 09:37:29 +00:00
s_vps = static_cast<float>(frames_runf / time);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_last_frame_number = s_frame_number;
s_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time;
s_last_internal_frame_number = s_internal_frame_number;
s_speed = static_cast<float>(static_cast<double>(global_tick_counter - s_last_global_tick_counter) /
(static_cast<double>(g_ticks_per_second) * time)) *
100.0f;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_last_global_tick_counter = global_tick_counter;
const Threading::Thread* sw_thread = g_gpu->GetSWThread();
const u64 cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
const u64 sw_time = sw_thread ? sw_thread->GetCPUTime() : 0;
const u64 cpu_delta = cpu_time - s_last_cpu_time;
const u64 sw_delta = sw_time - s_last_sw_time;
s_last_cpu_time = cpu_time;
s_last_sw_time = sw_time;
s_cpu_thread_usage = static_cast<float>(static_cast<double>(cpu_delta) * pct_divider);
s_cpu_thread_time = static_cast<float>(static_cast<double>(cpu_delta) * time_divider);
s_sw_thread_usage = static_cast<float>(static_cast<double>(sw_delta) * pct_divider);
s_sw_thread_time = static_cast<float>(static_cast<double>(sw_delta) * time_divider);
s_fps_timer.ResetTo(now_ticks);
if (g_gpu_device->IsGPUTimingEnabled())
2022-09-03 04:15:15 +00:00
{
s_average_gpu_time = s_accumulated_gpu_time / static_cast<float>(std::max(s_presents_since_last_update, 1u));
s_gpu_usage = s_accumulated_gpu_time / (time * 10.0f);
}
s_accumulated_gpu_time = 0.0f;
s_presents_since_last_update = 0;
2024-01-21 09:37:29 +00:00
if (g_settings.display_show_gpu_stats)
g_gpu->UpdateStatistics(frames_run);
if (s_pre_frame_sleep)
UpdatePreFrameSleepTime();
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("FPS: {:.2f} VPS: {:.2f} CPU: {:.2f} GPU: {:.2f} Average: {:.2f}ms Min: {:.2f}ms Max: {:.2f}ms", s_fps,
s_vps, s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time);
Host::OnPerformanceCountersUpdated();
}
void System::ResetPerformanceCounters()
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_last_frame_number = s_frame_number;
s_last_internal_frame_number = s_internal_frame_number;
s_last_global_tick_counter = GetGlobalTickCounter();
s_last_cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
s_last_sw_time = sw_thread->GetCPUTime();
else
s_last_sw_time = 0;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_average_frame_time_accumulator = 0.0f;
2023-01-07 03:09:20 +00:00
s_minimum_frame_time_accumulator = 0.0f;
s_maximum_frame_time_accumulator = 0.0f;
s_frame_timer.Reset();
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_fps_timer.Reset();
ResetThrottler();
2019-09-09 07:01:26 +00:00
}
void System::AccumulatePreFrameSleepTime()
{
DebugAssert(s_pre_frame_sleep);
s_max_active_frame_time = std::max(s_max_active_frame_time, s_last_active_frame_time);
// in case one frame runs over, adjust to compensate
const Common::Timer::Value max_sleep_time_for_this_frame =
s_frame_period - std::min(s_last_active_frame_time, s_frame_period);
if (max_sleep_time_for_this_frame < s_pre_frame_sleep_time)
{
s_pre_frame_sleep_time = Common::AlignDown(max_sleep_time_for_this_frame,
static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
2024-05-23 10:55:28 +00:00
DEV_LOG("Adjust pre-frame time to {} ms due to overrun of {} ms",
Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
}
}
void System::UpdatePreFrameSleepTime()
{
DebugAssert(s_pre_frame_sleep);
const Common::Timer::Value expected_frame_time =
s_max_active_frame_time + Common::Timer::ConvertMillisecondsToValue(g_settings.display_pre_frame_sleep_buffer);
s_pre_frame_sleep_time = Common::AlignDown(s_frame_period - std::min(expected_frame_time, s_frame_period),
static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
2024-05-23 10:55:28 +00:00
DEV_LOG("Set pre-frame time to {} ms (expected frame time of {} ms)",
Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
Common::Timer::ConvertValueToMilliseconds(expected_frame_time));
s_max_active_frame_time = 0;
}
void System::FormatLatencyStats(SmallStringBase& str)
{
AudioStream* audio_stream = SPU::GetOutputStream();
const u32 audio_latency =
AudioStream::GetMSForBufferSize(audio_stream->GetSampleRate(), audio_stream->GetBufferedFramesRelaxed());
const double active_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
const double pre_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time));
const double input_latency = std::ceil(
Common::Timer::ConvertValueToMilliseconds(s_frame_period - s_pre_frame_sleep_time) -
Common::Timer::ConvertValueToMilliseconds(static_cast<Common::Timer::Value>(s_runahead_frames) * s_frame_period));
str.format("AF: {:.0f}ms | PF: {:.0f}ms | IL: {:.0f}ms | AL: {}ms", active_frame_time, pre_frame_time, input_latency,
audio_latency);
}
void System::UpdateSpeedLimiterState()
{
DebugAssert(IsValid());
s_target_speed = s_turbo_enabled ?
g_settings.turbo_speed :
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
s_throttler_enabled = (s_target_speed != 0.0f);
2024-05-25 05:58:41 +00:00
s_optimal_frame_pacing = (s_throttler_enabled && g_settings.display_optimal_frame_pacing);
s_skip_presenting_duplicate_frames = s_throttler_enabled && g_settings.display_skip_presenting_duplicate_frames;
s_pre_frame_sleep = s_optimal_frame_pacing && g_settings.display_pre_frame_sleep;
s_can_sync_to_host = false;
s_syncing_to_host = false;
s_syncing_to_host_with_vsync = false;
2024-05-23 15:59:35 +00:00
if (g_settings.sync_to_host_refresh_rate)
{
const float host_refresh_rate = g_gpu_device->GetWindowInfo().surface_refresh_rate;
if (host_refresh_rate > 0.0f)
{
const float ratio = host_refresh_rate / System::GetThrottleFrequency();
s_can_sync_to_host = (ratio >= 0.95f && ratio <= 1.05f);
INFO_LOG("Refresh rate: Host={}hz Guest={}hz Ratio={} - {}", host_refresh_rate, System::GetThrottleFrequency(),
ratio, s_can_sync_to_host ? "can sync" : "can't sync");
s_syncing_to_host = (s_can_sync_to_host && g_settings.sync_to_host_refresh_rate && s_target_speed == 1.0f);
if (s_syncing_to_host)
2024-05-23 15:59:35 +00:00
{
s_target_speed = ratio;
// When syncing to host and using vsync, we don't need to sleep.
s_syncing_to_host_with_vsync = g_settings.display_vsync;
if (s_syncing_to_host_with_vsync)
{
INFO_LOG("Using host vsync for throttling.");
s_throttler_enabled = false;
}
2024-05-23 15:59:35 +00:00
}
}
}
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Target speed: {}%", s_target_speed * 100.0f);
2024-05-23 15:59:35 +00:00
VERBOSE_LOG("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate");
// Update audio output.
AudioStream* stream = SPU::GetOutputStream();
stream->SetOutputVolume(GetAudioOutputVolume());
stream->SetNominalRate(GetAudioNominalRate());
UpdateThrottlePeriod();
ResetThrottler();
2024-05-23 15:59:35 +00:00
UpdateDisplayVSync();
if (g_settings.increase_timer_resolution)
SetTimerResolutionIncreased(s_throttler_enabled);
}
2024-05-23 15:59:35 +00:00
void System::UpdateDisplayVSync()
{
2024-05-23 15:59:35 +00:00
static constexpr std::array<const char*, static_cast<size_t>(GPUVSyncMode::Count)> vsync_modes = {{
"Disabled",
"FIFO",
"Mailbox",
2024-05-23 15:59:35 +00:00
}};
// Avoid flipping vsync on and off by manually throttling when vsync is on.
const GPUVSyncMode vsync_mode = GetEffectiveVSyncMode();
const bool allow_present_throttle = ShouldAllowPresentThrottle();
VERBOSE_LOG("VSync: {}{}{}", vsync_modes[static_cast<size_t>(vsync_mode)],
s_syncing_to_host_with_vsync ? " (for throttling)" : "",
allow_present_throttle ? " (present throttle allowed)" : "");
2024-05-23 15:59:35 +00:00
g_gpu_device->SetVSyncMode(vsync_mode, allow_present_throttle);
}
2024-05-23 15:59:35 +00:00
GPUVSyncMode System::GetEffectiveVSyncMode()
{
2024-05-23 15:59:35 +00:00
// Vsync off => always disabled.
if (!g_settings.display_vsync)
return GPUVSyncMode::Disabled;
// If there's no VM, or we're using vsync for timing, then we always use double-buffered (blocking).
// Try to keep the same present mode whether we're running or not, since it'll avoid flicker.
const bool valid_vm = (s_state != State::Shutdown && s_state != State::Stopping);
if (s_can_sync_to_host || (!valid_vm && g_settings.sync_to_host_refresh_rate) ||
g_settings.display_disable_mailbox_presentation)
{
return GPUVSyncMode::FIFO;
}
2024-05-23 15:59:35 +00:00
// For PAL games, we always want to triple buffer, because otherwise we'll be tearing.
// Or for when we aren't using sync-to-host-refresh, to avoid dropping frames.
// Allow present skipping when running outside of normal speed, if mailbox isn't supported.
return GPUVSyncMode::Mailbox;
}
bool System::ShouldAllowPresentThrottle()
{
const bool valid_vm = (s_state != State::Shutdown && s_state != State::Stopping);
2024-05-27 01:46:18 +00:00
return !valid_vm || IsRunningAtNonStandardSpeed();
}
bool System::IsFastForwardEnabled()
{
return s_fast_forward_enabled;
}
void System::SetFastForwardEnabled(bool enabled)
{
if (!IsValid())
return;
s_fast_forward_enabled = enabled;
UpdateSpeedLimiterState();
}
bool System::IsTurboEnabled()
{
return s_turbo_enabled;
}
void System::SetTurboEnabled(bool enabled)
{
if (!IsValid())
return;
s_turbo_enabled = enabled;
UpdateSpeedLimiterState();
}
void System::SetRewindState(bool enabled)
{
if (!System::IsValid())
return;
if (!g_settings.rewind_enable)
{
if (enabled)
Host::AddKeyedOSDMessage("SetRewindState", TRANSLATE_STR("OSDMessage", "Rewinding is not enabled."), 5.0f);
return;
}
if (Achievements::IsHardcoreModeActive() && enabled)
{
Achievements::ConfirmHardcoreModeDisableAsync("Rewinding", [](bool approved) {
if (approved)
SetRewindState(true);
});
return;
}
System::SetRewinding(enabled);
UpdateSpeedLimiterState();
}
void System::DoFrameStep()
{
if (!IsValid())
return;
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync("Frame stepping", [](bool approved) {
if (approved)
DoFrameStep();
});
return;
}
s_frame_step_request = true;
PauseSystem(false);
}
void System::DoToggleCheats()
{
if (!System::IsValid())
return;
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync("Toggling cheats", [](bool approved) { DoToggleCheats(); });
return;
}
CheatList* cl = GetCheatList();
if (!cl)
{
Host::AddKeyedOSDMessage("ToggleCheats", TRANSLATE_STR("OSDMessage", "No cheats are loaded."), 10.0f);
return;
}
cl->SetMasterEnable(!cl->GetMasterEnable());
Host::AddIconOSDMessage(
"ToggleCheats", ICON_FA_EXCLAMATION_TRIANGLE,
cl->GetMasterEnable() ?
TRANSLATE_PLURAL_STR("System", "%n cheat(s) are now active.", "", cl->GetEnabledCodeCount()) :
TRANSLATE_PLURAL_STR("System", "%n cheat(s) are now inactive.", "", cl->GetEnabledCodeCount()),
Host::OSD_QUICK_DURATION);
}
static bool LoadEXEToRAM(const char* filename, bool patch_bios)
{
std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
if (!fp)
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to open exe file '{}'", filename);
return false;
}
2019-12-04 11:12:50 +00:00
std::fseek(fp, 0, SEEK_END);
const u32 file_size = static_cast<u32>(std::ftell(fp));
std::fseek(fp, 0, SEEK_SET);
BIOS::PSEXEHeader header;
if (std::fread(&header, sizeof(header), 1, fp) != 1 || !BIOS::IsValidPSExeHeader(header, file_size))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("'{}' is not a valid PS-EXE", filename);
std::fclose(fp);
return false;
}
if (header.memfill_size > 0)
{
const u32 words_to_write = header.memfill_size / 4;
u32 address = header.memfill_start & ~UINT32_C(3);
for (u32 i = 0; i < words_to_write; i++)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::SafeWriteMemoryWord(address, 0);
address += sizeof(u32);
}
}
const u32 file_data_size = std::min<u32>(file_size - sizeof(BIOS::PSEXEHeader), header.file_size);
if (file_data_size >= 4)
{
std::vector<u32> data_words((file_data_size + 3) / 4);
if (std::fread(data_words.data(), file_data_size, 1, fp) != 1)
{
std::fclose(fp);
return false;
}
const u32 num_words = file_data_size / 4;
u32 address = header.load_address;
for (u32 i = 0; i < num_words; i++)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::SafeWriteMemoryWord(address, data_words[i]);
address += sizeof(u32);
}
}
std::fclose(fp);
// patch the BIOS to jump to the executable directly
const u32 r_pc = header.initial_pc;
const u32 r_gp = header.initial_gp;
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
return BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp);
}
2019-09-14 10:28:47 +00:00
bool System::LoadEXE(const char* filename)
{
2022-07-08 11:57:06 +00:00
const std::string libps_path(Path::BuildRelativePath(filename, "libps.exe"));
if (!libps_path.empty() && FileSystem::FileExists(libps_path.c_str()) && !LoadEXEToRAM(libps_path.c_str(), false))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to load libps.exe from '{}'", libps_path.c_str());
return false;
}
return LoadEXEToRAM(filename, true);
}
bool System::InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_bios)
{
const u8* buffer_ptr = static_cast<const u8*>(buffer);
const u8* buffer_end = static_cast<const u8*>(buffer) + buffer_size;
BIOS::PSEXEHeader header;
if (buffer_size < sizeof(header))
return false;
std::memcpy(&header, buffer_ptr, sizeof(header));
buffer_ptr += sizeof(header);
const u32 file_size = static_cast<u32>(static_cast<u32>(buffer_end - buffer_ptr));
if (!BIOS::IsValidPSExeHeader(header, file_size))
return false;
if (header.memfill_size > 0)
{
const u32 words_to_write = header.memfill_size / 4;
u32 address = header.memfill_start & ~UINT32_C(3);
for (u32 i = 0; i < words_to_write; i++)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::SafeWriteMemoryWord(address, 0);
address += sizeof(u32);
}
}
const u32 file_data_size = std::min<u32>(file_size - sizeof(BIOS::PSEXEHeader), header.file_size);
if (file_data_size >= 4)
{
std::vector<u32> data_words((file_data_size + 3) / 4);
if ((buffer_end - buffer_ptr) < file_data_size)
return false;
std::memcpy(data_words.data(), buffer_ptr, file_data_size);
const u32 num_words = file_data_size / 4;
u32 address = header.load_address;
for (u32 i = 0; i < num_words; i++)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
CPU::SafeWriteMemoryWord(address, data_words[i]);
address += sizeof(u32);
}
}
// patch the BIOS to jump to the executable directly
if (patch_bios)
{
const u32 r_pc = header.initial_pc;
const u32 r_gp = header.initial_gp;
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
if (!BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp))
return false;
}
return true;
}
2021-12-01 09:27:51 +00:00
#if 0
// currently not used until EXP1 is implemented
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
bool SetExpansionROM(const char* filename)
2019-09-22 15:28:00 +00:00
{
std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
2019-09-22 15:28:00 +00:00
if (!fp)
{
2024-05-24 11:57:51 +00:00
ERROR_LOG("Failed to open '{}'", Path::GetFileName(filename));
2019-09-22 15:28:00 +00:00
return false;
}
std::fseek(fp, 0, SEEK_END);
const u32 size = static_cast<u32>(std::ftell(fp));
std::fseek(fp, 0, SEEK_SET);
std::vector<u8> data(size);
if (std::fread(data.data(), size, 1, fp) != 1)
{
2024-05-24 11:57:51 +00:00
ERROR_LOG("Failed to read ROM data from '{}'", Path::GetFileName(filename))
2019-09-22 15:28:00 +00:00
std::fclose(fp);
return false;
}
std::fclose(fp);
2024-05-24 11:57:51 +00:00
INFO_LOG("Loaded expansion ROM from '{}': {} bytes", Path::GetFileName(filename), size);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
Bus::SetExpansionROM(std::move(data));
2019-09-22 15:28:00 +00:00
return true;
}
#endif
Controller* System::GetController(u32 slot)
{
2023-01-11 09:10:21 +00:00
return Pad::GetController(slot);
}
void System::UpdateControllers()
{
auto lock = Host::GetSettingsLock();
2020-01-10 03:31:12 +00:00
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
2023-01-11 09:10:21 +00:00
Pad::SetController(i, nullptr);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
const ControllerType type = g_settings.controller_types[i];
2020-01-10 03:31:12 +00:00
if (type != ControllerType::None)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
std::unique_ptr<Controller> controller = Controller::Create(type, i);
if (controller)
{
controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str());
2023-01-11 09:10:21 +00:00
Pad::SetController(i, std::move(controller));
}
}
}
2019-09-29 15:07:38 +00:00
}
void System::UpdateControllerSettings()
{
auto lock = Host::GetSettingsLock();
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
2023-01-11 09:10:21 +00:00
Controller* controller = Pad::GetController(i);
if (controller)
controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str());
}
}
void System::ResetControllers()
{
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
2023-01-11 09:10:21 +00:00
Controller* controller = Pad::GetController(i);
if (controller)
controller->Reset();
}
}
std::unique_ptr<MemoryCard> System::GetMemoryCardForSlot(u32 slot, MemoryCardType type)
2019-09-29 15:07:38 +00:00
{
// Disable memory cards when running PSFs.
const bool is_running_psf = !s_running_game_path.empty() && IsPsfFileName(s_running_game_path.c_str());
if (is_running_psf)
return nullptr;
std::string message_key = fmt::format("MemoryCard{}SharedWarning", slot);
switch (type)
2019-10-27 06:45:23 +00:00
{
case MemoryCardType::PerGame:
2020-04-27 06:15:38 +00:00
{
2022-10-05 08:29:08 +00:00
if (s_running_game_serial.empty())
2020-04-27 06:15:38 +00:00
{
Host::AddIconOSDMessage(
std::move(message_key), ICON_FA_SD_CARD,
fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
"game has no code. Using shared card instead."),
slot + 1u),
Host::OSD_INFO_DURATION);
return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
2020-04-27 06:15:38 +00:00
}
else
{
Host::RemoveKeyedOSDMessage(std::move(message_key));
2022-10-05 08:29:08 +00:00
return MemoryCard::Open(g_settings.GetGameMemoryCardPath(s_running_game_serial.c_str(), slot));
}
}
case MemoryCardType::PerGameTitle:
{
if (s_running_game_title.empty())
{
Host::AddIconOSDMessage(
std::move(message_key), ICON_FA_SD_CARD,
fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
"game has no title. Using shared card instead."),
slot + 1u),
Host::OSD_INFO_DURATION);
return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
}
else
2020-04-27 06:15:38 +00:00
{
std::string card_path;
// Playlist - use title if different.
if (HasMediaSubImages() && s_running_game_entry && s_running_game_title != s_running_game_entry->title)
{
card_path = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(s_running_game_title), slot);
}
// Multi-disc game - use disc set name.
else if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty())
{
card_path =
g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(s_running_game_entry->disc_set_name), slot);
}
// But prefer a disc-specific card if one already exists.
std::string disc_card_path = g_settings.GetGameMemoryCardPath(
Path::SanitizeFileName((s_running_game_entry && !s_running_game_custom_title) ? s_running_game_entry->title :
s_running_game_title),
slot);
if (disc_card_path != card_path)
{
if (card_path.empty() || !g_settings.memory_card_use_playlist_title ||
FileSystem::FileExists(disc_card_path.c_str()))
{
if (g_settings.memory_card_use_playlist_title && !card_path.empty())
{
Host::AddIconOSDMessage(
fmt::format("DiscSpecificMC{}", slot), ICON_FA_SD_CARD,
fmt::format(TRANSLATE_FS("System", "Using disc-specific memory card '{}' instead of per-game card."),
Path::GetFileName(disc_card_path)),
Host::OSD_INFO_DURATION);
}
card_path = std::move(disc_card_path);
}
}
Host::RemoveKeyedOSDMessage(std::move(message_key));
return MemoryCard::Open(card_path.c_str());
2020-04-27 06:15:38 +00:00
}
}
case MemoryCardType::PerGameFileTitle:
{
const std::string display_name(FileSystem::GetDisplayNameFromPath(s_running_game_path));
2022-07-08 11:57:06 +00:00
const std::string_view file_title(Path::GetFileTitle(display_name));
if (file_title.empty())
{
Host::AddIconOSDMessage(
std::move(message_key), ICON_FA_SD_CARD,
fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
"game has no path. Using shared card instead."),
slot + 1u));
return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
}
else
{
Host::RemoveKeyedOSDMessage(std::move(message_key));
return MemoryCard::Open(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(file_title).c_str(), slot));
}
}
case MemoryCardType::Shared:
{
Host::RemoveKeyedOSDMessage(std::move(message_key));
return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
2020-04-27 06:15:38 +00:00
}
case MemoryCardType::NonPersistent:
{
Host::RemoveKeyedOSDMessage(std::move(message_key));
return MemoryCard::Create();
}
case MemoryCardType::None:
default:
{
Host::RemoveKeyedOSDMessage(std::move(message_key));
return nullptr;
}
}
}
void System::UpdateMemoryCardTypes()
{
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
2023-01-11 09:10:21 +00:00
Pad::SetMemoryCard(i, nullptr);
const MemoryCardType type = g_settings.memory_card_types[i];
std::unique_ptr<MemoryCard> card = GetMemoryCardForSlot(i, type);
if (card)
2023-10-17 03:08:44 +00:00
{
if (const std::string& filename = card->GetFilename(); !filename.empty())
2024-05-23 10:55:28 +00:00
INFO_LOG("Memory Card Slot {}: {}", i + 1, filename);
2023-10-17 03:08:44 +00:00
2023-01-11 09:10:21 +00:00
Pad::SetMemoryCard(i, std::move(card));
2023-10-17 03:08:44 +00:00
}
}
}
void System::UpdatePerGameMemoryCards()
{
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
const MemoryCardType type = g_settings.memory_card_types[i];
if (!Settings::IsPerGameMemoryCardType(type))
continue;
2023-01-11 09:10:21 +00:00
Pad::SetMemoryCard(i, nullptr);
std::unique_ptr<MemoryCard> card = GetMemoryCardForSlot(i, type);
2019-10-27 06:45:23 +00:00
if (card)
2023-10-17 03:08:44 +00:00
{
if (const std::string& filename = card->GetFilename(); !filename.empty())
2024-05-23 10:55:28 +00:00
INFO_LOG("Memory Card Slot {}: {}", i + 1, filename);
2023-10-17 03:08:44 +00:00
2023-01-11 09:10:21 +00:00
Pad::SetMemoryCard(i, std::move(card));
2023-10-17 03:08:44 +00:00
}
2019-10-27 06:45:23 +00:00
}
}
2019-09-20 10:14:00 +00:00
bool System::HasMemoryCard(u32 slot)
{
2023-01-11 09:10:21 +00:00
return (Pad::GetMemoryCard(slot) != nullptr);
}
bool System::IsSavingMemoryCards()
{
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
MemoryCard* card = Pad::GetMemoryCard(i);
if (card && card->IsOrWasRecentlyWriting())
return true;
}
return false;
}
void System::SwapMemoryCards()
{
if (!IsValid())
return;
2023-01-11 09:10:21 +00:00
std::unique_ptr<MemoryCard> first = Pad::RemoveMemoryCard(0);
std::unique_ptr<MemoryCard> second = Pad::RemoveMemoryCard(1);
Pad::SetMemoryCard(0, std::move(second));
Pad::SetMemoryCard(1, std::move(first));
if (HasMemoryCard(0) && HasMemoryCard(1))
{
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Both ports have a memory card."),
10.0f);
}
else if (HasMemoryCard(1))
{
Host::AddOSDMessage(
TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Port 2 has a memory card, Port 1 is empty."), 10.0f);
}
else if (HasMemoryCard(0))
{
Host::AddOSDMessage(
TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Port 1 has a memory card, Port 2 is empty."), 10.0f);
}
else
{
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Neither port has a memory card."),
10.0f);
}
}
void System::UpdateMultitaps()
2021-01-21 07:59:40 +00:00
{
switch (g_settings.multitap_mode)
{
case MultitapMode::Disabled:
{
2023-01-11 09:10:21 +00:00
Pad::GetMultitap(0)->SetEnable(false, 0);
Pad::GetMultitap(1)->SetEnable(false, 0);
2021-01-21 07:59:40 +00:00
}
break;
case MultitapMode::Port1Only:
{
2023-01-11 09:10:21 +00:00
Pad::GetMultitap(0)->SetEnable(true, 0);
Pad::GetMultitap(1)->SetEnable(false, 0);
}
break;
case MultitapMode::Port2Only:
{
2023-01-11 09:10:21 +00:00
Pad::GetMultitap(0)->SetEnable(false, 0);
Pad::GetMultitap(1)->SetEnable(true, 1);
2021-01-21 07:59:40 +00:00
}
break;
case MultitapMode::BothPorts:
{
2023-01-11 09:10:21 +00:00
Pad::GetMultitap(0)->SetEnable(true, 0);
Pad::GetMultitap(1)->SetEnable(true, 4);
2021-01-21 07:59:40 +00:00
}
break;
2023-09-03 04:30:26 +00:00
default:
UnreachableCode();
break;
2021-01-21 07:59:40 +00:00
}
}
bool System::DumpRAM(const char* filename)
{
2021-01-13 09:24:41 +00:00
if (!IsValid())
return false;
return FileSystem::WriteBinaryFile(filename, Bus::g_unprotected_ram, Bus::g_ram_size);
2021-01-13 09:24:41 +00:00
}
bool System::DumpVRAM(const char* filename)
2021-01-13 09:24:41 +00:00
{
if (!IsValid())
return false;
g_gpu->RestoreDeviceContext();
return g_gpu->DumpVRAMToFile(filename);
2021-01-13 09:24:41 +00:00
}
bool System::DumpSPURAM(const char* filename)
2021-01-13 09:24:41 +00:00
{
if (!IsValid())
return false;
2022-08-01 13:39:41 +00:00
return FileSystem::WriteBinaryFile(filename, SPU::GetRAM().data(), SPU::RAM_SIZE);
}
bool System::HasMedia()
2019-09-20 10:14:00 +00:00
{
2023-01-09 09:34:40 +00:00
return CDROM::HasMedia();
2019-09-20 10:14:00 +00:00
}
std::string System::GetMediaFileName()
{
2023-01-09 09:34:40 +00:00
if (!CDROM::HasMedia())
return {};
2023-01-09 09:34:40 +00:00
return CDROM::GetMediaFileName();
}
bool System::InsertMedia(const char* path)
2019-09-20 10:14:00 +00:00
{
2023-08-19 13:40:36 +00:00
Error error;
std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error);
if (!image)
{
Host::AddIconOSDMessage(
"DiscInserted", ICON_FA_COMPACT_DISC,
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open disc image '{}': {}."), path, error.GetDescription()),
Host::OSD_ERROR_DURATION);
return false;
}
const DiscRegion region = GameList::GetCustomRegionForPath(path).value_or(GetRegionForImage(image.get()));
UpdateRunningGame(path, image.get(), false);
CDROM::InsertMedia(std::move(image), region);
2024-05-23 10:55:28 +00:00
INFO_LOG("Inserted media from {} ({}, {})", s_running_game_path, s_running_game_serial, s_running_game_title);
if (g_settings.cdrom_load_image_to_ram)
2023-01-09 09:34:40 +00:00
CDROM::PrecacheMedia();
Host::AddIconOSDMessage(
"DiscInserted", ICON_FA_COMPACT_DISC,
fmt::format(TRANSLATE_FS("OSDMessage", "Inserted disc '{}' ({})."), s_running_game_title, s_running_game_serial),
Host::OSD_INFO_DURATION);
2020-04-27 06:15:38 +00:00
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (g_settings.HasAnyPerGameMemoryCards())
2020-04-27 06:15:38 +00:00
{
Host::AddIconOSDMessage("ReloadMemoryCardsFromGameChange", ICON_FA_SD_CARD,
TRANSLATE_STR("System", "Game changed, reloading memory cards."), Host::OSD_INFO_DURATION);
UpdatePerGameMemoryCards();
2020-04-27 06:15:38 +00:00
}
ClearMemorySaveStates();
return true;
2019-09-20 10:14:00 +00:00
}
void System::RemoveMedia()
{
2023-01-09 09:34:40 +00:00
CDROM::RemoveMedia(false);
ClearMemorySaveStates();
}
void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
{
if (!booting && s_running_game_path == path)
return;
2023-08-23 12:06:48 +00:00
const std::string prev_serial = std::move(s_running_game_serial);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_running_game_path.clear();
2023-08-23 12:06:48 +00:00
s_running_game_serial = {};
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_running_game_title.clear();
s_running_game_entry = nullptr;
s_running_game_hash = 0;
s_running_game_custom_title = false;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (path && std::strlen(path) > 0)
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
s_running_game_path = path;
s_running_game_title = GameList::GetCustomTitleForPath(s_running_game_path);
s_running_game_custom_title = !s_running_game_title.empty();
if (IsExeFileName(path))
{
if (s_running_game_title.empty())
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
s_running_game_hash = GetGameHashFromFile(path);
if (s_running_game_hash != 0)
s_running_game_serial = GetGameHashId(s_running_game_hash);
}
else if (IsPsfFileName(path))
{
// TODO: We could pull the title from the PSF.
if (s_running_game_title.empty())
s_running_game_title = Path::GetFileTitle(path);
}
// Check for an audio CD. Those shouldn't set any title.
else if (image && image->GetTrack(1).mode != CDImage::TrackMode::Audio)
{
std::string id;
GetGameDetailsFromImage(image, &id, &s_running_game_hash);
s_running_game_entry = GameDatabase::GetEntryForGameDetails(id, s_running_game_hash);
if (s_running_game_entry)
{
s_running_game_serial = s_running_game_entry->serial;
if (s_running_game_title.empty())
s_running_game_title = s_running_game_entry->title;
}
else
{
s_running_game_serial = std::move(id);
if (s_running_game_title.empty())
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
}
if (image->HasSubImages())
{
std::string image_title = image->GetMetadata("title");
if (!image_title.empty())
{
s_running_game_title = std::move(image_title);
s_running_game_custom_title = false;
}
}
}
}
if (!booting)
2024-06-02 14:45:36 +00:00
TextureReplacements::SetGameID(s_running_game_serial);
if (booting)
Achievements::ResetHardcoreMode(true);
Achievements::GameChanged(s_running_game_path, image);
2022-10-08 10:59:18 +00:00
UpdateGameSettingsLayer();
ApplySettings(true);
s_cheat_list.reset();
if (g_settings.enable_cheats)
LoadCheatList();
2022-10-08 10:59:18 +00:00
2023-08-23 12:06:48 +00:00
if (s_running_game_serial != prev_serial)
UpdateSessionTime(prev_serial);
if (SaveStateSelectorUI::IsOpen())
SaveStateSelectorUI::RefreshList(s_running_game_serial);
else
SaveStateSelectorUI::ClearList();
2023-08-23 12:06:48 +00:00
2024-07-04 04:40:16 +00:00
UpdateRichPresence(booting);
2023-08-23 12:06:48 +00:00
2022-10-08 10:59:18 +00:00
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
}
bool System::CheckForSBIFile(CDImage* image, Error* error)
{
if (!s_running_game_entry || !s_running_game_entry->HasTrait(GameDatabase::Trait::IsLibCryptProtected) || !image ||
image->HasNonStandardSubchannel())
{
return true;
}
2024-05-23 10:55:28 +00:00
WARNING_LOG("SBI file missing but required for {} ({})", s_running_game_serial, s_running_game_title);
if (Host::GetBoolSettingValue("CDROM", "AllowBootingWithoutSBIFile", false))
{
if (Host::ConfirmMessage(
"Confirm Unsupported Configuration",
LargeString::from_format(
TRANSLATE_FS("System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n{0}: "
"{1}\n\nThe game will likely not run properly.\n\nPlease check the README for "
"instructions on how to add an SBI file.\n\nDo you wish to continue?"),
s_running_game_serial, s_running_game_title)))
{
return true;
}
}
#ifndef __ANDROID__
Error::SetStringFmt(
error,
TRANSLATE_FS("System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n{0}: "
"{1}\n\nYour dump is incomplete, you must add the SBI file to run this game. \n\nThe "
"name of the SBI file must match the name of the disc image."),
s_running_game_serial, s_running_game_title);
#else
// Shorter because no confirm messages.
Error::SetStringView(error, "Missing SBI file.", "The selected game requires a SBI file to run properly.");
#endif
return false;
}
bool System::HasMediaSubImages()
{
2023-01-09 09:34:40 +00:00
const CDImage* cdi = CDROM::GetMedia();
return cdi ? cdi->HasSubImages() : false;
}
u32 System::GetMediaSubImageCount()
{
2023-01-09 09:34:40 +00:00
const CDImage* cdi = CDROM::GetMedia();
return cdi ? cdi->GetSubImageCount() : 0;
}
u32 System::GetMediaSubImageIndex()
{
2023-01-09 09:34:40 +00:00
const CDImage* cdi = CDROM::GetMedia();
return cdi ? cdi->GetCurrentSubImage() : 0;
}
2020-07-22 16:36:05 +00:00
2024-05-05 10:21:54 +00:00
u32 System::GetMediaSubImageIndexForTitle(std::string_view title)
2020-07-22 16:36:05 +00:00
{
2023-01-09 09:34:40 +00:00
const CDImage* cdi = CDROM::GetMedia();
if (!cdi)
return 0;
2021-02-26 14:44:53 +00:00
const u32 count = cdi->GetSubImageCount();
for (u32 i = 0; i < count; i++)
2020-07-22 16:36:05 +00:00
{
if (title == cdi->GetSubImageMetadata(i, "title"))
2020-07-22 16:36:05 +00:00
return i;
}
return std::numeric_limits<u32>::max();
}
std::string System::GetMediaSubImageTitle(u32 index)
2020-07-22 16:36:05 +00:00
{
2023-01-09 09:34:40 +00:00
const CDImage* cdi = CDROM::GetMedia();
if (!cdi)
return {};
2020-07-22 16:36:05 +00:00
return cdi->GetSubImageMetadata(index, "title");
2020-07-22 16:36:05 +00:00
}
bool System::SwitchMediaSubImage(u32 index)
2020-07-22 16:36:05 +00:00
{
2023-01-09 09:34:40 +00:00
if (!CDROM::HasMedia())
2020-07-22 16:36:05 +00:00
return false;
2023-01-09 09:34:40 +00:00
std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true);
Assert(image);
2020-07-22 16:36:05 +00:00
2023-08-19 13:40:36 +00:00
Error error;
if (!image->SwitchSubImage(index, &error))
2020-07-22 16:36:05 +00:00
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage("media_switch_subimage", ICON_FA_COMPACT_DISC,
fmt::format(TRANSLATE_FS("System", "Failed to switch to subimage {} in '{}': {}."),
index + 1u, Path::GetFileName(image->GetFileName()), error.GetDescription()),
Host::OSD_INFO_DURATION);
const DiscRegion region = GetRegionForImage(image.get());
CDROM::InsertMedia(std::move(image), region);
return false;
2020-07-22 16:36:05 +00:00
}
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage("media_switch_subimage", ICON_FA_COMPACT_DISC,
fmt::format(TRANSLATE_FS("System", "Switched to sub-image {} ({}) in '{}'."),
image->GetSubImageMetadata(index, "title"), index + 1u,
image->GetMetadata("title")),
Host::OSD_INFO_DURATION);
const DiscRegion region = GetRegionForImage(image.get());
CDROM::InsertMedia(std::move(image), region);
2020-07-22 16:36:05 +00:00
ClearMemorySaveStates();
return true;
2020-07-22 16:36:05 +00:00
}
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
bool System::HasCheatList()
{
return static_cast<bool>(s_cheat_list);
}
CheatList* System::GetCheatList()
{
return s_cheat_list.get();
}
void System::ApplyCheatCode(const CheatCode& code)
{
Assert(!IsShutdown());
code.Apply();
}
void System::SetCheatList(std::unique_ptr<CheatList> cheats)
{
Assert(!IsShutdown());
s_cheat_list = std::move(cheats);
if (s_cheat_list && s_cheat_list->GetEnabledCodeCount() > 0)
{
Host::AddIconOSDMessage("CheatsLoadWarning", ICON_FA_EXCLAMATION_TRIANGLE,
TRANSLATE_PLURAL_STR("System", "%n cheat(s) are enabled. This may crash games.", "",
s_cheat_list->GetEnabledCodeCount()),
Host::OSD_WARNING_DURATION);
}
else
{
Host::RemoveKeyedOSDMessage("CheatsLoadWarning");
}
}
void System::CheckForSettingsChanges(const Settings& old_settings)
{
if (IsValid() &&
(g_settings.gpu_renderer != old_settings.gpu_renderer ||
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation ||
g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache ||
g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend ||
g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch ||
g_settings.gpu_disable_texture_buffers != old_settings.gpu_disable_texture_buffers ||
g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self ||
g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import ||
g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control))
{
// if debug device/threaded presentation change, we need to recreate the whole display
const bool recreate_device =
(g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation ||
g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache ||
g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend ||
g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch ||
g_settings.gpu_disable_texture_buffers != old_settings.gpu_disable_texture_buffers ||
g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self ||
g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import ||
g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control);
Host::AddIconOSDMessage("RendererSwitch", ICON_FA_PAINT_ROLLER,
fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {}{} GPU renderer."),
Settings::GetRendererName(g_settings.gpu_renderer),
g_settings.gpu_use_debug_device ? " (debug)" : ""),
Host::OSD_INFO_DURATION);
RecreateGPU(g_settings.gpu_renderer, recreate_device);
}
if (IsValid())
{
ClearMemorySaveStates();
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
(g_settings.cpu_overclock_active &&
(g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator ||
g_settings.cpu_overclock_denominator != old_settings.cpu_overclock_denominator)))
{
UpdateOverclock();
}
2022-12-14 07:58:14 +00:00
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.audio_driver != old_settings.audio_driver ||
g_settings.audio_output_device != old_settings.audio_output_device)
{
if (g_settings.audio_backend != old_settings.audio_backend)
{
Host::AddIconOSDMessage("AudioBackendSwitch", ICON_FA_HEADPHONES,
fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} audio backend."),
AudioStream::GetBackendDisplayName(g_settings.audio_backend)),
Host::OSD_INFO_DURATION);
}
2022-08-01 13:39:41 +00:00
SPU::RecreateOutputStream();
2022-07-27 14:42:41 +00:00
}
if (g_settings.audio_stream_parameters.stretch_mode != old_settings.audio_stream_parameters.stretch_mode)
SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stream_parameters.stretch_mode);
if (g_settings.audio_stream_parameters != old_settings.audio_stream_parameters)
2022-07-27 14:42:41 +00:00
{
2022-08-01 13:39:41 +00:00
SPU::RecreateOutputStream();
2022-07-27 14:42:41 +00:00
UpdateSpeedLimiterState();
}
if (g_settings.emulation_speed != old_settings.emulation_speed)
UpdateThrottlePeriod();
if (g_settings.cpu_execution_mode != old_settings.cpu_execution_mode ||
g_settings.cpu_fastmem_mode != old_settings.cpu_fastmem_mode)
{
Host::AddIconOSDMessage("CPUExecutionModeSwitch", ICON_FA_MICROCHIP,
fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} CPU execution mode."),
TRANSLATE_SV("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(
g_settings.cpu_execution_mode))),
Host::OSD_INFO_DURATION);
CPU::ExecutionModeChanged();
if (old_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
CPU::CodeCache::Shutdown();
if (g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
CPU::CodeCache::Initialize();
CPU::ClearICache();
}
2023-10-03 14:39:18 +00:00
if (CPU::CodeCache::IsUsingAnyRecompiler() &&
(g_settings.cpu_recompiler_memory_exceptions != old_settings.cpu_recompiler_memory_exceptions ||
g_settings.cpu_recompiler_block_linking != old_settings.cpu_recompiler_block_linking ||
g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache ||
g_settings.bios_tty_logging != old_settings.bios_tty_logging))
{
Host::AddIconOSDMessage("CPUFlushAllBlocks", ICON_FA_MICROCHIP,
TRANSLATE_STR("OSDMessage", "Recompiler options changed, flushing all blocks."),
Host::OSD_INFO_DURATION);
CPU::ExecutionModeChanged();
CPU::CodeCache::Reset();
if (g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache)
CPU::ClearICache();
}
else if (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter &&
g_settings.bios_tty_logging != old_settings.bios_tty_logging)
{
CPU::UpdateDebugDispatcherFlag();
}
if (g_settings.enable_cheats != old_settings.enable_cheats)
{
if (g_settings.enable_cheats)
LoadCheatList();
else
SetCheatList(nullptr);
}
2022-08-01 13:39:41 +00:00
SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
g_settings.gpu_multisamples != old_settings.gpu_multisamples ||
g_settings.gpu_per_sample_shading != old_settings.gpu_per_sample_shading ||
g_settings.gpu_use_thread != old_settings.gpu_use_thread ||
g_settings.gpu_use_software_renderer_for_readbacks != old_settings.gpu_use_software_renderer_for_readbacks ||
g_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
g_settings.gpu_true_color != old_settings.gpu_true_color ||
g_settings.gpu_debanding != old_settings.gpu_debanding ||
g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
g_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords ||
g_settings.gpu_texture_filter != old_settings.gpu_texture_filter ||
g_settings.gpu_sprite_texture_filter != old_settings.gpu_sprite_texture_filter ||
g_settings.gpu_line_detect_mode != old_settings.gpu_line_detect_mode ||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
2023-09-03 07:10:40 +00:00
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale ||
2023-09-02 12:26:03 +00:00
g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode ||
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
g_settings.display_24bit_chroma_smoothing != old_settings.display_24bit_chroma_smoothing ||
g_settings.display_crop_mode != old_settings.display_crop_mode ||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.display_alignment != old_settings.display_alignment ||
g_settings.display_scaling != old_settings.display_scaling ||
2024-01-21 09:37:29 +00:00
g_settings.display_show_gpu_usage != old_settings.display_show_gpu_usage ||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
g_settings.gpu_pgxp_texture_correction != old_settings.gpu_pgxp_texture_correction ||
g_settings.gpu_pgxp_color_correction != old_settings.gpu_pgxp_color_correction ||
g_settings.gpu_pgxp_depth_buffer != old_settings.gpu_pgxp_depth_buffer ||
g_settings.display_active_start_offset != old_settings.display_active_start_offset ||
g_settings.display_active_end_offset != old_settings.display_active_end_offset ||
g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
g_settings.display_line_end_offset != old_settings.display_line_end_offset ||
g_settings.rewind_enable != old_settings.rewind_enable ||
2022-09-03 04:15:15 +00:00
g_settings.runahead_frames != old_settings.runahead_frames)
{
g_gpu->UpdateSettings(old_settings);
if (IsPaused())
InvalidateDisplay();
}
if (g_settings.gpu_widescreen_hack != old_settings.gpu_widescreen_hack ||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
(g_settings.display_aspect_ratio == DisplayAspectRatio::Custom &&
(g_settings.display_aspect_ratio_custom_numerator != old_settings.display_aspect_ratio_custom_numerator ||
g_settings.display_aspect_ratio_custom_denominator != old_settings.display_aspect_ratio_custom_denominator)))
{
GTE::UpdateAspectRatio();
}
if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
(g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling ||
g_settings.gpu_pgxp_vertex_cache != old_settings.gpu_pgxp_vertex_cache ||
g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu)))
{
if (old_settings.gpu_pgxp_enable)
CPU::PGXP::Shutdown();
if (g_settings.gpu_pgxp_enable)
CPU::PGXP::Initialize();
CPU::CodeCache::Reset();
}
2024-01-21 09:37:29 +00:00
if (g_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats)
g_gpu->ResetStatistics();
if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors)
2023-01-09 09:34:40 +00:00
CDROM::SetReadaheadSectors(g_settings.cdrom_readahead_sectors);
if (g_settings.memory_card_types != old_settings.memory_card_types ||
g_settings.memory_card_paths != old_settings.memory_card_paths ||
(g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title))
{
UpdateMemoryCardTypes();
}
if (g_settings.rewind_enable != old_settings.rewind_enable ||
g_settings.rewind_save_frequency != old_settings.rewind_save_frequency ||
g_settings.rewind_save_slots != old_settings.rewind_save_slots ||
g_settings.runahead_frames != old_settings.runahead_frames)
{
UpdateMemorySaveStateSettings();
}
if (g_settings.texture_replacements.enable_vram_write_replacements !=
old_settings.texture_replacements.enable_vram_write_replacements ||
g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures)
{
2024-06-02 14:45:36 +00:00
TextureReplacements::Reload();
}
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
g_settings.emulation_speed != old_settings.emulation_speed ||
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing ||
g_settings.display_skip_presenting_duplicate_frames != old_settings.display_skip_presenting_duplicate_frames ||
g_settings.display_pre_frame_sleep != old_settings.display_pre_frame_sleep ||
g_settings.display_pre_frame_sleep_buffer != old_settings.display_pre_frame_sleep_buffer ||
g_settings.display_vsync != old_settings.display_vsync ||
g_settings.display_disable_mailbox_presentation != old_settings.display_disable_mailbox_presentation ||
g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
{
UpdateSpeedLimiterState();
}
2023-08-23 12:06:48 +00:00
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
{
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
else
PlatformMisc::ResumeScreensaver();
}
PostProcessing::UpdateSettings();
2024-05-26 14:10:39 +00:00
#ifdef ENABLE_GDB_SERVER
if (g_settings.debugging.enable_gdb_server != old_settings.debugging.enable_gdb_server ||
g_settings.debugging.gdb_server_port != old_settings.debugging.gdb_server_port)
{
GDBServer::Shutdown();
if (g_settings.debugging.enable_gdb_server)
GDBServer::Initialize(g_settings.debugging.gdb_server_port);
}
#endif
}
2024-05-23 15:59:35 +00:00
else
{
if (g_gpu_device)
{
if (g_settings.display_vsync != old_settings.display_vsync ||
g_settings.display_disable_mailbox_presentation != old_settings.display_disable_mailbox_presentation)
{
2024-05-23 15:59:35 +00:00
UpdateDisplayVSync();
}
2024-05-23 15:59:35 +00:00
}
}
if (g_gpu_device)
{
if (g_settings.display_osd_scale != old_settings.display_osd_scale)
ImGuiManager::SetGlobalScale(g_settings.display_osd_scale / 100.0f);
if (g_settings.display_show_osd_messages != old_settings.display_show_osd_messages)
ImGuiManager::SetShowOSDMessages(g_settings.display_show_osd_messages);
}
2023-08-27 06:00:06 +00:00
bool controllers_updated = false;
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
if (g_settings.controller_types[i] != old_settings.controller_types[i])
{
if (IsValid() && !controllers_updated)
{
UpdateControllers();
ResetControllers();
controllers_updated = true;
}
}
}
2023-09-20 06:56:12 +00:00
if (IsValid() && !controllers_updated)
UpdateControllerSettings();
if (g_settings.multitap_mode != old_settings.multitap_mode)
UpdateMultitaps();
2023-08-23 12:06:48 +00:00
Achievements::UpdateSettings(old_settings);
FullscreenUI::CheckForConfigChanges(old_settings);
2023-10-31 15:32:29 +00:00
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
if (g_settings.enable_discord_presence != old_settings.enable_discord_presence)
{
if (g_settings.enable_discord_presence)
InitializeDiscordPresence();
else
ShutdownDiscordPresence();
}
2023-10-31 15:32:29 +00:00
#endif
2023-08-23 12:06:48 +00:00
2024-05-25 13:49:19 +00:00
#ifdef ENABLE_PINE_SERVER
if (g_settings.pine_enable != old_settings.pine_enable || g_settings.pine_slot != old_settings.pine_slot)
{
PINEServer::Shutdown();
if (g_settings.pine_enable)
PINEServer::Initialize(g_settings.pine_slot);
else
ReleaseSocketMultiplexer();
}
#endif
2023-08-23 12:06:48 +00:00
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
2023-09-30 04:40:50 +00:00
g_settings.log_timestamps != old_settings.log_timestamps ||
2023-08-23 12:06:48 +00:00
g_settings.log_to_console != old_settings.log_to_console ||
g_settings.log_to_debug != old_settings.log_to_debug || g_settings.log_to_window != old_settings.log_to_window ||
g_settings.log_to_file != old_settings.log_to_file)
{
g_settings.UpdateLogSettings();
}
}
2023-12-14 07:24:45 +00:00
void System::WarnAboutUnsafeSettings()
{
LargeString messages;
auto append = [&messages](const char* icon, std::string_view msg) { messages.append_format("{} {}\n", icon, msg); };
2023-12-14 07:24:45 +00:00
if (!g_settings.disable_all_enhancements)
2023-12-14 07:24:45 +00:00
{
if (g_settings.cpu_overclock_active)
{
append(ICON_FA_MICROCHIP,
SmallString::from_format(
TRANSLATE_FS("System", "CPU clock speed is set to {}% ({} / {}). This may crash games."),
g_settings.GetCPUOverclockPercent(), g_settings.cpu_overclock_numerator,
g_settings.cpu_overclock_denominator));
}
if (g_settings.cdrom_read_speedup > 1)
{
append(ICON_FA_COMPACT_DISC,
SmallString::from_format(
TRANSLATE_FS("System", "CD-ROM read speedup set to {}x (effective speed {}x). This may crash games."),
g_settings.cdrom_read_speedup, g_settings.cdrom_read_speedup * 2));
}
if (g_settings.cdrom_seek_speedup != 1)
{
append(ICON_FA_COMPACT_DISC,
SmallString::from_format(TRANSLATE_FS("System", "CD-ROM seek speedup set to {}. This may crash games."),
(g_settings.cdrom_seek_speedup == 0) ?
TinyString(TRANSLATE_SV("System", "Instant")) :
TinyString::from_format("{}x", g_settings.cdrom_seek_speedup)));
}
if (g_settings.gpu_force_ntsc_timings)
{
append(ICON_FA_TV, TRANSLATE_SV("System", "Force NTSC timings is enabled. Games may run at incorrect speeds."));
}
if (!g_settings.IsUsingSoftwareRenderer())
{
if (g_settings.gpu_multisamples != 1)
{
append(ICON_FA_MAGIC,
TRANSLATE_SV("System", "Multisample anti-aliasing is enabled, some games may not render correctly."));
}
if (g_settings.gpu_resolution_scale > 1 && g_settings.gpu_force_round_texcoords)
{
append(
ICON_FA_MAGIC,
TRANSLATE_SV("System", "Round upscaled texture coordinates is enabled. This may cause rendering errors."));
}
}
if (g_settings.enable_8mb_ram)
append(ICON_FA_MICROCHIP,
TRANSLATE_SV("System", "8MB RAM is enabled, this may be incompatible with some games."));
2023-12-14 07:24:45 +00:00
}
else
2023-12-14 07:24:45 +00:00
{
append(ICON_FA_COGS, TRANSLATE_SV("System", "All enhancements are currently disabled."));
}
if (!g_settings.apply_compatibility_settings)
{
append(ICON_FA_GAMEPAD,
TRANSLATE_STR("System", "Compatibility settings are not enabled. Some games may not function correctly."));
}
2023-12-14 07:24:45 +00:00
if (!messages.empty())
{
if (messages.back() == '\n')
messages.pop_back();
LogUnsafeSettingsToConsole(messages);
Host::AddKeyedOSDMessage("performance_settings_warning", std::string(messages.view()), Host::OSD_WARNING_DURATION);
2023-12-14 07:24:45 +00:00
}
else
{
Host::RemoveKeyedOSDMessage("performance_settings_warning");
}
}
void System::LogUnsafeSettingsToConsole(const SmallStringBase& messages)
2023-12-14 07:24:45 +00:00
{
// a not-great way of getting rid of the icons for the console message
LargeString console_messages = messages;
2023-12-14 07:24:45 +00:00
for (;;)
{
const s32 pos = console_messages.find("\xef");
if (pos >= 0)
2023-12-14 07:24:45 +00:00
{
console_messages.erase(pos, 3);
2023-12-14 07:24:45 +00:00
console_messages.insert(pos, "[Unsafe Settings]");
}
else
{
break;
}
}
2024-05-23 10:55:28 +00:00
WARNING_LOG(console_messages);
2023-12-14 07:24:45 +00:00
}
void System::CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64* ram_usage, u64* vram_usage)
{
2024-03-01 03:51:16 +00:00
const u64 real_resolution_scale = std::max<u64>(g_settings.gpu_resolution_scale, 1u);
*ram_usage = MAX_SAVE_STATE_SIZE * static_cast<u64>(num_saves);
*vram_usage = ((VRAM_WIDTH * real_resolution_scale) * (VRAM_HEIGHT * real_resolution_scale) * 4) *
static_cast<u64>(g_settings.gpu_multisamples) * static_cast<u64>(num_saves);
}
void System::ClearMemorySaveStates()
{
s_rewind_states.clear();
2021-01-23 16:52:52 +00:00
s_runahead_states.clear();
}
void System::UpdateMemorySaveStateSettings()
{
ClearMemorySaveStates();
s_memory_saves_enabled = g_settings.rewind_enable;
if (g_settings.rewind_enable)
{
s_rewind_save_frequency = static_cast<s32>(std::ceil(g_settings.rewind_save_frequency * s_throttle_frequency));
s_rewind_save_counter = 0;
u64 ram_usage, vram_usage;
CalculateRewindMemoryUsage(g_settings.rewind_save_slots, g_settings.gpu_resolution_scale, &ram_usage, &vram_usage);
2024-05-23 10:55:28 +00:00
INFO_LOG("Rewind is enabled, saving every {} frames, with {} slots and {}MB RAM and {}MB VRAM usage",
std::max(s_rewind_save_frequency, 1), g_settings.rewind_save_slots, ram_usage / 1048576,
vram_usage / 1048576);
}
else
{
s_rewind_save_frequency = -1;
s_rewind_save_counter = -1;
}
2021-01-23 16:52:52 +00:00
s_rewind_load_frequency = -1;
s_rewind_load_counter = -1;
2021-01-25 16:48:40 +00:00
s_runahead_frames = g_settings.runahead_frames;
2021-01-23 16:52:52 +00:00
s_runahead_replay_pending = false;
if (s_runahead_frames > 0)
2024-05-23 10:55:28 +00:00
INFO_LOG("Runahead is active with {} frames", s_runahead_frames);
}
bool System::LoadMemoryState(const MemorySaveState& mss)
{
2021-01-23 16:52:52 +00:00
mss.state_stream->SeekAbsolute(0);
2021-01-23 16:52:52 +00:00
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
GPUTexture* host_texture = mss.vram_texture.get();
if (!DoState(sw, &host_texture, true, true))
2021-01-23 16:52:52 +00:00
{
Host::ReportErrorAsync("Error", "Failed to load memory save state, resetting.");
InternalReset();
2021-01-23 16:52:52 +00:00
return false;
}
2021-01-23 16:52:52 +00:00
return true;
}
bool System::SaveMemoryState(MemorySaveState* mss)
2021-01-23 16:52:52 +00:00
{
if (!mss->state_stream)
mss->state_stream = std::make_unique<GrowableMemoryByteStream>(nullptr, MAX_SAVE_STATE_SIZE);
else
mss->state_stream->SeekAbsolute(0);
GPUTexture* host_texture = mss->vram_texture.release();
2021-01-23 16:52:52 +00:00
StateWrapper sw(mss->state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
if (!DoState(sw, &host_texture, false, true))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to create rewind state.");
delete host_texture;
return false;
}
2021-01-23 16:52:52 +00:00
mss->vram_texture.reset(host_texture);
return true;
}
bool System::SaveRewindState()
2021-01-23 16:52:52 +00:00
{
#ifdef PROFILE_MEMORY_SAVE_STATES
2021-01-23 16:52:52 +00:00
Common::Timer save_timer;
#endif
2021-01-23 16:52:52 +00:00
// try to reuse the frontmost slot
2021-01-23 16:52:52 +00:00
const u32 save_slots = g_settings.rewind_save_slots;
MemorySaveState mss;
2021-01-23 16:52:52 +00:00
while (s_rewind_states.size() >= save_slots)
{
mss = std::move(s_rewind_states.front());
2021-01-23 16:52:52 +00:00
s_rewind_states.pop_front();
}
2021-01-23 16:52:52 +00:00
if (!SaveMemoryState(&mss))
return false;
s_rewind_states.push_back(std::move(mss));
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
DEV_LOG("Saved rewind state ({} bytes, took {:.4f} ms)", s_rewind_states.back().state_stream->GetSize(),
save_timer.GetTimeMilliseconds());
#endif
return true;
}
bool System::LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */)
{
while (skip_saves > 0 && !s_rewind_states.empty())
{
g_gpu_device->RecycleTexture(std::move(s_rewind_states.back().vram_texture));
s_rewind_states.pop_back();
skip_saves--;
}
if (s_rewind_states.empty())
return false;
#ifdef PROFILE_MEMORY_SAVE_STATES
Common::Timer load_timer;
#endif
2021-01-23 16:52:52 +00:00
if (!LoadMemoryState(s_rewind_states.back()))
return false;
if (consume_state)
s_rewind_states.pop_back();
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
DEV_LOG("Rewind load took {:.4f} ms", load_timer.GetTimeMilliseconds());
#endif
return true;
}
bool System::IsRewinding()
{
return (s_rewind_load_frequency >= 0);
}
void System::SetRewinding(bool enabled)
{
if (enabled)
{
const bool was_enabled = IsRewinding();
// Try to rewind at the replay speed, or one per second maximum.
const float load_frequency = std::min(g_settings.rewind_save_frequency, 1.0f);
s_rewind_load_frequency = static_cast<s32>(std::ceil(load_frequency * s_throttle_frequency));
s_rewind_load_counter = 0;
if (!was_enabled && s_system_executing)
s_system_interrupted = true;
}
else
{
s_rewind_load_frequency = -1;
s_rewind_load_counter = -1;
s_rewinding_first_save = true;
}
}
void System::DoRewind()
{
if (s_rewind_load_counter == 0)
{
const u32 skip_saves = BoolToUInt32(!s_rewinding_first_save);
s_rewinding_first_save = false;
LoadRewindState(skip_saves, false);
ResetPerformanceCounters();
s_rewind_load_counter = s_rewind_load_frequency;
}
else
{
s_rewind_load_counter--;
}
2021-07-17 10:09:29 +00:00
2023-08-30 10:34:48 +00:00
InvalidateDisplay();
Host::PumpMessagesOnCPUThread();
Internal::IdlePollUpdate();
Throttle(Common::Timer::GetCurrentValue());
}
void System::SaveRunaheadState()
2021-01-23 16:52:52 +00:00
{
// try to reuse the frontmost slot
MemorySaveState mss;
while (s_runahead_states.size() >= s_runahead_frames)
{
mss = std::move(s_runahead_states.front());
2021-01-23 16:52:52 +00:00
s_runahead_states.pop_front();
}
2021-01-23 16:52:52 +00:00
if (!SaveMemoryState(&mss))
{
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to save runahead state.");
2021-01-23 16:52:52 +00:00
return;
}
s_runahead_states.push_back(std::move(mss));
}
bool System::DoRunahead()
2021-01-23 16:52:52 +00:00
{
#ifdef PROFILE_MEMORY_SAVE_STATES
static Common::Timer replay_timer;
#endif
2021-01-23 16:52:52 +00:00
if (s_runahead_replay_pending)
{
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
DEV_LOG("runahead starting at frame {}", s_frame_number);
replay_timer.Reset();
#endif
2021-01-23 16:52:52 +00:00
// we need to replay and catch up - load the state,
s_runahead_replay_pending = false;
if (s_runahead_states.empty() || !LoadMemoryState(s_runahead_states.front()))
{
s_runahead_states.clear();
return false;
}
2021-01-23 16:52:52 +00:00
// figure out how many frames we need to run to catch up
s_runahead_replay_frames = static_cast<u32>(s_runahead_states.size());
2021-01-23 16:52:52 +00:00
// and throw away all the states, forcing us to catch up below
s_runahead_states.clear();
// run the frames with no audio
2022-08-01 13:39:41 +00:00
SPU::SetAudioOutputMuted(true);
2021-01-23 16:52:52 +00:00
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Rewound to frame {}, took {:.2f} ms", s_frame_number, replay_timer.GetTimeMilliseconds());
#endif
// we don't want to save the frame we just loaded. but we are "one frame ahead", because the frame we just tossed
// was never saved, so return but don't decrement the counter
return true;
2021-01-23 16:52:52 +00:00
}
else if (s_runahead_replay_frames == 0)
2021-01-23 16:52:52 +00:00
{
return false;
}
s_runahead_replay_frames--;
if (s_runahead_replay_frames > 0)
{
// keep running ahead
2021-01-23 16:52:52 +00:00
SaveRunaheadState();
return true;
2021-01-23 16:52:52 +00:00
}
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
VERBOSE_LOG("Running {} frames to catch up took {:.2f} ms", s_runahead_frames, replay_timer.GetTimeMilliseconds());
#endif
2021-01-23 16:52:52 +00:00
// we're all caught up. this frame gets saved in DoMemoryStates().
SPU::SetAudioOutputMuted(false);
2021-01-23 16:52:52 +00:00
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
DEV_LOG("runahead ending at frame {}, took {:.2f} ms", s_frame_number, replay_timer.GetTimeMilliseconds());
#endif
return false;
2021-01-23 16:52:52 +00:00
}
void System::SetRunaheadReplayFlag()
2021-01-23 16:52:52 +00:00
{
if (s_runahead_frames == 0 || s_runahead_states.empty())
return;
#ifdef PROFILE_MEMORY_SAVE_STATES
2024-05-23 10:55:28 +00:00
DEV_LOG("Runahead rewind pending...");
#endif
2021-01-23 16:52:52 +00:00
s_runahead_replay_pending = true;
}
void System::ShutdownSystem(bool save_resume_state)
{
if (!IsValid())
return;
if (save_resume_state)
{
Error error;
if (!SaveResumeState(&error))
{
Host::ReportErrorAsync(
TRANSLATE_SV("System", "Error"),
fmt::format(TRANSLATE_FS("System", "Failed to save resume state: {}"), error.GetDescription()));
}
}
s_state = State::Stopping;
if (!s_system_executing)
DestroySystem();
}
bool System::CanUndoLoadState()
{
return static_cast<bool>(m_undo_load_state);
}
std::optional<ExtendedSaveStateInfo> System::GetUndoSaveStateInfo()
{
std::optional<ExtendedSaveStateInfo> ssi;
if (m_undo_load_state)
{
m_undo_load_state->SeekAbsolute(0);
ssi = InternalGetExtendedSaveStateInfo(m_undo_load_state.get());
m_undo_load_state->SeekAbsolute(0);
if (ssi)
ssi->timestamp = 0;
}
return ssi;
}
bool System::UndoLoadState()
{
if (!m_undo_load_state)
return false;
Assert(IsValid());
Error error;
m_undo_load_state->SeekAbsolute(0);
if (!LoadStateFromStream(m_undo_load_state.get(), &error, true))
{
Host::ReportErrorAsync("Error",
fmt::format("Failed to load undo state, resetting system:\n", error.GetDescription()));
m_undo_load_state.reset();
ResetSystem();
return false;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Loaded undo save state.");
m_undo_load_state.reset();
return true;
}
bool System::SaveUndoLoadState()
{
if (m_undo_load_state)
m_undo_load_state.reset();
Error error;
m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
if (!SaveStateToStream(m_undo_load_state.get(), &error))
{
Host::AddOSDMessage(
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save undo load state:\n{}"), error.GetDescription()),
Host::OSD_CRITICAL_ERROR_DURATION);
m_undo_load_state.reset();
return false;
}
2024-05-23 10:55:28 +00:00
INFO_LOG("Saved undo load state: {} bytes", m_undo_load_state->GetSize());
return true;
}
bool System::IsRunningAtNonStandardSpeed()
{
if (!IsValid())
return false;
2024-05-27 01:46:18 +00:00
return (s_target_speed != 1.0f && !s_syncing_to_host);
}
s32 System::GetAudioOutputVolume()
{
return g_settings.GetAudioOutputVolume(IsRunningAtNonStandardSpeed());
}
void System::UpdateVolume()
{
if (!IsValid())
return;
2022-08-01 13:39:41 +00:00
SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
}
bool System::IsDumpingAudio()
{
2022-08-01 13:39:41 +00:00
return SPU::IsDumpingAudio();
}
bool System::StartDumpingAudio(const char* filename)
{
if (System::IsShutdown())
return false;
std::string auto_filename;
if (!filename)
{
const auto& serial = System::GetGameSerial();
2022-10-05 08:29:08 +00:00
if (serial.empty())
{
auto_filename = Path::Combine(
EmuFolders::Dumps, fmt::format("audio" FS_OSPATH_SEPARATOR_STR "{}.wav", GetTimestampStringForFileName()));
}
else
{
2022-10-05 08:29:08 +00:00
auto_filename = Path::Combine(EmuFolders::Dumps, fmt::format("audio" FS_OSPATH_SEPARATOR_STR "{}_{}.wav", serial,
GetTimestampStringForFileName()));
}
filename = auto_filename.c_str();
}
2022-08-01 13:39:41 +00:00
if (SPU::StartDumpingAudio(filename))
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"audio_dumping", ICON_FA_VOLUME_UP,
fmt::format(TRANSLATE_FS("OSDMessage", "Started dumping audio to '{}'."), Path::GetFileName(filename)),
Host::OSD_INFO_DURATION);
return true;
}
else
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"audio_dumping", ICON_FA_VOLUME_UP,
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to start dumping audio to '{}'."), Path::GetFileName(filename)),
Host::OSD_ERROR_DURATION);
return false;
}
}
void System::StopDumpingAudio()
{
2022-08-01 13:39:41 +00:00
if (System::IsShutdown() || !SPU::StopDumpingAudio())
return;
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage("audio_dumping", ICON_FA_VOLUME_MUTE, TRANSLATE_STR("OSDMessage", "Stopped dumping audio."),
Host::OSD_INFO_DURATION);
}
bool System::SaveScreenshot(const char* filename, DisplayScreenshotMode mode, DisplayScreenshotFormat format,
u8 quality, bool compress_on_thread)
{
if (!System::IsValid())
return false;
std::string auto_filename;
if (!filename)
{
const std::string sanitized_name = Path::SanitizeFileName(System::GetGameTitle());
const char* extension = Settings::GetDisplayScreenshotFormatExtension(format);
2024-03-15 05:21:06 +00:00
std::string basename;
if (sanitized_name.empty())
2024-03-15 05:21:06 +00:00
basename = fmt::format("{}", GetTimestampStringForFileName());
else
basename = fmt::format("{} {}", sanitized_name, GetTimestampStringForFileName());
2024-03-15 05:21:06 +00:00
auto_filename = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.{}", EmuFolders::Screenshots, basename, extension);
// handle quick screenshots to the same filename
u32 next_suffix = 1;
while (FileSystem::FileExists(auto_filename.c_str()))
{
2024-03-15 05:21:06 +00:00
auto_filename = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{} ({}).{}", EmuFolders::Screenshots, basename,
next_suffix, extension);
next_suffix++;
}
filename = auto_filename.c_str();
}
return g_gpu->RenderScreenshotToFile(filename, mode, quality, compress_on_thread, true);
}
2024-05-05 10:21:54 +00:00
std::string System::GetGameSaveStateFileName(std::string_view serial, s32 slot)
{
if (slot < 0)
2022-10-05 08:29:08 +00:00
return Path::Combine(EmuFolders::SaveStates, fmt::format("{}_resume.sav", serial));
else
2022-10-05 08:29:08 +00:00
return Path::Combine(EmuFolders::SaveStates, fmt::format("{}_{}.sav", serial, slot));
}
std::string System::GetGlobalSaveStateFileName(s32 slot)
{
if (slot < 0)
return Path::Combine(EmuFolders::SaveStates, "resume.sav");
else
return Path::Combine(EmuFolders::SaveStates, fmt::format("savestate_{}.sav", slot));
}
2022-10-05 08:29:08 +00:00
std::vector<SaveStateInfo> System::GetAvailableSaveStates(const char* serial)
{
std::vector<SaveStateInfo> si;
std::string path;
auto add_path = [&si](std::string path, s32 slot, bool global) {
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path.c_str(), &sd))
return;
si.push_back(SaveStateInfo{std::move(path), sd.ModificationTime, static_cast<s32>(slot), global});
};
2022-10-05 08:29:08 +00:00
if (serial && std::strlen(serial) > 0)
{
2022-10-05 08:29:08 +00:00
add_path(GetGameSaveStateFileName(serial, -1), -1, false);
for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++)
2022-10-05 08:29:08 +00:00
add_path(GetGameSaveStateFileName(serial, i), i, false);
}
for (s32 i = 1; i <= GLOBAL_SAVE_STATE_SLOTS; i++)
add_path(GetGlobalSaveStateFileName(i), i, true);
return si;
}
2022-10-05 08:29:08 +00:00
std::optional<SaveStateInfo> System::GetSaveStateInfo(const char* serial, s32 slot)
{
2022-10-05 08:29:08 +00:00
const bool global = (!serial || serial[0] == 0);
std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(serial, slot);
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path.c_str(), &sd))
return std::nullopt;
return SaveStateInfo{std::move(path), sd.ModificationTime, slot, global};
}
std::optional<ExtendedSaveStateInfo> System::GetExtendedSaveStateInfo(const char* path)
{
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path, &sd))
return std::nullopt;
std::unique_ptr<ByteStream> stream = ByteStream::OpenFile(path, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE);
if (!stream)
return std::nullopt;
std::optional<ExtendedSaveStateInfo> ssi(InternalGetExtendedSaveStateInfo(stream.get()));
if (ssi)
ssi->timestamp = sd.ModificationTime;
return ssi;
}
std::optional<ExtendedSaveStateInfo> System::InternalGetExtendedSaveStateInfo(ByteStream* stream)
{
SAVE_STATE_HEADER header;
if (!stream->Read(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC)
return std::nullopt;
ExtendedSaveStateInfo ssi;
if (header.version < SAVE_STATE_MINIMUM_VERSION || header.version > SAVE_STATE_VERSION)
{
ssi.title = fmt::format(TRANSLATE_FS("System", "Invalid version {} ({} version {})"), header.version,
header.version > SAVE_STATE_VERSION ? "maximum" : "minimum",
header.version > SAVE_STATE_VERSION ? SAVE_STATE_VERSION : SAVE_STATE_MINIMUM_VERSION);
return ssi;
}
header.title[sizeof(header.title) - 1] = 0;
ssi.title = header.title;
2022-10-05 08:29:08 +00:00
header.serial[sizeof(header.serial) - 1] = 0;
ssi.serial = header.serial;
if (header.media_filename_length > 0 &&
(header.offset_to_media_filename + header.media_filename_length) <= stream->GetSize())
{
stream->SeekAbsolute(header.offset_to_media_filename);
ssi.media_path.resize(header.media_filename_length);
if (!stream->Read2(ssi.media_path.data(), header.media_filename_length))
std::string().swap(ssi.media_path);
}
if (header.screenshot_width > 0 && header.screenshot_height > 0 &&
header.screenshot_size >= (header.screenshot_width * header.screenshot_height * sizeof(u32)) &&
(static_cast<u64>(header.offset_to_screenshot) + static_cast<u64>(header.screenshot_size)) <= stream->GetSize())
{
stream->SeekAbsolute(header.offset_to_screenshot);
ssi.screenshot_data.resize((header.screenshot_size + 3u) / 4u);
if (stream->Read2(ssi.screenshot_data.data(), header.screenshot_size))
{
ssi.screenshot_width = header.screenshot_width;
ssi.screenshot_height = header.screenshot_height;
}
else
{
decltype(ssi.screenshot_data)().swap(ssi.screenshot_data);
}
}
return ssi;
}
2022-10-05 08:29:08 +00:00
void System::DeleteSaveStates(const char* serial, bool resume)
{
2022-10-05 08:29:08 +00:00
const std::vector<SaveStateInfo> states(GetAvailableSaveStates(serial));
for (const SaveStateInfo& si : states)
{
if (si.global || (!resume && si.slot < 0))
continue;
2024-05-23 10:55:28 +00:00
INFO_LOG("Removing save state '{}'", Path::GetFileName(si.path));
Error error;
if (!FileSystem::DeleteFile(si.path.c_str(), &error)) [[unlikely]]
2024-05-23 10:55:28 +00:00
ERROR_LOG("Failed to delete save state file '{}': {}", Path::GetFileName(si.path), error.GetDescription());
}
}
std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot,
MemoryCardType* out_type)
{
const char* section = "MemoryCards";
const TinyString type_key = TinyString::from_format("Card{}Type", slot + 1);
const MemoryCardType default_type =
(slot == 0) ? Settings::DEFAULT_MEMORY_CARD_1_TYPE : Settings::DEFAULT_MEMORY_CARD_2_TYPE;
const MemoryCardType global_type =
Settings::ParseMemoryCardTypeName(
Host::GetBaseTinyStringSettingValue(section, type_key, Settings::GetMemoryCardTypeName(default_type)))
.value_or(default_type);
MemoryCardType type = global_type;
std::unique_ptr<INISettingsInterface> ini;
if (!serial.empty())
{
std::string game_settings_path = GetGameSettingsPath(serial);
if (FileSystem::FileExists(game_settings_path.c_str()))
{
ini = std::make_unique<INISettingsInterface>(std::move(game_settings_path));
if (!ini->Load())
{
ini.reset();
}
else if (ini->ContainsValue(section, type_key))
{
type = Settings::ParseMemoryCardTypeName(
ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type)))
.value_or(global_type);
}
}
}
else if (type == MemoryCardType::PerGame)
{
// always shared without serial
type = MemoryCardType::Shared;
}
if (out_type)
*out_type = type;
std::string ret;
switch (type)
{
case MemoryCardType::None:
break;
case MemoryCardType::Shared:
{
const TinyString path_key = TinyString::from_format("Card{}Path", slot + 1);
std::string global_path =
Host::GetBaseStringSettingValue(section, path_key, Settings::GetDefaultSharedMemoryCardName(slot + 1).c_str());
if (ini && ini->ContainsValue(section, path_key))
ret = ini->GetStringValue(section, path_key, global_path.c_str());
else
ret = std::move(global_path);
if (!Path::IsAbsolute(ret))
ret = Path::Combine(EmuFolders::MemoryCards, ret);
}
break;
case MemoryCardType::PerGame:
ret = g_settings.GetGameMemoryCardPath(serial, slot);
break;
case MemoryCardType::PerGameTitle:
{
const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(serial);
if (entry)
{
ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), slot);
// Use disc set name if there isn't a per-disc card present.
const bool global_use_playlist_title = Host::GetBaseBoolSettingValue(section, "UsePlaylistTitle", true);
const bool use_playlist_title =
ini ? ini->GetBoolValue(section, "UsePlaylistTitle", global_use_playlist_title) : global_use_playlist_title;
if (!entry->disc_set_name.empty() && use_playlist_title && !FileSystem::FileExists(ret.c_str()))
ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), slot);
}
else
{
ret = g_settings.GetGameMemoryCardPath(
Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
}
}
break;
case MemoryCardType::PerGameFileTitle:
{
ret = g_settings.GetGameMemoryCardPath(
Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
}
break;
default:
break;
}
return ret;
}
std::string System::GetMostRecentResumeSaveStatePath()
{
std::vector<FILESYSTEM_FIND_DATA> files;
if (!FileSystem::FindFiles(EmuFolders::SaveStates.c_str(), "*resume.sav", FILESYSTEM_FIND_FILES, &files) ||
files.empty())
{
return {};
}
FILESYSTEM_FIND_DATA* most_recent = &files[0];
for (FILESYSTEM_FIND_DATA& file : files)
{
if (file.ModificationTime > most_recent->ModificationTime)
most_recent = &file;
}
return std::move(most_recent->FileName);
}
std::string System::GetCheatFileName()
{
std::string ret;
const std::string& title = System::GetGameTitle();
if (!title.empty())
ret = Path::Combine(EmuFolders::Cheats, fmt::format("{}.cht", title.c_str()));
return ret;
}
bool System::LoadCheatList()
{
// Called when booting, needs to test for shutdown.
if (IsShutdown() || !g_settings.enable_cheats)
return false;
const std::string filename(GetCheatFileName());
if (filename.empty() || !FileSystem::FileExists(filename.c_str()))
return false;
std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
if (!cl->LoadFromFile(filename.c_str(), CheatList::Format::Autodetect))
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"cheats_loaded", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Failed to load cheats from '{}'."), Path::GetFileName(filename)));
return false;
}
SetCheatList(std::move(cl));
return true;
}
bool System::LoadCheatListFromDatabase()
{
2023-09-07 10:13:48 +00:00
if (IsShutdown() || s_running_game_serial.empty() || Achievements::IsHardcoreModeActive())
return false;
std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
2022-10-05 08:29:08 +00:00
if (!cl->LoadFromPackage(s_running_game_serial))
return false;
2024-05-23 10:55:28 +00:00
INFO_LOG("Loaded {} cheats from database.", cl->GetCodeCount());
2022-07-22 12:51:29 +00:00
SetCheatList(std::move(cl));
return true;
}
bool System::SaveCheatList()
{
if (!System::IsValid() || !System::HasCheatList())
return false;
const std::string filename(GetCheatFileName());
if (filename.empty())
return false;
if (!System::GetCheatList()->SaveToPCSXRFile(filename.c_str()))
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"cheat_save_error", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Failed to save cheat list to '{}'."), Path::GetFileName(filename)),
Host::OSD_ERROR_DURATION);
}
return true;
}
bool System::DeleteCheatList()
{
if (!System::IsValid())
return false;
const std::string filename(GetCheatFileName());
if (!filename.empty())
{
if (!FileSystem::DeleteFile(filename.c_str()))
return false;
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(
"cheat_delete", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Deleted cheat list '{}'."), Path::GetFileName(filename)),
Host::OSD_INFO_DURATION);
}
System::SetCheatList(nullptr);
return true;
}
void System::ClearCheatList(bool save_to_file)
{
if (!System::IsValid())
return;
CheatList* cl = System::GetCheatList();
if (!cl)
return;
while (cl->GetCodeCount() > 0)
cl->RemoveCode(cl->GetCodeCount() - 1);
if (save_to_file)
SaveCheatList();
}
void System::SetCheatCodeState(u32 index, bool enabled)
{
if (!System::IsValid() || !System::HasCheatList())
return;
CheatList* cl = System::GetCheatList();
if (index >= cl->GetCodeCount())
return;
CheatCode& cc = cl->GetCode(index);
if (cc.enabled == enabled)
return;
cc.enabled = enabled;
if (!enabled)
cc.ApplyOnDisable();
if (enabled)
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Cheat '{}' enabled."), cc.description),
Host::OSD_INFO_DURATION);
}
else
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Cheat '{}' disabled."), cc.description),
Host::OSD_INFO_DURATION);
}
SaveCheatList();
}
void System::ApplyCheatCode(u32 index)
{
if (!System::HasCheatList() || index >= System::GetCheatList()->GetCodeCount())
return;
const CheatCode& cc = System::GetCheatList()->GetCode(index);
if (!cc.enabled)
{
cc.Apply();
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Applied cheat '{}'."), cc.description),
Host::OSD_INFO_DURATION);
}
else
{
2024-05-24 11:57:51 +00:00
Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Cheat '{}' is already enabled."), cc.description),
Host::OSD_INFO_DURATION);
}
}
void System::ToggleWidescreen()
{
g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack;
const DisplayAspectRatio user_ratio =
Settings::ParseDisplayAspectRatio(
Host::GetStringSettingValue("Display", "AspectRatio",
Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO))
.c_str())
.value_or(DisplayAspectRatio::Auto);
;
if (user_ratio == DisplayAspectRatio::Auto || user_ratio == DisplayAspectRatio::PAR1_1 ||
user_ratio == DisplayAspectRatio::R4_3)
{
g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? DisplayAspectRatio::R16_9 : user_ratio;
}
else
{
g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? user_ratio : DisplayAspectRatio::Auto;
}
if (g_settings.gpu_widescreen_hack)
{
Host::AddKeyedOSDMessage(
"WidescreenHack",
fmt::format(TRANSLATE_FS("OSDMessage", "Widescreen hack is now enabled, and aspect ratio is set to {}."),
Settings::GetDisplayAspectRatioDisplayName(g_settings.display_aspect_ratio)),
5.0f);
}
else
{
Host::AddKeyedOSDMessage(
"WidescreenHack",
fmt::format(TRANSLATE_FS("OSDMessage", "Widescreen hack is now disabled, and aspect ratio is set to {}."),
Settings::GetDisplayAspectRatioDisplayName(g_settings.display_aspect_ratio), 5.0f));
}
GTE::UpdateAspectRatio();
}
void System::ToggleSoftwareRendering()
{
if (IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software)
return;
const GPURenderer new_renderer = g_gpu->IsHardwareRenderer() ? GPURenderer::Software : g_settings.gpu_renderer;
Host::AddIconOSDMessage("SoftwareRendering", ICON_FA_PAINT_ROLLER,
fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} renderer..."),
Settings::GetRendererDisplayName(new_renderer)),
Host::OSD_QUICK_DURATION);
RecreateGPU(new_renderer);
ResetPerformanceCounters();
}
void System::RequestDisplaySize(float scale /*= 0.0f*/)
{
if (!IsValid())
return;
if (scale == 0.0f)
scale = g_gpu->IsHardwareRenderer() ? static_cast<float>(g_settings.gpu_resolution_scale) : 1.0f;
const float y_scale =
(static_cast<float>(g_gpu->GetCRTCDisplayWidth()) / static_cast<float>(g_gpu->GetCRTCDisplayHeight())) /
g_gpu->ComputeDisplayAspectRatio();
const u32 requested_width =
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetCRTCDisplayWidth()) * scale)), 1);
2023-08-27 08:13:50 +00:00
const u32 requested_height =
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetCRTCDisplayHeight()) * y_scale * scale)), 1);
Host::RequestResizeHostDisplay(static_cast<s32>(requested_width), static_cast<s32>(requested_height));
}
void System::HostDisplayResized()
{
if (!IsValid())
return;
if (g_settings.gpu_widescreen_hack && g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow)
GTE::UpdateAspectRatio();
g_gpu->UpdateResolutionScale();
}
bool System::PresentDisplay(bool skip_present, bool explicit_present)
2023-08-30 10:34:48 +00:00
{
// acquire for IO.MousePos.
std::atomic_thread_fence(std::memory_order_acquire);
if (!skip_present)
{
FullscreenUI::Render();
ImGuiManager::RenderTextOverlays();
ImGuiManager::RenderOSDMessages();
2023-09-20 06:56:12 +00:00
if (s_state == State::Running)
ImGuiManager::RenderSoftwareCursors();
2023-08-30 10:34:48 +00:00
}
// Debug windows are always rendered, otherwise mouse input breaks on skip.
ImGuiManager::RenderOverlayWindows();
ImGuiManager::RenderDebugWindows();
bool do_present;
if (g_gpu && !skip_present)
do_present = g_gpu->PresentDisplay();
else
do_present = g_gpu_device->BeginPresent(skip_present);
if (do_present)
{
g_gpu_device->RenderImGui();
g_gpu_device->EndPresent(explicit_present);
2023-08-30 10:34:48 +00:00
if (g_gpu_device->IsGPUTimingEnabled())
{
s_accumulated_gpu_time += g_gpu_device->GetAndResetAccumulatedGPUTime();
s_presents_since_last_update++;
}
}
else
{
// Still need to kick ImGui or it gets cranky.
ImGui::Render();
}
ImGuiManager::NewFrame();
return do_present;
}
void System::InvalidateDisplay()
{
PresentDisplay(false, false);
if (g_gpu)
g_gpu->RestoreDeviceContext();
2023-08-30 10:34:48 +00:00
}
void System::SetTimerResolutionIncreased(bool enabled)
{
2022-08-22 09:55:38 +00:00
#if defined(_WIN32)
static bool current_state = false;
if (current_state == enabled)
return;
current_state = enabled;
if (enabled)
timeBeginPeriod(1);
else
timeEndPeriod(1);
#endif
}
2023-08-23 12:06:48 +00:00
void System::UpdateSessionTime(const std::string& prev_serial)
{
const u64 ctime = Common::Timer::GetCurrentValue();
if (!prev_serial.empty() && GameList::IsGameListLoaded())
2023-08-23 12:06:48 +00:00
{
// round up to seconds
const std::time_t etime =
static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
const std::time_t wtime = std::time(nullptr);
GameList::AddPlayedTimeForSerial(prev_serial, wtime, etime);
}
s_session_start_time = ctime;
}
u64 System::GetSessionPlayedTime()
{
const u64 ctime = Common::Timer::GetCurrentValue();
return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
}
2024-05-25 13:49:19 +00:00
SocketMultiplexer* System::GetSocketMultiplexer()
{
#ifdef ENABLE_SOCKET_MULTIPLEXER
if (s_socket_multiplexer)
return s_socket_multiplexer.get();
Error error;
s_socket_multiplexer = SocketMultiplexer::Create(&error);
if (s_socket_multiplexer)
INFO_LOG("Created socket multiplexer.");
else
ERROR_LOG("Failed to create socket multiplexer: {}", error.GetDescription());
return s_socket_multiplexer.get();
#else
ERROR_LOG("This build does not support sockets.");
return nullptr;
#endif
}
void System::ReleaseSocketMultiplexer()
{
#ifdef ENABLE_SOCKET_MULTIPLEXER
if (!s_socket_multiplexer || s_socket_multiplexer->HasAnyOpenSockets())
return;
INFO_LOG("Destroying socket multiplexer.");
s_socket_multiplexer.reset();
#endif
}
#ifdef ENABLE_DISCORD_PRESENCE
2023-08-23 12:06:48 +00:00
2024-07-04 04:40:16 +00:00
#include "discord_rpc.h"
#define DISCORD_RPC_FUNCTIONS(X) \
X(Discord_Initialize) \
X(Discord_Shutdown) \
X(Discord_RunCallbacks) \
X(Discord_UpdatePresence) \
X(Discord_ClearPresence)
namespace dyn_libs {
static bool OpenDiscordRPC(Error* error);
static void CloseDiscordRPC();
static DynamicLibrary s_discord_rpc_library;
#define ADD_FUNC(F) static decltype(&::F) F;
DISCORD_RPC_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
} // namespace dyn_libs
bool dyn_libs::OpenDiscordRPC(Error* error)
{
if (s_discord_rpc_library.IsOpen())
return true;
const std::string libname = DynamicLibrary::GetVersionedFilename("discord-rpc");
if (!s_discord_rpc_library.Open(libname.c_str(), error))
{
Error::AddPrefix(error, "Failed to load discord-rpc: ");
return false;
}
#define LOAD_FUNC(F) \
if (!s_discord_rpc_library.GetSymbol(#F, &F)) \
{ \
Error::SetStringFmt(error, "Failed to find function {}", #F); \
CloseDiscordRPC(); \
return false; \
}
DISCORD_RPC_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
return true;
}
void dyn_libs::CloseDiscordRPC()
{
if (!s_discord_rpc_library.IsOpen())
return;
#define UNLOAD_FUNC(F) F = nullptr;
DISCORD_RPC_FUNCTIONS(UNLOAD_FUNC)
#undef UNLOAD_FUNC
s_discord_rpc_library.Close();
}
2023-08-23 12:06:48 +00:00
void System::InitializeDiscordPresence()
{
if (s_discord_presence_active)
return;
2024-07-04 04:40:16 +00:00
Error error;
if (!dyn_libs::OpenDiscordRPC(&error))
{
ERROR_LOG("Failed to open discord-rpc: {}", error.GetDescription());
return;
}
2023-08-23 12:06:48 +00:00
DiscordEventHandlers handlers = {};
2024-07-04 04:40:16 +00:00
dyn_libs::Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
2023-08-23 12:06:48 +00:00
s_discord_presence_active = true;
2024-07-04 04:40:16 +00:00
UpdateRichPresence(true);
2023-08-23 12:06:48 +00:00
}
void System::ShutdownDiscordPresence()
{
if (!s_discord_presence_active)
return;
2024-07-04 04:40:16 +00:00
dyn_libs::Discord_ClearPresence();
dyn_libs::Discord_Shutdown();
dyn_libs::CloseDiscordRPC();
2023-08-23 12:06:48 +00:00
s_discord_presence_active = false;
}
2024-07-04 04:40:16 +00:00
void System::UpdateRichPresence(bool update_session_time)
2023-08-23 12:06:48 +00:00
{
if (!s_discord_presence_active)
return;
if (update_session_time)
s_discord_presence_time_epoch = std::time(nullptr);
2023-08-23 12:06:48 +00:00
// https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
DiscordRichPresence rp = {};
rp.largeImageKey = "duckstation_logo";
rp.largeImageText = "DuckStation PS1/PSX Emulator";
rp.startTimestamp = s_discord_presence_time_epoch;
2023-10-29 12:46:02 +00:00
rp.details = "No Game Running";
if (IsValidOrInitializing())
{
// Use disc set name if it's not a custom title.
if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty() &&
s_running_game_title == s_running_game_entry->title)
{
rp.details = s_running_game_entry->disc_set_name.c_str();
}
else
{
rp.details = s_running_game_title.empty() ? "Unknown Game" : s_running_game_title.c_str();
}
}
2023-08-23 12:06:48 +00:00
2023-09-18 12:29:47 +00:00
std::string state_string;
2023-09-07 10:13:48 +00:00
if (Achievements::HasRichPresence())
2023-08-23 12:06:48 +00:00
{
2023-09-07 10:13:48 +00:00
const auto lock = Achievements::GetLock();
2023-09-18 12:29:47 +00:00
state_string = StringUtil::Ellipsise(Achievements::GetRichPresenceString(), 128);
rp.state = state_string.c_str();
2023-09-07 10:13:48 +00:00
}
2023-08-23 12:06:48 +00:00
2024-07-04 04:40:16 +00:00
dyn_libs::Discord_UpdatePresence(&rp);
2023-08-23 12:06:48 +00:00
}
void System::PollDiscordPresence()
{
if (!s_discord_presence_active)
return;
2024-07-04 04:40:16 +00:00
dyn_libs::Discord_RunCallbacks();
}
#else
void System::UpdateRichPresence(bool update_session_time)
{
2023-08-23 12:06:48 +00:00
}
#endif