Files
archived-pcsx2/pcsx2/System/SysThreadBase.cpp
cottonvibes c05dc759e3 Big gif transfer code rewrite!
Pcsx2 now has a gifUnit class which acts like the ps2's gif and executes a single gif transfer at a time (and performs path arbitration according to priority and path3 slicing).

This new code is generally a speedup for most games. Particularly VU heavy games like GoW.
This revision breaks old saved state compatibility so don't update if you wish to keep playing with your old saved states.
Leave comments if this revision fixes or breaks anything...

Message to GS Plugin authors:
This new code now uses only 1 gif callback to transfer data to the gs plugin (GSgifTransfer).
pcsx2 also is garaunteed to send full GS primitives to the plugin. So you don't have to account for partial-transfers anymore.

Thanks goes out to shadowlady who tested around 500 games for me :D

Note 1: The old gif code is still in this revision, and can be enabled by the USE_OLD_GIF macro. The old code will be deleted soon.
Note 2: This revision also enables assertion dialogs in devel builds, and changed a lot of assume cases into assertions.


git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4821 96395faa-99c1-11dd-bbfe-3dabce05a288
2011-07-24 13:02:50 +00:00

307 lines
9.3 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "System.h"
#include "SysThreads.h"
// --------------------------------------------------------------------------------------
// SysThreadBase *External Thread* Implementations
// (Called form outside the context of this thread)
// --------------------------------------------------------------------------------------
SysThreadBase::SysThreadBase() :
m_ExecMode( ExecMode_NoThreadYet )
, m_ExecModeMutex()
{
}
SysThreadBase::~SysThreadBase() throw()
{
}
void SysThreadBase::Start()
{
_parent::Start();
Sleep( 1 );
pxAssertDev( (m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed),
"Unexpected thread status during SysThread startup."
);
m_sem_event.Post();
}
void SysThreadBase::OnStart()
{
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
m_sem_Resume.Reset();
m_sem_ChangingExecMode.Reset();
FrankenMutex( m_ExecModeMutex );
FrankenMutex( m_RunningLock );
_parent::OnStart();
}
// Suspends emulation and closes the emulation state (including plugins) at the next PS2 vsync,
// and returns control to the calling thread; or does nothing if the core is already suspended.
//
// Parameters:
// isNonblocking - if set to true then the function will not block for emulation suspension.
// Defaults to false if parameter is not specified. Performing non-blocking suspension
// is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness).
//
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// suspended.
//
// Exceptions:
// CancelEvent - thrown if the thread is already in a Paused or Closing state. Because
// actions that pause emulation typically rely on plugins remaining loaded/active,
// Suspension must cancel itself forcefully or risk crashing whatever other action is
// in progress.
//
void SysThreadBase::Suspend( bool isBlocking )
{
if (!pxAssertDev(!IsSelf(),"Suspend/Resume are not allowed from this thread.")) return;
if (!IsRunning()) return;
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
if( m_ExecMode == ExecMode_Closed ) return;
{
ScopedLock locker( m_ExecModeMutex );
switch( m_ExecMode )
{
// Check again -- status could have changed since above.
case ExecMode_Closed: return;
case ExecMode_Pausing:
case ExecMode_Paused:
if( !isBlocking )
throw Exception::CancelEvent( L"Cannot suspend in non-blocking fashion: Another thread is pausing the VM state." );
m_ExecMode = ExecMode_Closing;
m_sem_Resume.Post();
m_sem_ChangingExecMode.Wait();
break;
case ExecMode_Opened:
m_ExecMode = ExecMode_Closing;
break;
}
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
m_sem_event.Post();
}
if( isBlocking )
m_RunningLock.Wait();
}
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// closed, not running, or paused.
//
void SysThreadBase::Pause()
{
if( IsSelf() || !IsRunning() ) return;
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
{
ScopedLock locker( m_ExecModeMutex );
// Check again -- status could have changed since above.
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
if( m_ExecMode == ExecMode_Opened )
m_ExecMode = ExecMode_Pausing;
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
m_sem_event.Post();
}
m_RunningLock.Wait();
}
// Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates.
//
// Note that this is considered a non-blocking action. Most times the state is safely resumed
// on return, but in the case of re-entrant or nested message handling the function may return
// before the thread has resumed. If you need explicit behavior tied to the completion of the
// Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
//
// Exceptions:
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
// on the first time the thread is resumed from it's initial idle state)
// ThreadCreationError - Insufficient system resources to create thread.
//
void SysThreadBase::Resume()
{
if( IsSelf() ) return;
if( m_ExecMode == ExecMode_Opened ) return;
ScopedLock locker( m_ExecModeMutex );
// Implementation Note:
// The entire state coming out of a Wait is indeterminate because of user input
// and pending messages being handled. So after each call we do some seemingly redundant
// sanity checks against m_ExecMode/m_Running status, and if something doesn't feel
// right, we should abort; the user may have canceled the action before it even finished.
switch( m_ExecMode )
{
case ExecMode_Opened: return;
case ExecMode_NoThreadYet:
{
Start();
if( !m_running || (m_ExecMode == ExecMode_NoThreadYet) )
throw Exception::ThreadCreationError(this);
if( m_ExecMode == ExecMode_Opened ) return;
}
// fall through...
case ExecMode_Closing:
case ExecMode_Pausing:
// we need to make sure and wait for the emuThread to enter a fully suspended
// state before continuing...
m_RunningLock.Wait();
if( !m_running ) return;
if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
if( !GetCorePlugins().AreLoaded() ) return;
break;
}
pxAssertDev( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused),
"SysThreadBase is not in a closed/paused state? wtf!" );
OnResumeReady();
m_ExecMode = ExecMode_Opened;
m_sem_Resume.Post();
}
// --------------------------------------------------------------------------------------
// SysThreadBase *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
void SysThreadBase::OnStartInThread()
{
m_RunningLock.Acquire();
_parent::OnStartInThread();
m_ExecMode = ExecMode_Closing;
}
void SysThreadBase::OnCleanupInThread()
{
m_ExecMode = ExecMode_NoThreadYet;
_parent::OnCleanupInThread();
m_RunningLock.Release();
}
void SysThreadBase::OnSuspendInThread() {}
void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
// Tests for Pause and Suspend/Close requests. If the thread is trying to be paused or
// closed, it will enter a wait/holding pattern here in this method until the managing
// thread releases it. Use the return value to detect if changes to the thread's state
// may have been changed (based on the rule that other threads are not allowed to modify
// this thread's state without pausing or closing it first, to prevent race conditions).
//
// Return value:
// TRUE if the thread was paused or closed; FALSE if the thread
// continued execution unimpeded.
bool SysThreadBase::StateCheckInThread()
{
switch( m_ExecMode )
{
#ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
case ExecMode_NoThreadYet:
// threads should never have this state set while the thread is in any way
// active or alive. (for obvious reasons!!)
pxFailDev( "Invalid execution state detected." );
return false;
#endif
case ExecMode_Opened:
// Other cases don't need TestCancel() because its built into the various
// threading wait/signal actions.
TestCancel();
return false;
// -------------------------------------
case ExecMode_Pausing:
{
OnPauseInThread();
m_ExecMode = ExecMode_Paused;
m_RunningLock.Release();
}
// fallthrough...
case ExecMode_Paused:
while( m_ExecMode == ExecMode_Paused )
m_sem_Resume.WaitWithoutYield();
m_RunningLock.Acquire();
if( m_ExecMode != ExecMode_Closing )
{
OnResumeInThread( false );
break;
}
m_sem_ChangingExecMode.Post();
// fallthrough if we're switching to closing state...
// -------------------------------------
case ExecMode_Closing:
{
OnSuspendInThread();
m_ExecMode = ExecMode_Closed;
m_RunningLock.Release();
}
// fallthrough...
case ExecMode_Closed:
while( m_ExecMode == ExecMode_Closed )
m_sem_Resume.WaitWithoutYield();
m_RunningLock.Acquire();
OnResumeInThread( true );
break;
jNO_DEFAULT;
}
return true;
}