Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / Core.cpp
blobaaf2c6e58e207965ec7f5f35e5a969e8b7b01fad
1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <set>
20 #include "base/NativeApp.h"
21 #include "base/display.h"
22 #include "base/mutex.h"
23 #include "base/timeutil.h"
24 #include "input/input_state.h"
25 #include "profiler/profiler.h"
27 #include "Core/Core.h"
28 #include "Core/Config.h"
29 #include "Core/MemMap.h"
30 #include "Core/SaveState.h"
31 #include "Core/System.h"
32 #include "Core/MIPS/MIPS.h"
34 #ifdef _WIN32
35 #ifndef _XBOX
36 #include "Windows/OpenGLBase.h"
37 #include "Windows/D3D9Base.h"
38 #endif
39 #include "Windows/InputDevice.h"
40 #endif
42 #include "Host.h"
44 #include "Core/Debugger/Breakpoints.h"
46 // Time until we stop considering the core active without user input.
47 // Should this be configurable? 2 hours currently.
48 static const double ACTIVITY_IDLE_TIMEOUT = 2.0 * 3600.0;
50 static event m_hStepEvent;
51 static recursive_mutex m_hStepMutex;
52 static event m_hInactiveEvent;
53 static recursive_mutex m_hInactiveMutex;
54 static bool singleStepPending = false;
55 static std::set<Core_ShutdownFunc> shutdownFuncs;
56 static bool windowHidden = false;
57 static double lastActivity = 0.0;
58 static double lastKeepAwake = 0.0;
60 #if defined (_WIN32) && !defined (__LIBRETRO__)
61 InputState input_state;
62 #else
63 extern InputState input_state;
64 #endif
66 void Core_NotifyWindowHidden(bool hidden) {
67 windowHidden = hidden;
68 // TODO: Wait until we can react?
71 void Core_NotifyActivity() {
72 lastActivity = time_now_d();
75 void Core_ListenShutdown(Core_ShutdownFunc func) {
76 shutdownFuncs.insert(func);
79 void Core_NotifyShutdown() {
80 for (auto it = shutdownFuncs.begin(); it != shutdownFuncs.end(); ++it) {
81 (*it)();
85 void Core_ErrorPause() {
86 Core_UpdateState(CORE_ERROR);
89 void Core_Halt(const char *msg) {
90 Core_EnableStepping(true);
91 ERROR_LOG(CPU, "CPU HALTED : %s",msg);
92 _dbg_update_();
95 void Core_Stop() {
96 Core_UpdateState(CORE_POWERDOWN);
97 Core_NotifyShutdown();
98 m_hStepEvent.notify_one();
101 bool Core_IsStepping() {
102 return coreState == CORE_STEPPING || coreState == CORE_POWERDOWN;
105 bool Core_IsActive() {
106 return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME || coreStatePending;
109 bool Core_IsInactive() {
110 return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending;
113 void Core_WaitInactive() {
114 while (Core_IsActive()) {
115 m_hInactiveEvent.wait(m_hInactiveMutex);
119 void Core_WaitInactive(int milliseconds) {
120 if (Core_IsActive()) {
121 m_hInactiveEvent.wait_for(m_hInactiveMutex, milliseconds);
125 bool UpdateScreenScale(int width, int height, bool smallWindow) {
126 g_dpi = 72;
127 g_dpi_scale = 1.0f;
128 #if defined(__SYMBIAN32__)
129 g_dpi_scale = 1.4f;
130 #elif defined(_WIN32)
131 if (smallWindow) {
132 g_dpi_scale = 2.0f;
134 #endif
135 pixel_in_dps = 1.0f / g_dpi_scale;
137 int new_dp_xres = width * g_dpi_scale;
138 int new_dp_yres = height * g_dpi_scale;
140 bool dp_changed = new_dp_xres != dp_xres || new_dp_yres != dp_yres;
141 bool px_changed = pixel_xres != width || pixel_yres != height;
143 if (dp_changed || px_changed) {
144 dp_xres = new_dp_xres;
145 dp_yres = new_dp_yres;
146 pixel_xres = width;
147 pixel_yres = height;
149 NativeResized();
150 return true;
152 return false;
155 void UpdateRunLoop() {
156 if (windowHidden && g_Config.bPauseWhenMinimized) {
157 sleep_ms(16);
158 return;
160 NativeUpdate(input_state);
163 lock_guard guard(input_state.lock);
164 EndInputState(&input_state);
167 if (GetUIState() != UISTATE_EXIT) {
168 NativeRender();
172 #if defined(USING_WIN_UI)
174 void GPU_SwapBuffers() {
175 switch (g_Config.iGPUBackend) {
176 case GPU_BACKEND_OPENGL:
177 GL_SwapBuffers();
178 break;
179 case GPU_BACKEND_DIRECT3D9:
180 D3D9_SwapBuffers();
181 break;
185 #endif
187 void Core_RunLoop() {
188 while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) {
189 time_update();
190 #if defined(USING_WIN_UI)
191 double startTime = time_now_d();
192 UpdateRunLoop();
194 // Simple throttling to not burn the GPU in the menu.
195 time_update();
196 double diffTime = time_now_d() - startTime;
197 int sleepTime = (int)(1000.0 / 60.0) - (int)(diffTime * 1000.0);
198 if (sleepTime > 0)
199 Sleep(sleepTime);
200 if (!windowHidden) {
201 GPU_SwapBuffers();
203 #else
204 UpdateRunLoop();
205 #endif
208 while (!coreState && GetUIState() == UISTATE_INGAME) {
209 time_update();
210 UpdateRunLoop();
211 #if defined(USING_WIN_UI)
212 if (!windowHidden && !Core_IsStepping()) {
213 GPU_SwapBuffers();
215 // Keep the system awake for longer than normal for cutscenes and the like.
216 const double now = time_now_d();
217 if (now < lastActivity + ACTIVITY_IDLE_TIMEOUT) {
218 // Only resetting it ever prime number seconds in case the call is expensive.
219 // Using a prime number to ensure there's no interaction with other periodic events.
220 if (now - lastKeepAwake > 89.0 || now < lastKeepAwake) {
221 SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
222 lastKeepAwake = now;
226 #endif
230 void Core_DoSingleStep() {
231 singleStepPending = true;
232 m_hStepEvent.notify_one();
235 void Core_UpdateSingleStep() {
236 m_hStepEvent.notify_one();
239 void Core_SingleStep() {
240 currentMIPS->SingleStep();
243 static inline void CoreStateProcessed() {
244 if (coreStatePending) {
245 coreStatePending = false;
246 m_hInactiveEvent.notify_one();
250 // Some platforms, like Android, do not call this function but handle things on their own.
251 void Core_Run()
253 #if defined(_DEBUG)
254 host->UpdateDisassembly();
255 #endif
256 #if !defined(USING_QT_UI) || defined(MOBILE_DEVICE)
257 while (true)
258 #endif
260 reswitch:
261 if (GetUIState() != UISTATE_INGAME) {
262 CoreStateProcessed();
263 if (GetUIState() == UISTATE_EXIT) {
264 return;
266 Core_RunLoop();
267 #if defined(USING_QT_UI) && !defined(MOBILE_DEVICE)
268 return;
269 #else
270 continue;
271 #endif
274 switch (coreState)
276 case CORE_RUNNING:
277 // enter a fast runloop
278 Core_RunLoop();
279 break;
281 // We should never get here on Android.
282 case CORE_STEPPING:
283 singleStepPending = false;
284 CoreStateProcessed();
286 // Check if there's any pending savestate actions.
287 SaveState::Process();
288 if (coreState == CORE_POWERDOWN) {
289 return;
292 // wait for step command..
293 #if defined(USING_QT_UI) || defined(_DEBUG)
294 host->UpdateDisassembly();
295 host->UpdateMemView();
296 host->SendCoreWait(true);
297 #endif
299 m_hStepEvent.wait(m_hStepMutex);
301 #if defined(USING_QT_UI) || defined(_DEBUG)
302 host->SendCoreWait(false);
303 #endif
304 #if defined(USING_QT_UI) && !defined(MOBILE_DEVICE)
305 if (coreState != CORE_STEPPING)
306 return;
307 #endif
308 // No step pending? Let's go back to the wait.
309 if (!singleStepPending || coreState != CORE_STEPPING) {
310 if (coreState == CORE_POWERDOWN) {
311 return;
313 goto reswitch;
316 Core_SingleStep();
317 // update disasm dialog
318 #if defined(USING_QT_UI) || defined(_DEBUG)
319 host->UpdateDisassembly();
320 host->UpdateMemView();
321 #endif
322 break;
324 case CORE_POWERUP:
325 case CORE_POWERDOWN:
326 case CORE_ERROR:
327 // Exit loop!!
328 CoreStateProcessed();
330 return;
332 case CORE_NEXTFRAME:
333 return;
340 void Core_EnableStepping(bool step) {
341 if (step) {
342 sleep_ms(1);
343 #if defined(_DEBUG)
344 host->SetDebugMode(true);
345 #endif
346 m_hStepEvent.reset();
347 Core_UpdateState(CORE_STEPPING);
348 } else {
349 #if defined(_DEBUG)
350 host->SetDebugMode(false);
351 #endif
352 coreState = CORE_RUNNING;
353 coreStatePending = false;
354 m_hStepEvent.notify_one();