diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 3b5bfba7f..9805305e8 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -9,8 +9,6 @@ Log_SetChannel(CPU::CodeCache); namespace CPU { -bool USE_CODE_CACHE = false; -bool USE_RECOMPILER = false; constexpr bool USE_BLOCK_LINKING = true; static constexpr size_t RECOMPILER_CODE_CACHE_SIZE = 32 * 1024 * 1024; @@ -20,11 +18,12 @@ CodeCache::CodeCache() = default; CodeCache::~CodeCache() = default; -void CodeCache::Initialize(System* system, Core* core, Bus* bus) +void CodeCache::Initialize(System* system, Core* core, Bus* bus, bool use_recompiler) { m_system = system; m_core = core; m_bus = bus; + m_use_recompiler = use_recompiler; m_code_buffer = std::make_unique(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE); m_asm_functions = std::make_unique(); @@ -63,7 +62,7 @@ void CodeCache::Execute() #endif reexecute_block: - if (USE_RECOMPILER) + if (m_use_recompiler) block->host_code(m_core); else InterpretCachedBlock(*block); @@ -118,7 +117,16 @@ void CodeCache::Execute() } } -void CodeCache::Reset() +void CodeCache::SetUseRecompiler(bool enable) +{ + if (m_use_recompiler == enable) + return; + + m_use_recompiler = enable; + Flush(); +} + +void CodeCache::Flush() { m_bus->ClearRAMCodePageFlags(); for (auto& it : m_ram_block_map) @@ -285,7 +293,7 @@ bool CodeCache::CompileBlock(CodeBlock* block) return false; } - if (USE_RECOMPILER) + if (m_use_recompiler) { // Ensure we're not going to run out of space while compiling this block. if (m_code_buffer->GetFreeCodeSpace() < @@ -294,7 +302,7 @@ bool CodeCache::CompileBlock(CodeBlock* block) (block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION)) { Log_WarningPrintf("Out of code space, flushing all blocks."); - Reset(); + Flush(); } Recompiler::CodeGenerator codegen(m_core, m_code_buffer.get(), *m_asm_functions.get()); diff --git a/src/core/cpu_code_cache.h b/src/core/cpu_code_cache.h index 73ce1ed3e..8801295de 100644 --- a/src/core/cpu_code_cache.h +++ b/src/core/cpu_code_cache.h @@ -24,10 +24,15 @@ public: CodeCache(); ~CodeCache(); - void Initialize(System* system, Core* core, Bus* bus); - void Reset(); + void Initialize(System* system, Core* core, Bus* bus, bool use_recompiler); void Execute(); + /// Flushes the code cache, forcing all blocks to be recompiled. + void Flush(); + + /// Changes whether the recompiler is enabled. + void SetUseRecompiler(bool enable); + /// Invalidates all blocks which are in the range of the specified code page. void InvalidateBlocksWithPageIndex(u32 page_index); @@ -69,10 +74,9 @@ private: BlockMap m_blocks; + bool m_use_recompiler = false; + std::array, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map; }; -extern bool USE_CODE_CACHE; -extern bool USE_RECOMPILER; - } // namespace CPU \ No newline at end of file diff --git a/src/core/settings.cpp b/src/core/settings.cpp index ba6a60fc3..97c87592b 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -14,6 +14,7 @@ Settings::Settings() = default; void Settings::SetDefaults() { region = ConsoleRegion::Auto; + cpu_execution_mode = CPUExecutionMode::Interpreter; audio_sync_enabled = true; video_sync_enabled = true; @@ -50,6 +51,9 @@ void Settings::Load(const char* filename) speed_limiter_enabled = ini.GetBoolValue("General", "SpeedLimiterEnabled", true); start_paused = ini.GetBoolValue("General", "StartPaused", false); + cpu_execution_mode = + ParseCPUExecutionMode(ini.GetValue("CPU", "ExecutionMode", "Interpreter")).value_or(CPUExecutionMode::Interpreter); + gpu_renderer = ParseRendererName(ini.GetValue("GPU", "Renderer", "OpenGL")).value_or(GPURenderer::HardwareOpenGL); gpu_resolution_scale = static_cast(ini.GetLongValue("GPU", "ResolutionScale", 1)); gpu_true_color = ini.GetBoolValue("GPU", "TrueColor", false); @@ -79,6 +83,8 @@ bool Settings::Save(const char* filename) const ini.SetBoolValue("General", "SpeedLimiterEnabled", speed_limiter_enabled); ini.SetBoolValue("General", "StartPaused", start_paused); + ini.SetValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode)); + ini.SetValue("GPU", "Renderer", GetRendererName(gpu_renderer)); ini.SetLongValue("GPU", "ResolutionScale", static_cast(gpu_resolution_scale)); ini.SetBoolValue("GPU", "VSync", video_sync_enabled); @@ -138,6 +144,34 @@ const char* Settings::GetConsoleRegionDisplayName(ConsoleRegion region) return s_console_region_display_names[static_cast(region)]; } +static std::array s_cpu_execution_mode_names = {{"Interpreter", "CachedInterpreter", "Recompiler"}}; +static std::array s_cpu_execution_mode_display_names = { + {"Intepreter (Slowest)", "Cached Interpreter (Faster)", "Recompiler (Fastest)"}}; + +std::optional Settings::ParseCPUExecutionMode(const char* str) +{ + u8 index = 0; + for (const char* name : s_cpu_execution_mode_names) + { + if (strcasecmp(name, str) == 0) + return static_cast(index); + + index++; + } + + return std::nullopt; +} + +const char* Settings::GetCPUExecutionModeName(CPUExecutionMode mode) +{ + return s_cpu_execution_mode_names[static_cast(mode)]; +} + +const char* Settings::GetCPUExecutionModeDisplayName(CPUExecutionMode mode) +{ + return s_cpu_execution_mode_display_names[static_cast(mode)]; +} + static std::array s_gpu_renderer_names = {{"D3D11", "OpenGL", "Software"}}; static std::array s_gpu_renderer_display_names = { {"Hardware (D3D11)", "Hardware (OpenGL)", "Software"}}; diff --git a/src/core/settings.h b/src/core/settings.h index 7a639d2b5..90a542e4c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -8,6 +8,8 @@ struct Settings ConsoleRegion region = ConsoleRegion::Auto; + CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter; + bool start_paused = false; bool speed_limiter_enabled = true; bool audio_sync_enabled = true; @@ -51,6 +53,10 @@ struct Settings static const char* GetConsoleRegionName(ConsoleRegion region); static const char* GetConsoleRegionDisplayName(ConsoleRegion region); + static std::optional ParseCPUExecutionMode(const char* str); + static const char* GetCPUExecutionModeName(CPUExecutionMode mode); + static const char* GetCPUExecutionModeDisplayName(CPUExecutionMode mode); + static std::optional ParseRendererName(const char* str); static const char* GetRendererName(GPURenderer renderer); static const char* GetRendererDisplayName(GPURenderer renderer); diff --git a/src/core/system.cpp b/src/core/system.cpp index 64f89ae07..57f22e923 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -34,6 +34,7 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface) m_spu = std::make_unique(); m_mdec = std::make_unique(); m_region = host_interface->GetSettings().region; + m_cpu_execution_mode = host_interface->GetSettings().cpu_execution_mode; } System::~System() = default; @@ -171,7 +172,7 @@ bool System::Boot(const char* filename) void System::InitializeComponents() { m_cpu->Initialize(m_bus.get()); - m_cpu_code_cache->Initialize(this, m_cpu.get(), m_bus.get()); + m_cpu_code_cache->Initialize(this, m_cpu.get(), m_bus.get(), m_cpu_execution_mode == CPUExecutionMode::Recompiler); m_bus->Initialize(m_cpu.get(), m_cpu_code_cache.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_pad.get(), m_timers.get(), m_spu.get(), m_mdec.get()); @@ -239,7 +240,7 @@ bool System::DoState(StateWrapper& sw) return false; if (sw.IsReading()) - m_cpu_code_cache->Reset(); + m_cpu_code_cache->Flush(); if (!sw.DoMarker("Bus") || !m_bus->DoState(sw)) return false; @@ -274,7 +275,7 @@ bool System::DoState(StateWrapper& sw) void System::Reset() { m_cpu->Reset(); - m_cpu_code_cache->Reset(); + m_cpu_code_cache->Flush(); m_bus->Reset(); m_dma->Reset(); m_interrupt_controller->Reset(); @@ -303,14 +304,23 @@ bool System::SaveState(ByteStream* state) void System::RunFrame() { + // Duplicated to avoid branch in the while loop, as the downcount can be quite low at times. u32 current_frame_number = m_frame_number; - while (current_frame_number == m_frame_number) + if (m_cpu_execution_mode == CPUExecutionMode::Interpreter) { - if (CPU::USE_CODE_CACHE) - m_cpu_code_cache->Execute(); - else + while (current_frame_number == m_frame_number) + { m_cpu->Execute(); - Synchronize(); + Synchronize(); + } + } + else + { + while (current_frame_number == m_frame_number) + { + m_cpu_code_cache->Execute(); + Synchronize(); + } } } @@ -471,6 +481,13 @@ void System::UpdateMemoryCards() } } +void System::UpdateCPUExecutionMode() +{ + m_cpu_execution_mode = GetSettings().cpu_execution_mode; + m_cpu_code_cache->Flush(); + m_cpu_code_cache->SetUseRecompiler(m_cpu_execution_mode == CPUExecutionMode::Recompiler); +} + bool System::HasMedia() const { return m_cdrom->HasMedia(); diff --git a/src/core/system.h b/src/core/system.h index 5954db3d2..a5935bc34 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -83,6 +83,7 @@ public: void SetController(u32 slot, std::shared_ptr dev); void UpdateMemoryCards(); + void UpdateCPUExecutionMode(); bool HasMedia() const; bool InsertMedia(const char* path); @@ -109,6 +110,7 @@ private: std::unique_ptr m_spu; std::unique_ptr m_mdec; ConsoleRegion m_region = ConsoleRegion::NTSC_U; + CPUExecutionMode m_cpu_execution_mode = CPUExecutionMode::Interpreter; u32 m_frame_number = 1; u32 m_internal_frame_number = 1; u32 m_global_tick_counter = 0; diff --git a/src/core/types.h b/src/core/types.h index cfef32361..beb7ee931 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -31,6 +31,14 @@ enum class ConsoleRegion Count }; +enum class CPUExecutionMode : u8 +{ + Interpreter, + CachedInterpreter, + Recompiler, + Count +}; + enum class GPURenderer : u8 { HardwareD3D11, diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp index c5fcd492b..c095f332e 100644 --- a/src/duckstation/sdl_host_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -803,6 +803,26 @@ void SDLHostInterface::DrawQuickSettingsMenu() ImGui::Separator(); + if (ImGui::BeginMenu("CPU Execution Mode")) + { + const CPUExecutionMode current = m_settings.cpu_execution_mode; + for (u32 i = 0; i < static_cast(CPUExecutionMode::Count); i++) + { + if (ImGui::MenuItem(Settings::GetCPUExecutionModeDisplayName(static_cast(i)), nullptr, + i == static_cast(current))) + { + m_settings.cpu_execution_mode = static_cast(i); + settings_changed = true; + if (m_system) + m_system->UpdateCPUExecutionMode(); + } + } + + ImGui::EndMenu(); + } + + ImGui::Separator(); + if (ImGui::BeginMenu("Renderer")) { const GPURenderer current = m_settings.gpu_renderer; @@ -1012,6 +1032,13 @@ void SDLHostInterface::DrawSettingsWindow() m_settings.region = static_cast(region); settings_changed = true; } + + ImGui::Text("BIOS Path:"); + ImGui::SameLine(indent); + settings_changed |= DrawFileChooser("##bios_path", &m_settings.bios_path); + + settings_changed |= ImGui::Checkbox("Enable TTY Output", &m_settings.bios_patch_tty_enable); + settings_changed |= ImGui::Checkbox("Fast Boot", &m_settings.bios_patch_fast_boot); } ImGui::NewLine(); @@ -1041,17 +1068,6 @@ void SDLHostInterface::DrawSettingsWindow() } } - ImGui::NewLine(); - if (DrawSettingsSectionHeader("BIOS")) - { - ImGui::Text("ROM Path:"); - ImGui::SameLine(indent); - settings_changed |= DrawFileChooser("##bios_path", &m_settings.bios_path); - - settings_changed |= ImGui::Checkbox("Enable TTY Output", &m_settings.bios_patch_tty_enable); - settings_changed |= ImGui::Checkbox("Fast Boot", &m_settings.bios_patch_fast_boot); - } - ImGui::EndTabItem(); } @@ -1089,6 +1105,29 @@ void SDLHostInterface::DrawSettingsWindow() ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("CPU")) + { + ImGui::Text("Execution Mode:"); + ImGui::SameLine(indent); + + int execution_mode = static_cast(m_settings.cpu_execution_mode); + if (ImGui::Combo( + "##execution_mode", &execution_mode, + [](void*, int index, const char** out_text) { + *out_text = Settings::GetCPUExecutionModeDisplayName(static_cast(index)); + return true; + }, + nullptr, static_cast(CPUExecutionMode::Count))) + { + m_settings.cpu_execution_mode = static_cast(execution_mode); + settings_changed = true; + if (m_system) + m_system->UpdateCPUExecutionMode(); + } + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("GPU")) { if (DrawSettingsSectionHeader("Basic"))