diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h index 7f5abc7d86..d38a5afa07 100644 --- a/Source/Core/Common/StringUtil.h +++ b/Source/Core/Common/StringUtil.h @@ -64,6 +64,8 @@ template static bool TryParse(const std::string &str, N *const output) { std::istringstream iss(str); + // is this right? not doing this breaks reading floats on locales that use different decimal separators + iss.imbue(std::locale(".1252")); N tmp = 0; if (iss >> tmp) diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index 2578e5a22b..34b2ca8d56 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -250,6 +250,8 @@ bool BootCore(const std::string& _rFilename) StartUp.bEnableMemcardSaving = g_NetPlaySettings.m_WriteToMemcard; StartUp.iCPUCore = g_NetPlaySettings.m_CPUcore; SConfig::GetInstance().m_DSPEnableJIT = g_NetPlaySettings.m_DSPEnableJIT; + SConfig::GetInstance().m_OCEnable = g_NetPlaySettings.m_OCEnable; + SConfig::GetInstance().m_OCFactor = g_NetPlaySettings.m_OCFactor; SConfig::GetInstance().m_EXIDevice[0] = g_NetPlaySettings.m_EXIDevice[0]; SConfig::GetInstance().m_EXIDevice[1] = g_NetPlaySettings.m_EXIDevice[1]; config_cache.bSetEXIDevice[0] = true; diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index dc7c0f950b..2dfb2c2d06 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -345,6 +345,8 @@ void SConfig::SaveCoreSettings(IniFile& ini) core->Set("RunCompareClient", m_LocalCoreStartupParameter.bRunCompareClient); core->Set("FrameLimit", m_Framelimit); core->Set("FrameSkip", m_FrameSkip); + core->Set("Overclock", m_OCFactor); + core->Set("OverclockEnable", m_OCEnable); core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend); core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode); core->Set("GameCubeAdapter", m_GameCubeAdapter); @@ -580,6 +582,8 @@ void SConfig::LoadCoreSettings(IniFile& ini) core->Get("FastDiscSpeed", &m_LocalCoreStartupParameter.bFastDiscSpeed, false); core->Get("DCBZ", &m_LocalCoreStartupParameter.bDCBZOFF, false); core->Get("FrameLimit", &m_Framelimit, 1); // auto frame limit by default + core->Get("Overclock", &m_OCFactor, 1.0f); + core->Get("OverclockEnable", &m_OCEnable, false); core->Get("FrameSkip", &m_FrameSkip, 0); core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, ""); core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto"); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 4a011ca9dc..5341ec61e0 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -49,6 +49,8 @@ struct SConfig : NonCopyable int m_InterfaceLanguage; // framelimit choose unsigned int m_Framelimit; + bool m_OCEnable; + float m_OCFactor; // other interface settings bool m_InterfaceToolbar; bool m_InterfaceStatusbar; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 119828c9aa..4756659b36 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -327,6 +327,8 @@ void EmuThread() Common::SetCurrentThreadName("Emuthread - Starting"); + if (SConfig::GetInstance().m_OCEnable) + DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(core_parameter.m_strFilename, 3000); diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index faee2a2f36..ff50f5b38a 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -48,6 +48,7 @@ static Common::FifoQueue tsQueue; // event pools static Event *eventPool = nullptr; +float lastOCFactor; int slicelength; static int maxSliceLength = MAX_SLICE_LENGTH; @@ -82,6 +83,23 @@ static void FreeEvent(Event* ev) static void EmptyTimedCallback(u64 userdata, int cyclesLate) {} +// Changing the CPU speed in Dolphin isn't actually done by changing the physical clock rate, +// but by changing the amount of work done in a particular amount of time. This tends to be more +// compatible because it stops the games from actually knowing directly that the clock rate has +// changed, and ensures that anything based on waiting a specific number of cycles still works. +// +// Technically it might be more accurate to call this changing the IPC instead of the CPU speed, +// but the effect is largely the same. +static int DowncountToCycles(int downcount) +{ + return (int)(downcount / lastOCFactor); +} + +static int CyclesToDowncount(int cycles) +{ + return (int)(cycles * lastOCFactor); +} + int RegisterEvent(const std::string& name, TimedCallback callback) { EventType type; @@ -115,7 +133,8 @@ void UnregisterAllEvents() void Init() { - PowerPC::ppcState.downcount = maxSliceLength; + lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f; + PowerPC::ppcState.downcount = CyclesToDowncount(maxSliceLength); slicelength = maxSliceLength; globalTimer = 0; idledCycles = 0; @@ -182,6 +201,7 @@ void DoState(PointerWrap &p) p.Do(fakeDecStartTicks); p.Do(fakeTBStartValue); p.Do(fakeTBStartTicks); + p.Do(lastOCFactor); p.DoMarker("CoreTimingData"); MoveEvents(); @@ -338,10 +358,10 @@ void SetMaximumSlice(int maximumSliceLength) void ForceExceptionCheck(int cycles) { - if (PowerPC::ppcState.downcount > cycles) + if (DowncountToCycles(PowerPC::ppcState.downcount) > cycles) { - slicelength -= (PowerPC::ppcState.downcount - cycles); // Account for cycles already executed by adjusting the slicelength - PowerPC::ppcState.downcount = cycles; + slicelength -= (DowncountToCycles(PowerPC::ppcState.downcount) - cycles); // Account for cycles already executed by adjusting the slicelength + PowerPC::ppcState.downcount = CyclesToDowncount(cycles); } } @@ -392,9 +412,10 @@ void Advance() { MoveEvents(); - int cyclesExecuted = slicelength - PowerPC::ppcState.downcount; + int cyclesExecuted = slicelength - DowncountToCycles(PowerPC::ppcState.downcount); globalTimer += cyclesExecuted; - PowerPC::ppcState.downcount = slicelength; + lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f; + PowerPC::ppcState.downcount = CyclesToDowncount(slicelength); while (first) { @@ -416,14 +437,14 @@ void Advance() if (!first) { WARN_LOG(POWERPC, "WARNING - no events in queue. Setting downcount to 10000"); - PowerPC::ppcState.downcount += 10000; + PowerPC::ppcState.downcount += CyclesToDowncount(10000); } else { slicelength = (int)(first->time - globalTimer); if (slicelength > maxSliceLength) slicelength = maxSliceLength; - PowerPC::ppcState.downcount = slicelength; + PowerPC::ppcState.downcount = CyclesToDowncount(slicelength); } if (advanceCallback) @@ -456,7 +477,7 @@ void Idle() } } - idledCycles += PowerPC::ppcState.downcount; + idledCycles += DowncountToCycles(PowerPC::ppcState.downcount); PowerPC::ppcState.downcount = 0; Advance(); diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index bcabce5742..57866dbcff 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -250,6 +250,8 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) packet >> g_NetPlaySettings.m_DSPEnableJIT; packet >> g_NetPlaySettings.m_DSPHLE; packet >> g_NetPlaySettings.m_WriteToMemcard; + packet >> g_NetPlaySettings.m_OCEnable; + packet >> g_NetPlaySettings.m_OCFactor; int tmp; packet >> tmp; g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices) tmp; diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index ae16243475..01b4964350 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -16,6 +16,8 @@ struct NetSettings bool m_DSPHLE; bool m_DSPEnableJIT; bool m_WriteToMemcard; + bool m_OCEnable; + float m_OCFactor; TEXIDevices m_EXIDevice[2]; }; diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index d484b97f26..9239f9a7b2 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -554,6 +554,8 @@ bool NetPlayServer::StartGame() spac << m_settings.m_DSPEnableJIT; spac << m_settings.m_DSPHLE; spac << m_settings.m_WriteToMemcard; + spac << m_settings.m_OCEnable; + spac << m_settings.m_OCFactor; spac << m_settings.m_EXIDevice[0]; spac << m_settings.m_EXIDevice[1]; diff --git a/Source/Core/DolphinWX/ConfigMain.cpp b/Source/Core/DolphinWX/ConfigMain.cpp index ca45ce9838..c9373614ed 100644 --- a/Source/Core/DolphinWX/ConfigMain.cpp +++ b/Source/Core/DolphinWX/ConfigMain.cpp @@ -134,7 +134,8 @@ EVT_CHOICE(ID_FRAMELIMIT, CConfigMain::CoreSettingsChanged) EVT_RADIOBOX(ID_CPUENGINE, CConfigMain::CoreSettingsChanged) EVT_CHECKBOX(ID_NTSCJ, CConfigMain::CoreSettingsChanged) - +EVT_SLIDER(ID_OVERCLOCK, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_ENABLEOVERCLOCK, CConfigMain::CoreSettingsChanged) EVT_RADIOBOX(ID_DSPENGINE, CConfigMain::AudioSettingsChanged) EVT_CHECKBOX(ID_ENABLE_THROTTLE, CConfigMain::AudioSettingsChanged) @@ -327,6 +328,10 @@ void CConfigMain::InitializeGUIValues() SkipIdle->SetValue(startup_params.bSkipIdle); EnableCheats->SetValue(startup_params.bEnableCheats); Framelimit->SetSelection(SConfig::GetInstance().m_Framelimit); + int ocFactor = (int)(log2f(SConfig::GetInstance().m_OCFactor) * 25.f + 100.f + 0.5f); + EnableOC->SetValue(SConfig::GetInstance().m_OCEnable); + OCSlider->SetValue(ocFactor); + UpdateCPUClock(); // General - Advanced for (unsigned int a = 0; a < (sizeof(CPUCores) / sizeof(CPUCore)); ++a) @@ -497,6 +502,7 @@ void CConfigMain::CreateGUIControls() wxPanel* const AudioPage = new wxPanel(Notebook, ID_AUDIOPAGE); wxPanel* const GamecubePage = new wxPanel(Notebook, ID_GAMECUBEPAGE); wxPanel* const WiiPage = new wxPanel(Notebook, ID_WIIPAGE); + wxPanel* const AdvancedPage = new wxPanel(Notebook, ID_ADVANCEDPAGE); PathsPage = new wxPanel(Notebook, ID_PATHSPAGE); Notebook->AddPage(GeneralPage, _("General")); @@ -505,6 +511,7 @@ void CConfigMain::CreateGUIControls() Notebook->AddPage(GamecubePage, _("GameCube")); Notebook->AddPage(WiiPage, _("Wii")); Notebook->AddPage(PathsPage, _("Paths")); + Notebook->AddPage(AdvancedPage, _("Advanced")); // General page // Core Settings - Basic @@ -521,6 +528,7 @@ void CConfigMain::CreateGUIControls() wxBoxSizer* sFramelimit = new wxBoxSizer(wxHORIZONTAL); sFramelimit->Add(TEXT_BOX(GeneralPage, _("Framelimit:")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM, 5); sFramelimit->Add(Framelimit, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); + wxStaticBoxSizer* const sbBasic = new wxStaticBoxSizer(wxVERTICAL, GeneralPage, _("Basic Settings")); sbBasic->Add(CPUThread, 0, wxALL, 5); sbBasic->Add(SkipIdle, 0, wxALL, 5); @@ -782,6 +790,33 @@ void CConfigMain::CreateGUIControls() sMain->Add(Notebook, 1, wxEXPAND|wxALL, 5); sMain->Add(CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxStaticBoxSizer* sbCPUOptions = new wxStaticBoxSizer(wxVERTICAL, AdvancedPage, _("CPU Options")); + wxBoxSizer* bOverclockEnable = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* bOverclock = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* bOverclockDesc = new wxBoxSizer(wxHORIZONTAL); + EnableOC = new wxCheckBox(AdvancedPage, ID_ENABLEOVERCLOCK, _("Enable CPU Clock Override")); + OCSlider = new wxSlider(AdvancedPage, ID_OVERCLOCK, 100, 0, 150, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + wxStaticText* OCDescription = new wxStaticText(AdvancedPage, wxID_ANY, + _("Higher values can make variable-framerate games\n" + "run at a higher framerate, at the expense of CPU.\n" + "Lower values can make variable-framerate games\n" + "run at a lower framerate, saving CPU.\n\n" + "WARNING: Changing this from the default (100%)\n" + "can and will break games and cause glitches.\n" + "Do so at your own risk. Please do not report\n" + "bugs that occur with a non-default clock.\n")); + OCText = new wxStaticText(AdvancedPage, wxID_ANY, ""); + bOverclockEnable->Add(EnableOC); + bOverclock->Add(OCSlider, 1, wxALL, 5); + bOverclock->Add(OCText, 1, wxALL, 5); + bOverclockDesc->Add(OCDescription, 1, wxALL, 5); + sbCPUOptions->Add(bOverclockEnable); + sbCPUOptions->Add(bOverclock); + sbCPUOptions->Add(bOverclockDesc); + wxBoxSizer* const sAdvancedPage = new wxBoxSizer(wxVERTICAL); + sAdvancedPage->Add(sbCPUOptions, 0, wxEXPAND | wxALL, 5); + AdvancedPage->SetSizer(sAdvancedPage); + InitializeGUIValues(); InitializeGUITooltips(); @@ -805,6 +840,14 @@ void CConfigMain::OnOk(wxCommandEvent& WXUNUSED (event)) SConfig::GetInstance().SaveSettings(); } +void CConfigMain::UpdateCPUClock() +{ + bool wii = SConfig::GetInstance().m_LocalCoreStartupParameter.bWii; + int percent = (int)(roundf(SConfig::GetInstance().m_OCFactor * 100.f)); + int clock = (int)(roundf(SConfig::GetInstance().m_OCFactor * (wii ? 729.f : 486.f))); + OCText->SetLabel(SConfig::GetInstance().m_OCEnable ? wxString::Format("%d %% (%d mhz)", percent, clock) : ""); +} + // Core settings void CConfigMain::CoreSettingsChanged(wxCommandEvent& event) { @@ -839,6 +882,16 @@ void CConfigMain::CoreSettingsChanged(wxCommandEvent& event) case ID_NTSCJ: startup_params.bForceNTSCJ = _NTSCJ->IsChecked(); break; + case ID_ENABLEOVERCLOCK: + SConfig::GetInstance().m_OCEnable = EnableOC->IsChecked(); + OCSlider->Enable(SConfig::GetInstance().m_OCEnable); + UpdateCPUClock(); + break; + case ID_OVERCLOCK: + // Vaguely exponential scaling? + SConfig::GetInstance().m_OCFactor = exp2f((OCSlider->GetValue() - 100.f) / 25.f); + UpdateCPUClock(); + break; } } diff --git a/Source/Core/DolphinWX/ConfigMain.h b/Source/Core/DolphinWX/ConfigMain.h index 3997678e9c..85e4b67c27 100644 --- a/Source/Core/DolphinWX/ConfigMain.h +++ b/Source/Core/DolphinWX/ConfigMain.h @@ -69,6 +69,7 @@ public: ID_GAMECUBEPAGE, ID_WIIPAGE, ID_PATHSPAGE, + ID_ADVANCEDPAGE, }; private: @@ -77,7 +78,9 @@ private: ID_CPUTHREAD = 1010, ID_IDLESKIP, ID_ENABLECHEATS, + ID_ENABLEOVERCLOCK, ID_FRAMELIMIT, + ID_OVERCLOCK, ID_CPUENGINE, @@ -144,6 +147,9 @@ private: // Advanced wxRadioBox* CPUEngine; wxCheckBox* _NTSCJ; + wxSlider* OCSlider; + wxStaticText* OCText; + wxCheckBox* EnableOC; wxBoxSizer* sDisplayPage; // Display settings @@ -238,6 +244,7 @@ private: void UpdateGUI(); void OnClose(wxCloseEvent& event); + void UpdateCPUClock(); void CoreSettingsChanged(wxCommandEvent& event); void DisplaySettingsChanged(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/NetWindow.cpp b/Source/Core/DolphinWX/NetWindow.cpp index 7e8f08e507..071563398f 100644 --- a/Source/Core/DolphinWX/NetWindow.cpp +++ b/Source/Core/DolphinWX/NetWindow.cpp @@ -438,6 +438,8 @@ void NetPlayDiag::GetNetSettings(NetSettings &settings) settings.m_DSPHLE = instance.m_LocalCoreStartupParameter.bDSPHLE; settings.m_DSPEnableJIT = instance.m_DSPEnableJIT; settings.m_WriteToMemcard = m_memcard_write->GetValue(); + settings.m_OCEnable = instance.m_OCEnable; + settings.m_OCFactor = instance.m_OCFactor; settings.m_EXIDevice[0] = instance.m_EXIDevice[0]; settings.m_EXIDevice[1] = instance.m_EXIDevice[1]; }