Files
pcsx2/common/src/Utilities/wxAppWithHelpers.cpp
T
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

696 lines
19 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 "wxAppWithHelpers.h"
#include "ThreadingInternal.h"
#include "PersistentThread.h"
DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
DEFINE_EVENT_TYPE( pxEvt_DeleteThread );
DEFINE_EVENT_TYPE( pxEvt_StartIdleEventTimer );
DEFINE_EVENT_TYPE( pxEvt_InvokeAction );
DEFINE_EVENT_TYPE( pxEvt_SynchronousCommand );
IMPLEMENT_DYNAMIC_CLASS( pxSimpleEvent, wxEvent )
ConsoleLogSource_App::ConsoleLogSource_App()
{
static const TraceLogDescriptor myDesc =
{
L"AppEvents", L"App Events",
pxLt("Includes idle event processing and some other uncommon event usages.")
};
m_Descriptor = &myDesc;
}
ConsoleLogSource_App pxConLog_App;
void BaseDeletableObject::DoDeletion()
{
wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers );
pxAssert( app != NULL );
app->DeleteObject( *this );
}
// --------------------------------------------------------------------------------------
// SynchronousActionState Implementations
// --------------------------------------------------------------------------------------
void SynchronousActionState::SetException( const BaseException& ex )
{
m_exception = ex.Clone();
}
void SynchronousActionState::SetException( BaseException* ex )
{
if( !m_posted )
{
m_exception = ex;
}
else if( wxTheApp )
{
// transport the exception to the main thread, since the message is fully
// asynchronous, or has already entered an asynchronous state. Message is sent
// as a non-blocking action since proper handling of user errors on async messages
// is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
// box to the user.
pxExceptionEvent ev( ex );
wxTheApp->AddPendingEvent( ev );
}
}
void SynchronousActionState::RethrowException() const
{
if( m_exception ) m_exception->Rethrow();
}
int SynchronousActionState::WaitForResult()
{
m_sema.WaitNoCancel();
RethrowException();
return return_value;
}
int SynchronousActionState::WaitForResult_NoExceptions()
{
m_sema.WaitNoCancel();
return return_value;
}
void SynchronousActionState::PostResult( int res )
{
return_value = res;
PostResult();
}
void SynchronousActionState::ClearResult()
{
m_posted = false;
m_exception = NULL;
}
void SynchronousActionState::PostResult()
{
if( m_posted ) return;
m_posted = true;
m_sema.Post();
}
// --------------------------------------------------------------------------------------
// pxActionEvent Implementations
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxActionEvent, wxEvent )
pxActionEvent::pxActionEvent( SynchronousActionState* sema, int msgtype )
: wxEvent( 0, msgtype )
{
m_state = sema;
}
pxActionEvent::pxActionEvent( SynchronousActionState& sema, int msgtype )
: wxEvent( 0, msgtype )
{
m_state = &sema;
}
pxActionEvent::pxActionEvent( const pxActionEvent& src )
: wxEvent( src )
{
m_state = src.m_state;
}
void pxActionEvent::SetException( const BaseException& ex )
{
SetException( ex.Clone() );
}
void pxActionEvent::SetException( BaseException* ex )
{
const wxString& prefix( pxsFmt(L"(%s) ", GetClassInfo()->GetClassName()) );
ex->DiagMsg() = prefix + ex->DiagMsg();
if( !m_state )
{
ScopedExcept exptr( ex ); // auto-delete it after handling.
ex->Rethrow();
}
m_state->SetException( ex );
}
// --------------------------------------------------------------------------------------
// pxSynchronousCommandEvent
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent )
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid)
: wxCommandEvent( pxEvt_SynchronousCommand, winid )
{
m_sync = sema;
m_realEvent = commandType;
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid)
: wxCommandEvent( pxEvt_SynchronousCommand )
{
m_sync = &sema;
m_realEvent = commandType;
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt )
: wxCommandEvent( evt )
{
m_sync = sema;
m_realEvent = evt.GetEventType();
SetEventType( pxEvt_SynchronousCommand );
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt )
: wxCommandEvent( evt )
{
m_sync = &sema;
m_realEvent = evt.GetEventType();
SetEventType( pxEvt_SynchronousCommand );
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src )
: wxCommandEvent( src )
{
m_sync = src.m_sync;
m_realEvent = src.m_realEvent;
}
void pxSynchronousCommandEvent::SetException( const BaseException& ex )
{
if( !m_sync ) ex.Rethrow();
m_sync->SetException( ex );
}
void pxSynchronousCommandEvent::SetException( BaseException* ex )
{
if( !m_sync )
{
ScopedExcept exptr( ex ); // auto-delete it after handling.
ex->Rethrow();
}
m_sync->SetException( ex );
}
// --------------------------------------------------------------------------------------
// pxRpcEvent
// --------------------------------------------------------------------------------------
// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
// invoked method is completed. If the method can be executed in non-blocking fashion then
// it should leave the semaphore postback NULL.
//
class pxRpcEvent : public pxActionEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxRpcEvent)
typedef pxActionEvent _parent;
protected:
void (*m_Method)();
public:
virtual ~pxRpcEvent() throw() { }
virtual pxRpcEvent *Clone() const { return new pxRpcEvent(*this); }
explicit pxRpcEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL )
: pxActionEvent( sema )
{
m_Method = method;
}
explicit pxRpcEvent( void (*method)(), SynchronousActionState& sema )
: pxActionEvent( sema )
{
m_Method = method;
}
pxRpcEvent( const pxRpcEvent& src )
: pxActionEvent( src )
{
m_Method = src.m_Method;
}
void SetMethod( void (*method)() )
{
m_Method = method;
}
protected:
void InvokeEvent()
{
if( m_Method ) m_Method();
}
};
IMPLEMENT_DYNAMIC_CLASS( pxRpcEvent, pxActionEvent )
// --------------------------------------------------------------------------------------
// pxExceptionEvent implementations
// --------------------------------------------------------------------------------------
pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
{
m_except = ex.Clone();
}
void pxExceptionEvent::InvokeEvent()
{
ScopedExcept deleteMe( m_except );
if( deleteMe ) deleteMe->Rethrow();
}
// --------------------------------------------------------------------------------------
// wxAppWithHelpers Implementation
// --------------------------------------------------------------------------------------
//
// TODO : Ping dispatch and IdleEvent dispatch can be unified into a single dispatch, which
// would mean checking only one list of events per idle event, instead of two. (ie, ping
// events can be appended to the idle event list, instead of into their own custom list).
//
IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp )
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void wxAppWithHelpers::PostMethod( FnType_Void* method )
{
PostEvent( pxRpcEvent( method ) );
}
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void wxAppWithHelpers::PostIdleMethod( FnType_Void* method )
{
pxRpcEvent evt( method );
AddIdleEvent( evt );
}
// Invokes the specified void method, or posts the method to the main thread if the calling
// thread is not Main. Action is blocking. For non-blocking method execution, use
// AppRpc_TryInvokeAsync.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
//
// Returns:
// FALSE if the method was not invoked (meaning this IS the main thread!)
// TRUE if the method was invoked.
//
bool wxAppWithHelpers::Rpc_TryInvoke( FnType_Void* method )
{
if( wxThread::IsMain() ) return false;
SynchronousActionState sync;
PostEvent( pxRpcEvent( method, sync ) );
sync.WaitForResult();
return true;
}
// Invokes the specified void method, or posts the method to the main thread if the calling
// thread is not Main. Action is non-blocking (asynchronous). For blocking method execution,
// use AppRpc_TryInvoke.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
//
// Returns:
// FALSE if the method was not posted to the main thread (meaning this IS the main thread!)
// TRUE if the method was posted.
//
bool wxAppWithHelpers::Rpc_TryInvokeAsync( FnType_Void* method )
{
if( wxThread::IsMain() ) return false;
PostEvent( pxRpcEvent( method ) );
return true;
}
void wxAppWithHelpers::ProcessMethod( FnType_Void* method )
{
if( wxThread::IsMain() )
{
method();
return;
}
SynchronousActionState sync;
PostEvent( pxRpcEvent( method, sync ) );
sync.WaitForResult();
}
void wxAppWithHelpers::PostEvent( const wxEvent& evt )
{
// Const Cast is OK!
// Truth is, AddPendingEvent should be a const-qualified parameter, as
// it makes an immediate clone copy of the event -- but wxWidgets
// fails again in structured C/C++ design design. So I'm forcing it as such
// here. -- air
_parent::AddPendingEvent( const_cast<wxEvent&>(evt) );
}
bool wxAppWithHelpers::ProcessEvent( wxEvent& evt )
{
// Note: We can't do an automatic blocking post of the message here, because wxWidgets
// isn't really designed for it (some events return data to the caller via the event
// struct, and posting the event would require a temporary clone, where changes would
// be lost).
AffinityAssert_AllowFrom_MainUI();
return _parent::ProcessEvent( evt );
}
bool wxAppWithHelpers::ProcessEvent( wxEvent* evt )
{
AffinityAssert_AllowFrom_MainUI();
ScopedPtr<wxEvent> deleteMe( evt );
return _parent::ProcessEvent( *deleteMe );
}
bool wxAppWithHelpers::ProcessEvent( pxActionEvent& evt )
{
if( wxThread::IsMain() )
return _parent::ProcessEvent( evt );
else
{
SynchronousActionState sync;
evt.SetSyncState( sync );
AddPendingEvent( evt );
sync.WaitForResult();
return true;
}
}
bool wxAppWithHelpers::ProcessEvent( pxActionEvent* evt )
{
if( wxThread::IsMain() )
{
ScopedPtr<wxEvent> deleteMe( evt );
return _parent::ProcessEvent( *deleteMe );
}
else
{
SynchronousActionState sync;
evt->SetSyncState( sync );
AddPendingEvent( *evt );
sync.WaitForResult();
return true;
}
}
void wxAppWithHelpers::CleanUp()
{
// I'm pretty sure the message pump is dead by now, which means we need to run through
// idle event list by hand and process the pending Deletion messages (all others can be
// ignored -- it's only deletions we want handled, and really we *could* ignore those too
// but I like to be tidy. -- air
//IdleEventDispatcher( "CleanUp" );
//DeletionDispatcher();
_parent::CleanUp();
}
// Executes the event with exception handling. If the event throws an exception, the exception
// will be neatly packaged and transported back to the thread that posted the event.
// This function is virtual, however overloading it is not recommended. Derrived classes
// should overload InvokeEvent() instead.
void pxActionEvent::_DoInvokeEvent()
{
AffinityAssert_AllowFrom_MainUI();
try {
InvokeEvent();
}
catch( BaseException& ex )
{
SetException( ex );
}
catch( std::runtime_error& ex )
{
SetException( new Exception::RuntimeError( ex ) );
}
if( m_state ) m_state->PostResult();
}
void wxAppWithHelpers::OnSynchronousCommand( pxSynchronousCommandEvent& evt )
{
AffinityAssert_AllowFrom_MainUI();
pxAppLog.Write(L"(App) Executing command event synchronously...");
evt.SetEventType( evt.GetRealEventType() );
try {
ProcessEvent( evt );
}
catch( BaseException& ex )
{
evt.SetException( ex );
}
catch( std::runtime_error& ex )
{
evt.SetException( new Exception::RuntimeError( ex, evt.GetClassInfo()->GetClassName() ) );
}
if( Semaphore* sema = evt.GetSemaphore() ) sema->Post();
}
void wxAppWithHelpers::AddIdleEvent( const wxEvent& evt )
{
ScopedLock lock( m_IdleEventMutex );
if( m_IdleEventQueue.size() == 0 )
PostEvent( pxSimpleEvent( pxEvt_StartIdleEventTimer ) );
m_IdleEventQueue.push_back( evt.Clone() );
}
void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt )
{
ScopedLock lock( m_IdleEventMutex );
if( m_IdleEventQueue.size() != 0 )
m_IdleEventTimer.Start( 100, true );
}
void wxAppWithHelpers::IdleEventDispatcher( const wxChar* action )
{
// Recursion is possible thanks to modal dialogs being issued from the idle event handler.
// (recursion shouldn't hurt anything anyway, since the node system re-creates the iterator
// on each pass)
//static int __guard=0;
//RecursionGuard guard(__guard);
//if( !pxAssertDev(!guard.IsReentrant(), "Re-entrant call to IdleEventdispatcher caught on camera!") ) return;
wxEventList postponed;
wxEventList::iterator node;
ScopedLock lock( m_IdleEventMutex );
while( node = m_IdleEventQueue.begin(), node != m_IdleEventQueue.end() )
{
ScopedPtr<wxEvent> deleteMe(*node);
m_IdleEventQueue.erase( node );
lock.Release();
if( !Threading::AllowDeletions() && (deleteMe->GetEventType() == pxEvt_DeleteThread) )
{
// Threads that have active semaphores or mutexes (other threads are waiting on them) cannot
// be deleted because those mutex/sema objects will become invalid and cause the pending
// thread to crash. So we disallow deletions when those waits are in action, and continue
// to postpone the deletion of the thread until such time that it is safe.
pxThreadLog.Write( ((pxThread*)((wxCommandEvent*)deleteMe.GetPtr())->GetClientData())->GetName(), L"Deletion postponed due to mutex or semaphore dependency." );
postponed.push_back(deleteMe.DetachPtr());
}
else
{
pxAppLog.Write( L"(AppIdleQueue%s) Dispatching event '%s'", action, deleteMe->GetClassInfo()->GetClassName() );
ProcessEvent( *deleteMe ); // dereference to prevent auto-deletion by ProcessEvent
}
lock.Acquire();
}
m_IdleEventQueue = postponed;
if( m_IdleEventQueue.size() > 0 )
pxAppLog.Write( L"(AppIdleQueue%s) %d events postponed due to dependencies.", action, m_IdleEventQueue.size() );
}
void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
{
m_IdleEventTimer.Stop();
IdleEventDispatcher();
}
void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt )
{
IdleEventDispatcher( L"[Timeout]" );
}
void wxAppWithHelpers::Ping()
{
pxThreadLog.Write( pxGetCurrentThreadName().c_str(), L"App Event Ping Requested." );
SynchronousActionState sync;
pxActionEvent evt( sync );
AddIdleEvent( evt );
sync.WaitForResult();
}
void wxAppWithHelpers::PostCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
{
wxCommandEvent evt( evtType );
evt.SetClientData( clientData );
evt.SetInt( intParam );
evt.SetExtraLong( longParam );
evt.SetString( stringParam );
AddPendingEvent( evt );
}
void wxAppWithHelpers::PostCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
{
PostCommand( NULL, evtType, intParam, longParam, stringParam );
}
sptr wxAppWithHelpers::ProcessCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
{
SynchronousActionState sync;
pxSynchronousCommandEvent evt( sync, evtType );
evt.SetClientData( clientData );
evt.SetInt( intParam );
evt.SetExtraLong( longParam );
evt.SetString( stringParam );
AddPendingEvent( evt );
sync.WaitForResult();
return sync.return_value;
}
sptr wxAppWithHelpers::ProcessCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
{
return ProcessCommand( NULL, evtType, intParam, longParam, stringParam );
}
void wxAppWithHelpers::PostAction( const pxActionEvent& evt )
{
PostEvent( evt );
}
void wxAppWithHelpers::ProcessAction( pxActionEvent& evt )
{
if( !wxThread::IsMain() )
{
SynchronousActionState sync;
evt.SetSyncState( sync );
AddPendingEvent( evt );
sync.WaitForResult();
}
else
evt._DoInvokeEvent();
}
void wxAppWithHelpers::DeleteObject( BaseDeletableObject& obj )
{
pxAssert( !obj.IsBeingDeleted() );
wxCommandEvent evt( pxEvt_DeleteObject );
evt.SetClientData( (void*)&obj );
AddIdleEvent( evt );
}
void wxAppWithHelpers::DeleteThread( pxThread& obj )
{
pxThreadLog.Write(obj.GetName(), L"Scheduling for deletion...");
wxCommandEvent evt( pxEvt_DeleteThread );
evt.SetClientData( (void*)&obj );
AddIdleEvent( evt );
}
typedef void (wxEvtHandler::*pxInvokeActionEventFunction)(pxActionEvent&);
bool wxAppWithHelpers::OnInit()
{
#define pxActionEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeActionEventFunction, &func )
Connect( pxEvt_SynchronousCommand, pxSynchronousEventHandler (wxAppWithHelpers::OnSynchronousCommand) );
Connect( pxEvt_InvokeAction, pxActionEventHandler (wxAppWithHelpers::OnInvokeAction) );
Connect( pxEvt_StartIdleEventTimer, wxEventHandler (wxAppWithHelpers::OnStartIdleEventTimer) );
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
Connect( pxEvt_DeleteThread, wxCommandEventHandler (wxAppWithHelpers::OnDeleteThread) );
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
Connect( m_IdleEventTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnIdleEventTimeout) );
return _parent::OnInit();
}
void wxAppWithHelpers::OnInvokeAction( pxActionEvent& evt )
{
evt._DoInvokeEvent(); // wow this is easy!
}
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )
{
if( evt.GetClientData() == NULL ) return;
delete (BaseDeletableObject*)evt.GetClientData();
}
// Threads have their own deletion handler that propagates exceptions thrown by the thread to the UI.
// (thus we have a fairly automatic threaded exception system!)
void wxAppWithHelpers::OnDeleteThread( wxCommandEvent& evt )
{
ScopedPtr<pxThread> thr( (pxThread*)evt.GetClientData() );
if( !thr )
{
pxThreadLog.Write( L"null", L"OnDeleteThread: NULL thread object received (and ignored)." );
return;
}
pxThreadLog.Write(thr->GetName(), (wxString)L"Thread object deleted successfully" + (thr->HasPendingException() ? L" [exception pending!]" : wxEmptyString));
thr->RethrowException();
}
wxAppWithHelpers::wxAppWithHelpers()
: m_IdleEventTimer( this )
{
#ifdef __WXMSW__
// This variable assignment ensures that MSVC links in the TLS setup stubs even in
// full optimization builds. Without it, DLLs that use TLS won't work because the
// FS segment register won't have been initialized by the main exe, due to tls_insurance
// being optimized away >_< --air
static __threadlocal int tls_insurance = 0;
tls_insurance = 1;
#endif
}