/[pcsx2_0.9.7]/trunk/pcsx2/gui/AppCoreThread.cpp
ViewVC logotype

Contents of /trunk/pcsx2/gui/AppCoreThread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 1 month ago) by william
File size: 18504 byte(s)
re-commit (had local access denied errors when committing)
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "App.h"
18 #include "AppSaveStates.h"
19 #include "AppGameDatabase.h"
20
21 #include "Utilities/TlsVariable.inl"
22
23 #include "ps2/BiosTools.h"
24 #include "GS.h"
25
26 #include "CDVD/CDVD.h"
27 #include "Elfheader.h"
28 #include "Patch.h"
29 #include "R5900Exceptions.h"
30
31 __aligned16 SysMtgsThread mtgsThread;
32 __aligned16 AppCoreThread CoreThread;
33
34 typedef void (AppCoreThread::*FnPtr_CoreThreadMethod)();
35
36 // --------------------------------------------------------------------------------------
37 // SysExecEvent_InvokeCoreThreadMethod
38 // --------------------------------------------------------------------------------------
39 class SysExecEvent_InvokeCoreThreadMethod : public SysExecEvent
40 {
41 protected:
42 FnPtr_CoreThreadMethod m_method;
43 bool m_IsCritical;
44
45 public:
46 wxString GetEventName() const { return L"CoreThreadMethod"; }
47 virtual ~SysExecEvent_InvokeCoreThreadMethod() throw() {}
48 SysExecEvent_InvokeCoreThreadMethod* Clone() const { return new SysExecEvent_InvokeCoreThreadMethod(*this); }
49
50 bool AllowCancelOnExit() const { return false; }
51 bool IsCriticalEvent() const { return m_IsCritical; }
52
53 SysExecEvent_InvokeCoreThreadMethod( FnPtr_CoreThreadMethod method, bool critical=false )
54 {
55 m_method = method;
56 m_IsCritical = critical;
57 }
58
59 SysExecEvent_InvokeCoreThreadMethod& Critical()
60 {
61 m_IsCritical = true;
62 return *this;
63 }
64
65 protected:
66 void InvokeEvent()
67 {
68 if( m_method ) (CoreThread.*m_method)();
69 }
70 };
71
72 static void PostCoreStatus( CoreThreadStatus pevt )
73 {
74 sApp.PostAction( CoreThreadStatusEvent( pevt ) );
75 }
76
77 // --------------------------------------------------------------------------------------
78 // AppCoreThread Implementations
79 // --------------------------------------------------------------------------------------
80 AppCoreThread::AppCoreThread() : SysCoreThread()
81 {
82 m_resetCdvd = false;
83 }
84
85 AppCoreThread::~AppCoreThread() throw()
86 {
87 _parent::Cancel(); // use parent's, skips thread affinity check.
88 }
89
90 static void _Cancel()
91 {
92 GetCoreThread().Cancel();
93 }
94
95 void AppCoreThread::Cancel( bool isBlocking )
96 {
97 if (GetSysExecutorThread().IsRunning() && !GetSysExecutorThread().Rpc_TryInvoke( _Cancel, L"AppCoreThread::Cancel" ))
98 _parent::Cancel( wxTimeSpan(0, 0, 4, 0) );
99 }
100
101 void AppCoreThread::Reset()
102 {
103 if( !GetSysExecutorThread().IsSelf() )
104 {
105 GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Reset) );
106 return;
107 }
108
109 _parent::Reset();
110 }
111
112 void AppCoreThread::ResetQuick()
113 {
114 if( !GetSysExecutorThread().IsSelf() )
115 {
116 GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ResetQuick) );
117 return;
118 }
119
120 _parent::ResetQuick();
121 }
122
123 ExecutorThread& GetSysExecutorThread()
124 {
125 return wxGetApp().SysExecutorThread;
126 }
127
128 static void _Suspend()
129 {
130 GetCoreThread().Suspend(true);
131 }
132
133 void AppCoreThread::Suspend( bool isBlocking )
134 {
135 if (IsClosed()) return;
136
137 if (IsSelf())
138 {
139 // this should never fail...
140 bool result = GetSysExecutorThread().Rpc_TryInvokeAsync( _Suspend, L"AppCoreThread::Suspend" );
141 pxAssert(result);
142 }
143 else if (!GetSysExecutorThread().Rpc_TryInvoke( _Suspend, L"AppCoreThread::Suspend" ))
144 _parent::Suspend(true);
145 }
146
147 void AppCoreThread::Resume()
148 {
149 if( !GetSysExecutorThread().IsSelf() )
150 {
151 GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Resume) );
152 return;
153 }
154
155 GetCorePlugins().Init();
156 _parent::Resume();
157 }
158
159 void AppCoreThread::ChangeCdvdSource()
160 {
161 if( !GetSysExecutorThread().IsSelf() )
162 {
163 GetSysExecutorThread().PostEvent( new SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ChangeCdvdSource) );
164 return;
165 }
166
167 CDVD_SourceType cdvdsrc( g_Conf->CdvdSource );
168 if( cdvdsrc == CDVDsys_GetSourceType() ) return;
169
170 // Fast change of the CDVD source only -- a Pause will suffice.
171
172 ScopedCoreThreadPause paused_core;
173 GetCorePlugins().Close( PluginId_CDVD );
174 CDVDsys_ChangeSource( cdvdsrc );
175 paused_core.AllowResume();
176
177 // TODO: Add a listener for CDVDsource changes? Or should we bother?
178 }
179
180 void Pcsx2App::SysApplySettings()
181 {
182 if( AppRpc_TryInvoke(&Pcsx2App::SysApplySettings) ) return;
183 CoreThread.ApplySettings( g_Conf->EmuOptions );
184
185 CDVD_SourceType cdvdsrc( g_Conf->CdvdSource );
186 if( cdvdsrc != CDVDsys_GetSourceType() || (cdvdsrc==CDVDsrc_Iso && (CDVDsys_GetFile(cdvdsrc) != g_Conf->CurrentIso)) )
187 {
188 CoreThread.ResetCdvd();
189 }
190
191 CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
192 }
193
194 void AppCoreThread::OnResumeReady()
195 {
196 wxGetApp().SysApplySettings();
197 wxGetApp().PostMethod( AppSaveSettings );
198 _parent::OnResumeReady();
199 }
200
201 // Load Game Settings found in database
202 // (game fixes, round modes, clamp modes, etc...)
203 // Returns number of gamefixes set
204 static int loadGameSettings(Pcsx2Config& dest, const Game_Data& game, bool verbose = true) {
205 if( !game.IsOk() ) return 0;
206
207 int gf = 0;
208
209 if (game.keyExists("eeRoundMode"))
210 {
211 SSE_RoundMode eeRM = (SSE_RoundMode)game.getInt("eeRoundMode");
212 if (EnumIsValid(eeRM))
213 {
214 if(verbose) Console.WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
215 dest.Cpu.sseMXCSR.SetRoundMode(eeRM);
216 ++gf;
217 }
218 }
219
220 if (game.keyExists("vuRoundMode"))
221 {
222 SSE_RoundMode vuRM = (SSE_RoundMode)game.getInt("vuRoundMode");
223 if (EnumIsValid(vuRM))
224 {
225 if(verbose) Console.WriteLn("(GameDB) Changing VU0/VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
226 dest.Cpu.sseVUMXCSR.SetRoundMode(vuRM);
227 ++gf;
228 }
229 }
230
231 if (game.keyExists("eeClampMode")) {
232 int clampMode = game.getInt("eeClampMode");
233 if(verbose) Console.WriteLn("(GameDB) Changing EE/FPU clamp mode [mode=%d]", clampMode);
234 dest.Cpu.Recompiler.fpuOverflow = (clampMode >= 1);
235 dest.Cpu.Recompiler.fpuExtraOverflow = (clampMode >= 2);
236 dest.Cpu.Recompiler.fpuFullMode = (clampMode >= 3);
237 gf++;
238 }
239
240 if (game.keyExists("vuClampMode")) {
241 int clampMode = game.getInt("vuClampMode");
242 if(verbose) Console.WriteLn("(GameDB) Changing VU0/VU1 clamp mode [mode=%d]", clampMode);
243 dest.Cpu.Recompiler.vuOverflow = (clampMode >= 1);
244 dest.Cpu.Recompiler.vuExtraOverflow = (clampMode >= 2);
245 dest.Cpu.Recompiler.vuSignOverflow = (clampMode >= 3);
246 gf++;
247 }
248
249 for( GamefixId id=GamefixId_FIRST; id<pxEnumEnd; ++id )
250 {
251 wxString key( EnumToString(id) );
252 key += L"Hack";
253
254 if (game.keyExists(key))
255 {
256 bool enableIt = game.getBool(key);
257 dest.Gamefixes.Set(id, enableIt);
258 if(verbose) Console.WriteLn(L"(GameDB) %s Gamefix: " + key, enableIt ? L"Enabled" : L"Disabled" );
259 gf++;
260 }
261 }
262
263 return gf;
264 }
265
266 void AppCoreThread::ApplySettings( const Pcsx2Config& src )
267 {
268 // Used to track the current game serial/id, and used to disable verbose logging of
269 // applied patches if the game info hasn't changed. (avoids spam when suspending/resuming
270 // or using TAB or other things).
271 static wxString curGameKey;
272
273 // 'fixup' is the EmuConfig we're going to upload to the emulator, which very well may
274 // differ from the user-configured EmuConfig settings. So we make a copy here and then
275 // we apply the commandline overrides and database gamefixes, and then upload 'fixup'
276 // to the global EmuConfig.
277 //
278 // Note: It's important that we apply the commandline overrides *before* database fixes.
279 // The database takes precedence (if enabled).
280
281 Pcsx2Config fixup( src );
282
283 const CommandlineOverrides& overrides( wxGetApp().Overrides );
284 if( overrides.DisableSpeedhacks || !g_Conf->EnableSpeedHacks )
285 fixup.Speedhacks.DisableAll();
286
287 if( overrides.ApplyCustomGamefixes )
288 {
289 for (GamefixId id=GamefixId_FIRST; id < pxEnumEnd; ++id)
290 fixup.Gamefixes.Set( id, overrides.Gamefixes.Get(id) );
291 }
292 else if( !g_Conf->EnableGameFixes )
293 fixup.Gamefixes.DisableAll();
294
295 wxString gameCRC;
296 wxString gameSerial;
297 wxString gamePatch;
298 wxString gameFixes;
299 wxString gameCheats;
300
301 // [TODO] : Fix this so that it recognizes and reports BIOS-booting status!
302 wxString gameName (L"Unknown");
303 wxString gameCompat;
304
305 if (ElfCRC) gameCRC.Printf( L"%8.8x", ElfCRC );
306 if (!DiscSerial.IsEmpty()) gameSerial = L" [" + DiscSerial + L"]";
307
308 const wxString newGameKey( SysGetDiscID() );
309 const bool verbose( newGameKey != curGameKey );
310 curGameKey = newGameKey;
311
312 if (IGameDatabase* GameDB = AppHost_GetGameDatabase() )
313 {
314 Game_Data game;
315 if (GameDB->findGame(game, curGameKey)) {
316 int compat = game.getInt("Compat");
317 gameName = game.getString("Name");
318 gameName += L" (" + game.getString("Region") + L")";
319 gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
320 }
321
322 if (EmuConfig.EnablePatches) {
323 if (int patches = InitPatches(gameCRC, game)) {
324 gamePatch.Printf(L" [%d Patches]", patches);
325 if (verbose) Console.WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patches);
326 }
327 if (int fixes = loadGameSettings(fixup, game, verbose)) {
328 gameFixes.Printf(L" [%d Fixes]", fixes);
329 }
330 }
331 }
332
333 if (EmuConfig.EnableCheats) {
334 if (int cheats = InitCheats(gameCRC)) {
335 gameCheats.Printf(L" [%d Cheats]", cheats);
336 }
337 }
338
339 Console.SetTitle(gameName+gameSerial+gameCompat+gameFixes+gamePatch+gameCheats);
340
341 // Re-entry guard protects against cases where code wants to manually set core settings
342 // which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is
343 // usually the desired behavior) will be ignored.
344
345 static int localc = 0;
346 RecursionGuard guard( localc );
347 if( guard.IsReentrant() ) return;
348 if( fixup == EmuConfig ) return;
349
350 if( m_ExecMode >= ExecMode_Opened )
351 {
352 ScopedCoreThreadPause paused_core;
353 _parent::ApplySettings( fixup );
354 paused_core.AllowResume();
355 }
356 else
357 {
358 _parent::ApplySettings( fixup );
359 }
360 }
361
362 // --------------------------------------------------------------------------------------
363 // AppCoreThread *Worker* Implementations
364 // (Called from the context of this thread only)
365 // --------------------------------------------------------------------------------------
366
367 void AppCoreThread::DoCpuReset()
368 {
369 PostCoreStatus( CoreThread_Reset );
370 _parent::DoCpuReset();
371 }
372
373 void AppCoreThread::OnResumeInThread( bool isSuspended )
374 {
375 if( m_resetCdvd )
376 {
377 GetCorePlugins().Close( PluginId_CDVD );
378 CDVDsys_ChangeSource( g_Conf->CdvdSource );
379 m_resetCdvd = false;
380 }
381
382 _parent::OnResumeInThread( isSuspended );
383 PostCoreStatus( CoreThread_Resumed );
384 }
385
386 void AppCoreThread::OnSuspendInThread()
387 {
388 _parent::OnSuspendInThread();
389 PostCoreStatus( CoreThread_Suspended );
390 }
391
392 // Called whenever the thread has terminated, for either regular or irregular reasons.
393 // Typically the thread handles all its own errors, so there's no need to have error
394 // handling here. However it's a good idea to update the status of the GUI to reflect
395 // the new (lack of) thread status, so this posts a message to the App to do so.
396 void AppCoreThread::OnCleanupInThread()
397 {
398 m_ExecMode = ExecMode_Closing;
399 PostCoreStatus( CoreThread_Stopped );
400 _parent::OnCleanupInThread();
401 }
402
403 void AppCoreThread::VsyncInThread()
404 {
405 wxGetApp().LogicalVsync();
406 _parent::VsyncInThread();
407 }
408
409 void AppCoreThread::GameStartingInThread()
410 {
411 // Simulate a Close/Resume, so that settings get re-applied and the database
412 // lookups and other game-based detections are done.
413
414 m_ExecMode = ExecMode_Paused;
415 OnResumeReady();
416 _reset_stuff_as_needed();
417 m_ExecMode = ExecMode_Opened;
418
419 _parent::GameStartingInThread();
420 }
421
422 bool AppCoreThread::StateCheckInThread()
423 {
424 return _parent::StateCheckInThread();
425 }
426
427 void AppCoreThread::UploadStateCopy( const VmStateBuffer& copy )
428 {
429 ScopedCoreThreadPause paused_core;
430 _parent::UploadStateCopy( copy );
431 paused_core.AllowResume();
432 }
433
434 static uint m_except_threshold = 0;
435
436 void AppCoreThread::ExecuteTaskInThread()
437 {
438 PostCoreStatus( CoreThread_Started );
439 m_except_threshold = 0;
440 _parent::ExecuteTaskInThread();
441 }
442
443 void AppCoreThread::DoCpuExecute()
444 {
445 try {
446 _parent::DoCpuExecute();
447 }
448 catch (BaseR5900Exception& ex)
449 {
450 Console.Error( ex.FormatMessage() );
451
452 // [TODO] : Debugger Hook!
453
454 if( ++m_except_threshold > 6 )
455 {
456 // If too many TLB Misses occur, we're probably going to crash and
457 // the game is probably running miserably.
458
459 m_except_threshold = 0;
460 //Suspend();
461
462 // [TODO] Issue error dialog to the user here...
463 Console.Error( "Too many execution errors. VM execution has been suspended!" );
464
465 // Hack: this keeps the EE thread from running more code while the SysExecutor
466 // thread catches up and signals it for suspension.
467 m_ExecMode = ExecMode_Closing;
468 }
469 }
470 }
471
472 // --------------------------------------------------------------------------------------
473 // BaseSysExecEvent_ScopedCore / SysExecEvent_CoreThreadClose / SysExecEvent_CoreThreadPause
474 // --------------------------------------------------------------------------------------
475 void BaseSysExecEvent_ScopedCore::_post_and_wait( IScopedCoreThread& core )
476 {
477 DoScopedTask();
478
479 ScopedLock lock( m_mtx_resume );
480 PostResult();
481
482 if( m_resume )
483 {
484 // If the sender of the message requests a non-blocking resume, then we need
485 // to deallocate the m_sync object, since the sender will likely leave scope and
486 // invalidate it.
487 switch( m_resume->WaitForResult() )
488 {
489 case ScopedCore_BlockingResume:
490 if( m_sync ) m_sync->ClearResult();
491 core.AllowResume();
492 break;
493
494 case ScopedCore_NonblockingResume:
495 m_sync = NULL;
496 core.AllowResume();
497 break;
498
499 case ScopedCore_SkipResume:
500 m_sync = NULL;
501 break;
502 }
503 }
504 }
505
506
507 void SysExecEvent_CoreThreadClose::InvokeEvent()
508 {
509 ScopedCoreThreadClose closed_core;
510 _post_and_wait(closed_core);
511 closed_core.AllowResume();
512 }
513
514
515 void SysExecEvent_CoreThreadPause::InvokeEvent()
516 {
517 #ifdef PCSX2_DEVBUILD
518 bool CorePluginsAreOpen = GetCorePlugins().AreOpen();
519 ScopedCoreThreadPause paused_core;
520 _post_and_wait(paused_core);
521
522 // All plugins should be initialized and opened upon resuming from
523 // a paused state. If the thread that puased us changed plugin status, it should
524 // have used Close instead.
525 if( CorePluginsAreOpen )
526 {
527 CorePluginsAreOpen = GetCorePlugins().AreOpen();
528 pxAssumeDev( CorePluginsAreOpen, "Invalid plugin close/shutdown detected during paused CoreThread; please Stop/Suspend the core instead." );
529 }
530 paused_core.AllowResume();
531
532 #else
533
534 ScopedCoreThreadPause paused_core;
535 _post_and_wait(paused_core);
536 paused_core.AllowResume();
537
538 #endif
539 }
540
541
542 // --------------------------------------------------------------------------------------
543 // ScopedCoreThreadClose / ScopedCoreThreadPause
544 // --------------------------------------------------------------------------------------
545
546 static DeclareTls(bool) ScopedCore_IsPaused = false;
547 static DeclareTls(bool) ScopedCore_IsFullyClosed = false;
548
549 BaseScopedCoreThread::BaseScopedCoreThread()
550 {
551 //AffinityAssert_AllowFrom_MainUI();
552
553 m_allowResume = false;
554 m_alreadyStopped = false;
555 m_alreadyScoped = false;
556 }
557
558 BaseScopedCoreThread::~BaseScopedCoreThread() throw()
559 {
560 }
561
562 // Allows the object to resume execution upon object destruction. Typically called as the last thing
563 // in the object's scope. Any code prior to this call that causes exceptions will not resume the emulator,
564 // which is *typically* the intended behavior when errors occur.
565 void BaseScopedCoreThread::AllowResume()
566 {
567 m_allowResume = true;
568 }
569
570 void BaseScopedCoreThread::DisallowResume()
571 {
572 m_allowResume = false;
573 }
574
575 void BaseScopedCoreThread::DoResume()
576 {
577 if( m_alreadyStopped ) return;
578 if( !GetSysExecutorThread().IsSelf() )
579 {
580 //DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
581 m_sync_resume.PostResult( m_allowResume ? ScopedCore_NonblockingResume : ScopedCore_SkipResume );
582 m_mtx_resume.Wait();
583 }
584 else
585 CoreThread.Resume();
586 }
587
588 // Returns TRUE if the event is posted to the SysExecutor.
589 // Returns FALSE if the thread *is* the SysExecutor (no message is posted, calling code should
590 // handle the code directly).
591 bool BaseScopedCoreThread::PostToSysExec( BaseSysExecEvent_ScopedCore* msg )
592 {
593 ScopedPtr<BaseSysExecEvent_ScopedCore> smsg( msg );
594 if( !smsg || GetSysExecutorThread().IsSelf()) return false;
595
596 msg->SetSyncState(m_sync);
597 msg->SetResumeStates(m_sync_resume, m_mtx_resume);
598
599 GetSysExecutorThread().PostEvent( smsg.DetachPtr() );
600 m_sync.WaitForResult();
601 m_sync.RethrowException();
602
603 return true;
604 }
605
606 ScopedCoreThreadClose::ScopedCoreThreadClose()
607 {
608 if( ScopedCore_IsFullyClosed )
609 {
610 // tracks if we're already in scope or not.
611 m_alreadyScoped = true;
612 return;
613 }
614
615 if( !PostToSysExec(new SysExecEvent_CoreThreadClose()) )
616 {
617 if( !(m_alreadyStopped = CoreThread.IsClosed()) )
618 CoreThread.Suspend();
619 }
620
621 ScopedCore_IsFullyClosed = true;
622 }
623
624 ScopedCoreThreadClose::~ScopedCoreThreadClose() throw()
625 {
626 if( m_alreadyScoped ) return;
627 _parent::DoResume();
628 ScopedCore_IsFullyClosed = false;
629 }
630
631 ScopedCoreThreadPause::ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me )
632 {
633 if( ScopedCore_IsFullyClosed || ScopedCore_IsPaused )
634 {
635 // tracks if we're already in scope or not.
636 m_alreadyScoped = true;
637 return;
638 }
639
640 if( !abuse_me ) abuse_me = new SysExecEvent_CoreThreadPause();
641 if( !PostToSysExec( abuse_me ) )
642 {
643 if( !(m_alreadyStopped = CoreThread.IsPaused()) )
644 CoreThread.Pause();
645 }
646
647 ScopedCore_IsPaused = true;
648 }
649
650 ScopedCoreThreadPause::~ScopedCoreThreadPause() throw()
651 {
652 if( m_alreadyScoped ) return;
653 _parent::DoResume();
654 ScopedCore_IsPaused = false;
655 }
656
657 ScopedCoreThreadPopup::ScopedCoreThreadPopup()
658 {
659 // The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to
660 // ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window
661 // is maximized or fullscreen.
662
663 if( !GSopen2 )
664 m_scoped_core = new ScopedCoreThreadClose();
665 else
666 m_scoped_core = new ScopedCoreThreadPause();
667 };
668
669 void ScopedCoreThreadPopup::AllowResume()
670 {
671 if( m_scoped_core ) m_scoped_core->AllowResume();
672 }
673
674 void ScopedCoreThreadPopup::DisallowResume()
675 {
676 if( m_scoped_core ) m_scoped_core->DisallowResume();
677 }

  ViewVC Help
Powered by ViewVC 1.1.22