* Improve GUI responsiveness while doing things like enumerating/loading plugins.

* ConsoleLogger Optimization: Improved performance considerably for when the log gets spammed by EEcore or MTGS threads.

DevNotes:
 * Added a new pxYieldToMain() method (currently implemented in Win32 only).  It provides a smart way to allow passive-task worker threads to yield time to the Main Thread when important messages are pending (keyboard or mouse clicks, primarily).  It also replaces the wxGetApp().Ping() stuff, which never worked anyway due to wxCommandEvents having a higher priority than native system messages.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2253 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-11-26 03:37:10 +00:00
parent 35c78ae660
commit 03449cf682
18 changed files with 354 additions and 272 deletions

View File

@ -529,10 +529,6 @@
RelativePath="..\..\include\Utilities\win_memzero.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\WinVersion.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\wxBaseTools.h"
>

View File

@ -80,3 +80,5 @@ namespace HostSys
extern void InitCPUTicks();
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();
extern wxString GetOSVersionString();

View File

@ -124,6 +124,10 @@ namespace Exception
#endif
}
// Yields this thread against the main thread *if* the main thread's message pump has pending
// messages. If the main thread is idle then no yield is performed.
extern void pxYieldToMain();
namespace Threading
{
// --------------------------------------------------------------------------------------
@ -348,7 +352,7 @@ namespace Threading
pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads.
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
@ -394,7 +398,8 @@ namespace Threading
// Implemented by derived class to perform actual threaded task!
virtual void ExecuteTaskInThread()=0;
void TestCancel();
void TestCancel() const;
void YieldToMain() const;
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
@ -411,7 +416,7 @@ namespace Threading
Threading::Sleep( ms );
TestCancel();
}
void FrankenMutex( Mutex& mutex );
// ----------------------------------------------------------------------------

View File

@ -1,40 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 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/>.
*/
#pragma once
#include "RedtapeWindows.h"
#include <winnt.h>
namespace Win32
{
class RealVersionInfo
{
protected:
HMODULE m_kernel32;
wxString m_VersionString;
public:
~RealVersionInfo();
RealVersionInfo();
bool IsVista() const;
bool IsXP() const;
const wxString& GetVersionString() const;
protected:
void InitVersionString();
};
}

View File

@ -40,3 +40,14 @@ u64 GetCPUTicks()
gettimeofday(&t, NULL);
return ((u64)t.tv_sec*GetTickFrequency())+t.tv_usec;
}
wxString GetOSVersionString()
{
// TODO : Implement me!!
// This shoul return a single comprehensive string description for the linux operating system:
// Kernel version, distribution, etc.
return L"Linux Maybe?";
}

View File

@ -18,6 +18,12 @@
#include "Threading.h"
#include "x86emitter/tools.h"
#if !defined(__LINUX__) || !defined(__WXMAC__)
# pragma message( "LnxThreads.cpp should only be compiled by projects or makefiles targeted at Linux/Mac distros.")
#else
// Note: assuming multicore is safer because it forces the interlocked routines to use
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
// having the LOCK prefix is very bad indeed.
@ -44,3 +50,18 @@ __forceinline void Threading::EnableHiresScheduler()
__forceinline void Threading::DisableHiresScheduler()
{
}
void pxYieldToMain()
{
// Linux/GTK+ Implementation Notes:
// I have no idea if wxEventLoop::Pending() is thread safe or not, nor do I have
// any idea how to properly obtain the message queue status of GTK+. So let's
// just play dumb (and slow) and sleep for a couple milliseconds regardless, until
// a better fix is found. --air
// (FIXME : Find a more correct implementation for this?)
Sleep( 2 );
}
#endif

View File

@ -338,12 +338,18 @@ bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& ti
// function will throw an SEH exception designed to exit the thread (so make sure to use C++
// object encapsulation for anything that could leak resources, to ensure object unwinding
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
void Threading::PersistentThread::TestCancel()
void Threading::PersistentThread::TestCancel() const
{
pxAssert( IsSelf() );
pthread_testcancel();
}
void Threading::PersistentThread::YieldToMain() const
{
pxYieldToMain();
TestCancel();
}
// Executes the virtual member method
void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
{

View File

@ -12,14 +12,13 @@
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma comment(lib, "User32.lib")
#include "PrecompiledHeader.h"
#include "RedtapeWindows.h"
#include "WinVersion.h"
#include <ShTypes.h>
#include <shlwapi.h> // for IsOS()
static LARGE_INTEGER lfreq;
@ -40,147 +39,160 @@ u64 GetCPUTicks()
return count.QuadPart;
}
//////////////////////////////////////////////////////////////////////////////////////////
static const char* GetVersionName( DWORD minorVersion, DWORD majorVersion )
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
// Calculates the Windows OS Version and install information, and returns it as a
// human-readable string. :)
// (Handy function borrowed from Microsoft's MSDN Online, and reformatted to use wxString.)
wxString GetOSVersionString()
{
switch( majorVersion )
{
case 6:
return "Vista";
break;
wxString retval;
case 5:
switch( minorVersion )
{
case 0:
return "2000";
break;
case 1:
return "XP";
break;
case 2:
return "Server 2003";
break;
OSVERSIONINFOEX osvi;
SYSTEM_INFO si;
PGNSI pGNSI;
PGPI pGPI;
BOOL bOsVersionInfoEx;
DWORD dwType;
default:
return "2000/XP";
}
break;
case 4:
switch( minorVersion )
{
case 0:
return "95";
break;
case 10:
return "98";
break;
case 90:
return "ME";
break;
default:
return "95/98/ME";
}
break;
case 3:
return "32S";
break;
default:
return "Unknown";
}
}
memzero( si );
memzero( osvi );
void Win32::RealVersionInfo::InitVersionString()
{
DWORD version = GetVersion();
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
int majorVersion = (DWORD)(LOBYTE(LOWORD(version)));
int minorVersion = (DWORD)(HIBYTE(LOWORD(version)));
if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
return L"GetVersionEx Error!";
wxString verName( fromUTF8( GetVersionName( minorVersion, majorVersion ) ) );
// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
bool IsCompatMode = false;
if( IsVista() )
{
m_VersionString = L"Windows Vista";
if( verName != L"Vista" )
{
if( verName == L"Unknown" )
m_VersionString += L" or Newer";
else
IsCompatMode = true;
}
m_VersionString += wxsFormat( L" v%d.%d", majorVersion, minorVersion );
if( IsOS( OS_WOW6432 ) )
m_VersionString += L" (64-bit)";
else
m_VersionString += L" (32-bit)";
}
else if( IsXP() )
{
m_VersionString = wxsFormat( L"Windows XP v%d.%d", majorVersion, minorVersion );
if( IsOS( OS_WOW6432 ) )
m_VersionString += L" (64-bit)";
else
m_VersionString += L" (32-bit)";
if( verName != L"XP" )
IsCompatMode = true;
}
pGNSI = (PGNSI) GetProcAddress( GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo" );
if(NULL != pGNSI)
pGNSI( &si );
else
GetSystemInfo( &si );
if ( VER_PLATFORM_WIN32_NT!=osvi.dwPlatformId || osvi.dwMajorVersion <= 4 )
return L"Unsupported Operating System!";
retval += L"Microsoft ";
// Test for the specific product.
if ( osvi.dwMajorVersion == 6 )
{
m_VersionString = L"Windows " + verName;
if( osvi.dwMinorVersion == 0 )
retval += ( osvi.wProductType == VER_NT_WORKSTATION ) ? L"Windows Vista " : L"Windows Server 2008 ";
if ( osvi.dwMinorVersion == 1 )
retval += ( osvi.wProductType == VER_NT_WORKSTATION ) ? L"Windows 7 " : L"Windows Server 2008 R2 ";
pGPI = (PGPI) GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
switch( dwType )
{
case PRODUCT_ULTIMATE: retval += L"Ultimate Edition"; break;
case PRODUCT_HOME_PREMIUM: retval += L"Home Premium Edition"; break;
case PRODUCT_HOME_BASIC: retval += L"Home Basic Edition"; break;
case PRODUCT_ENTERPRISE: retval += L"Enterprise Edition"; break;
case PRODUCT_BUSINESS: retval += L"Business Edition"; break;
case PRODUCT_STARTER: retval += L"Starter Edition"; break;
case PRODUCT_CLUSTER_SERVER: retval += L"Cluster Server Edition"; break;
case PRODUCT_DATACENTER_SERVER: retval += L"Datacenter Edition"; break;
case PRODUCT_DATACENTER_SERVER_CORE: retval += L"Datacenter Edition (core installation)"; break;
case PRODUCT_ENTERPRISE_SERVER: retval += L"Enterprise Edition"; break;
case PRODUCT_ENTERPRISE_SERVER_CORE: retval += L"Enterprise Edition (core installation)"; break;
case PRODUCT_SMALLBUSINESS_SERVER: retval += L"Small Business Server"; break;
case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: retval += L"Small Business Server Premium Edition"; break;
case PRODUCT_STANDARD_SERVER: retval += L"Standard Edition"; break;
case PRODUCT_STANDARD_SERVER_CORE: retval += L"Standard Edition (core installation)"; break;
case PRODUCT_WEB_SERVER: retval += L"Web Server Edition"; break;
}
}
if( IsCompatMode )
m_VersionString += wxsFormat( L" [compatibility mode, running as Windows %s]", verName );
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
{
if( GetSystemMetrics(SM_SERVERR2) )
retval += L"Windows Server 2003 R2, ";
else if ( osvi.wSuiteMask==VER_SUITE_STORAGE_SERVER )
retval += L"Windows Storage Server 2003";
else if ( osvi.wSuiteMask==VER_SUITE_WH_SERVER )
retval += L"Windows Home Server";
else if( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
retval += L"Windows XP Professional x64 Edition";
else
retval += L"Windows Server 2003, ";
// Test for the server type.
if ( osvi.wProductType != VER_NT_WORKSTATION )
{
if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
{
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
retval += L"Datacenter x64 Edition";
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
retval += L"Enterprise x64 Edition";
else
retval += L"Standard x64 Edition";
}
else
{
if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
retval += L"Compute Cluster Edition";
else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
retval += L"Datacenter Edition";
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
retval += L"Enterprise Edition";
else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
retval += L"Web Edition";
else
retval += L"Standard Edition";
}
}
}
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
{
retval += L"Windows XP ";
retval += ( osvi.wSuiteMask & VER_SUITE_PERSONAL ) ? L"Professional" : L"Home Edition";
}
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
{
retval += L"Windows 2000 ";
if ( osvi.wProductType == VER_NT_WORKSTATION )
{
retval += L"Professional";
}
else
{
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
retval += L"Datacenter Server";
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
retval += L"Advanced Server";
else
retval += L"Server";
}
}
// Include service pack (if any) and build number.
if( _tcslen(osvi.szCSDVersion) > 0 )
retval += (wxString)L" " + osvi.szCSDVersion;
retval += wxsFormat( L" (build %d)", osvi.dwBuildNumber );
if ( osvi.dwMajorVersion >= 6 )
{
if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
retval += L", 64-bit";
else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
retval += L", 32-bit";
}
return retval;
}
Win32::RealVersionInfo::~RealVersionInfo()
{
if( m_kernel32 != NULL )
FreeLibrary( m_kernel32 );
}
Win32::RealVersionInfo::RealVersionInfo() :
m_kernel32( LoadLibrary( L"kernel32" ) )
{
InitVersionString();
}
bool Win32::RealVersionInfo::IsVista() const
{
if( m_kernel32 == NULL ) return false;
// CreateThreadpoolWait is a good pick -- it's not likely to exist on older OS's
// GetLocaleInfoEx is also good -- two picks are better than one, just in case
// one of the APIs becomes available in a future XP service pack.
return
( GetProcAddress( m_kernel32, "CreateThreadpoolWait" ) != NULL ) &&
( GetProcAddress( m_kernel32, "GetLocaleInfoEx" ) != NULL );
}
bool Win32::RealVersionInfo::IsXP() const
{
return ( GetProcAddress( m_kernel32, "GetNativeSystemInfo" ) != NULL );
}
const wxString& Win32::RealVersionInfo::GetVersionString() const
{
return m_VersionString;
}

View File

@ -18,6 +18,12 @@
#include "x86emitter/tools.h"
#include "Threading.h"
#ifndef __WXMSW__
# pragma message( "WinThreads.cpp should only be compiled by projects or makefiles targeted at Microsoft Windows.")
#else
__forceinline void Threading::Sleep( int ms )
{
::Sleep( ms );
@ -46,3 +52,26 @@ __forceinline void Threading::DisableHiresScheduler()
timeEndPeriod( 1 );
}
void pxYieldToMain()
{
// Windows Implementation Note:
// wxWidgets has a wxEventLoop::Pending() function, however it uses PeekMessage internally
// which is a multithreaded NO-NO. So instead we must use GetQueueStatus. This is ok
// anyway since I get extra fancy and scale the sleep duration based on the type of messages
// in the queue. User input (key and mouse button) cue longer worker thread yields since
// maintaining a responsive gui is typically a high priority.
DWORD result = GetQueueStatus( QS_ALLEVENTS );
uint hiword = HIWORD( result );
if( hiword == 0 ) return;
int sleepdur = 1;
if( (hiword & (QS_MOUSEBUTTON | QS_KEY)) != 0 )
sleepdur += 3;
Sleep( sleepdur );
}
#endif

View File

@ -108,20 +108,20 @@ void wxDialogWithHelpers::AddOkCancel( wxSizer &sizer, bool hasApply )
{
SetExtraStyle( wxDIALOG_EX_CONTEXTHELP );
#ifndef __WXMSW__
s_littles += new wxContextHelpButton(this) | StdButton();
m_extraButtonSizer += new wxContextHelpButton(this) | StdButton();
#endif
}
// create a sizer to hold the help and ok/cancel buttons, for platforms
// that need a custom help icon. [fixme: help icon prolly better off somewhere else]
wxFlexGridSizer& flex( *new wxFlexGridSizer( 2 ) );
flex.AddGrowableCol( 0, 1 );
flex.AddGrowableCol( 1, 15 );
flex += m_extraButtonSizer | pxAlignLeft;
flex += s_buttons | pxExpand, pxCenter;
flex += m_extraButtonSizer | pxAlignLeft;
flex += s_buttons | pxExpand, pxCenter;
sizer += flex | StdExpand();
sizer += flex | StdExpand();
s_buttons.Realize();
}

View File

@ -42,7 +42,6 @@ class AppCoreThread;
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 )
@ -408,7 +407,6 @@ public:
void PostPadKey( wxKeyEvent& evt );
void PostMenuAction( MenuIdentifiers menu_id ) const;
int ThreadedModalDialog( DialogIdentifiers dialogId );
void Ping() const;
bool PrepForExit( bool canCancel );
@ -480,7 +478,6 @@ protected:
void OnSysExecute( wxCommandEvent& evt );
void OnReloadPlugins( wxCommandEvent& evt );
void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadStatus( wxCommandEvent& evt );

View File

@ -240,7 +240,6 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );

View File

@ -32,7 +32,6 @@
IMPLEMENT_APP(Pcsx2App)
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_SysExecute );
@ -114,20 +113,6 @@ int Pcsx2App::ThreadedModalDialog( DialogIdentifiers dialogId )
return result.result;
}
// Waits for the main GUI thread to respond. If run from the main GUI thread, returns
// immediately without error. Use this on non-GUI threads to have them sleep until
// the GUI has processed all its pending messages.
void Pcsx2App::Ping() const
{
if( wxThread::IsMain() ) return;
Semaphore sema;
wxCommandEvent bean( pxEVT_SemaphorePing );
bean.SetClientData( &sema );
wxGetApp().AddPendingEvent( bean );
sema.WaitNoCancel();
}
// ----------------------------------------------------------------------------
// Pcsx2App Event Handlers
// ----------------------------------------------------------------------------
@ -143,11 +128,6 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
CoreThread.RethrowException();
}
void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt )
{
((Semaphore*)evt.GetClientData())->Post();
}
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
{
using namespace Dialogs;

View File

@ -246,12 +246,15 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
, m_QueueColorSection( L"ConsoleLog::QueueColorSection" )
, m_QueueBuffer( L"ConsoleLog::QueueBuffer" )
, m_CurQueuePos( false )
, m_threadlogger( EnableThreadedLoggingTest ? new ConsoleTestThread() : NULL )
, m_Listener_CoreThreadStatus ( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener ( this, OnCoreThreadStatusChanged ) )
, m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
{
m_CurQueuePos = 0;
m_pendingFlushes = 0;
m_WaitingThreadsForFlush = 0;
m_ThreadedLogInQueue = false;
m_ThawThrottle = 0;
m_ThawNeeded = false;
@ -330,6 +333,8 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
Connect( wxEVT_SetTitleText, wxCommandEventHandler(ConsoleLogFrame::OnSetTitle) );
Connect( wxEVT_DockConsole, wxCommandEventHandler(ConsoleLogFrame::OnDockedMove) );
Connect( wxEVT_FlushQueue, wxCommandEventHandler(ConsoleLogFrame::OnFlushEvent) );
//Connect( wxEVT_IDLE, wxIdleEventHandler(ConsoleLogFrame::OnIdleEvent) );
m_item_Deci2 ->Check( g_Conf->EmuOptions.Log.Deci2 );
m_item_StdoutEE ->Check( g_Conf->EmuOptions.Log.StdoutEE );
@ -385,25 +390,30 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
}
++m_pendingFlushes;
if( m_pendingFlushes > 24 && !wxThread::IsMain() )
if( !wxThread::IsMain() )
{
++m_WaitingThreadsForFlush;
lock.Release();
m_ThreadedLogInQueue = true;
if( !m_sem_QueueFlushed.Wait( wxTimeSpan( 0,0,0,500 ) ) )
if( m_pendingFlushes > 48 )
{
// Necessary since the main thread could grab the lock and process before
// the above function actually returns (gotta love threading!)
lock.Acquire();
if( m_WaitingThreadsForFlush != 0 ) --m_WaitingThreadsForFlush;
}
else
{
// give gui thread time to repaint and handle other pending messages.
// (those are prioritized lower than wxEvents, typically, which means we
// can't post a ping event since it'll still just starve out paint msgs.)
Sleep(1);
++m_WaitingThreadsForFlush;
lock.Release();
if( !m_sem_QueueFlushed.Wait( wxTimeSpan( 0,0,0,500 ) ) )
{
// Necessary since the main thread could grab the lock and process before
// the above function actually returns (gotta love threading!)
lock.Acquire();
if( m_WaitingThreadsForFlush != 0 ) --m_WaitingThreadsForFlush;
}
else
{
// give gui thread time to repaint and handle other pending messages.
// (those are prioritized lower than wxEvents, typically, which means we
// can't post a ping event since it'll still just starve out paint msgs.)
pxYieldToMain();
}
}
}
}
@ -561,25 +571,68 @@ void ConsoleLogFrame::OnSetTitle( wxCommandEvent& event )
SetTitle( event.GetString() );
}
void ConsoleLogFrame::OnIdleEvent( wxIdleEvent& evt )
{
// bah, wx's Idle Events are the most worthless crap.
//::SendMessage((HWND)m_TextCtrl.GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
}
void __evt_fastcall ConsoleLogFrame::OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt )
{
#ifdef __WXMSW__
if( obj == NULL ) return;
ConsoleLogFrame* mframe = (ConsoleLogFrame*)obj;
// WM_VSCROLL makes the scrolling 'smooth' (such that the last line of the log contents
// are always displayed as the last line of the log window). Unfortunately this also
// makes logging very slow, so we only send the message for status changes, so that the
// log aligns itself nicely when we pause emulation or when errors occur.
::SendMessage((HWND)mframe->m_TextCtrl.GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
#endif
}
void __evt_fastcall ConsoleLogFrame::OnCorePluginStatusChanged( void* obj, PluginEventType& evt )
{
#ifdef __WXMSW__
if( obj == NULL ) return;
ConsoleLogFrame* mframe = (ConsoleLogFrame*)obj;
// WM_VSCROLL makes the scrolling 'smooth' (such that the last line of the log contents
// are always displayed as the last line of the log window). Unfortunately this also
// makes logging very slow, so we only send the message for status changes, so that the
// log aligns itself nicely when we pause emulation or when errors occur.
::SendMessage((HWND)mframe->m_TextCtrl.GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
#endif
}
void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& evt )
{
ScopedLock locker( m_QueueLock );
if( m_CurQueuePos != 0 )
{
if( m_ThreadedLogInQueue && (m_pendingFlushes < 20) )
{
// Hacky Speedup -->
// Occasionally the EEcore thread can send ups some serious amounts of spam, and
// if we don't sleep the main thread some, the stupid text control refresh will
// drive framerates toward zero as it tries to refresh for every single log.
// This hack checks if a thread has been posting logs and, if so, we "rest" the
// main thread so that other threads can accumulate a more sizable log chunk.
locker.Release();
Sleep( 2 );
locker.Acquire();
}
DoFlushQueue();
#ifdef __WXMSW__
// This nicely sets the scroll position to the end of our log window, regardless of if
// the textctrl has focus or not. The wxWidgets AppendText() function uses EM_LINESCROLL
// instead, which tends to be much faster for high-volume logs, but also ends up refreshing
// the console in sloppy fashion for normal logging.
// (both are needed, the WM_VSCROLL makes the scrolling smooth, and the EM_LINESCROLL avoids
// weird errors when the buffer reaches "max" and starts clearing old history)
// EM_LINESCROLL avoids weird errors when the buffer reaches "max" and starts
// clearing old history:
::SendMessage((HWND)m_TextCtrl.GetHWND(), EM_LINESCROLL, 0, 0xfffffff);
::SendMessage((HWND)m_TextCtrl.GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
#endif
//m_TextCtrl.Thaw();
}
@ -653,9 +706,10 @@ void ConsoleLogFrame::DoFlushQueue()
// +1 / -1 ). This works better for some reason:
m_TextCtrl.SetInsertionPointEnd(); //( insertPoint );
m_CurQueuePos = 0;
m_QueueColorSection.Clear();
m_pendingFlushes = 0;
m_CurQueuePos = 0;
m_pendingFlushes = 0;
m_ThreadedLogInQueue= false;
}
ConsoleLogFrame* Pcsx2App::GetProgramLog()

View File

@ -162,6 +162,8 @@ protected:
// This is a counter of the number of threads waiting for the Queue to flush.
volatile int m_WaitingThreadsForFlush;
volatile bool m_ThreadedLogInQueue;
// Used by threads waiting on the queue to flush.
Semaphore m_sem_QueueFlushed;
@ -179,6 +181,9 @@ protected:
// Current write position into the m_QueueBuffer;
int m_CurQueuePos;
CmdEvt_ListenerBinding m_Listener_CoreThreadStatus;
EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus;
// Threaded log spammer, useful for testing console logging performance.
// (alternatively you can enable Disasm logging in any recompiler and achieve
// a similar effect)
@ -229,5 +234,8 @@ protected:
void OnMoveAround( wxMoveEvent& evt );
void OnResize( wxSizeEvent& evt );
static void __evt_fastcall OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt );
static void __evt_fastcall OnCorePluginStatusChanged( void* obj, PluginEventType& evt );
};

View File

@ -239,33 +239,33 @@ static int GetPluginMenuId_Name( PluginsEnum_t pid )
}
// ------------------------------------------------------------------------
MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER) ),
MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
: wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER) )
m_statusbar( *CreateStatusBar(2, 0) ),
m_background( this, wxID_ANY, wxGetApp().GetLogoBitmap() ),
, m_statusbar( *CreateStatusBar(2, 0) )
, m_background( this, wxID_ANY, wxGetApp().GetLogoBitmap() )
// All menu components must be created on the heap!
m_menubar( *new wxMenuBar() ),
, m_menubar( *new wxMenuBar() )
m_menuBoot ( *new wxMenu() ),
m_menuCDVD ( *new wxMenu() ),
m_menuSys ( *new wxMenu() ),
m_menuConfig( *new wxMenu() ),
m_menuMisc ( *new wxMenu() ),
m_menuDebug ( *new wxMenu() ),
, m_menuBoot ( *new wxMenu() )
, m_menuCDVD ( *new wxMenu() )
, m_menuSys ( *new wxMenu() )
, m_menuConfig( *new wxMenu() )
, m_menuMisc ( *new wxMenu() )
, m_menuDebug ( *new wxMenu() )
m_LoadStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Load01 ) ),
m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) ),
, m_LoadStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Load01 ) )
, m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) )
m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ),
m_MenuItem_Console_Stdio( *new wxMenuItem( &m_menuMisc, MenuId_Console_Stdio, L"Console to Stdio", wxEmptyString, wxITEM_CHECK ) ),
, m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) )
, m_MenuItem_Console_Stdio( *new wxMenuItem( &m_menuMisc, MenuId_Console_Stdio, L"Console to Stdio", wxEmptyString, wxITEM_CHECK ) )
m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) ),
m_Listener_CorePluginStatus( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType>( this, OnCorePluginStatusChanged ) ),
m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ),
m_Listener_SettingsLoadSave( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface>( this, OnSettingsLoadSave ) )
, m_Listener_CoreThreadStatus ( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener ( this, OnCoreThreadStatusChanged ) )
, m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
, m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) )
, m_Listener_SettingsLoadSave ( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface> ( this, OnSettingsLoadSave ) )
{
for( int i=0; i<PluginId_Count; ++i )
m_PluginMenuPacks[i].Populate( (PluginsEnum_t)i );

View File

@ -553,7 +553,7 @@ Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master
void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
{
DbgCon.WriteLn( L"\tEnumerating Plugin: " + m_master.GetFilename( curidx ) );
DbgCon.Indent().WriteLn( L"Enumerating Plugin: " + m_master.GetFilename( curidx ) );
try
{
@ -589,14 +589,17 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
{
DevCon.WriteLn( "Plugin Enumeration Thread started..." );
wxGetApp().Ping(); // gives the gui thread some time to refresh
Yield( 3 );
YieldToMain();
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{
DoNextPlugin( curidx );
if( (curidx & 3) == 3 ) wxGetApp().Ping(); // gives the gui thread some time to refresh
TestCancel();
// speed isn't critical here, but the pretty status bar sure is.
// second try yield should give the status bars UI a "good" chance to refresh before we advance. :)
YieldToMain();
YieldToMain();
//Sleep(150); // uncomment this to slow down the selector, for debugging threading.
}

View File

@ -125,8 +125,7 @@ LoadPluginsTask::~LoadPluginsTask() throw()
void LoadPluginsTask::ExecuteTaskInThread()
{
wxGetApp().Ping();
Yield(3);
pxYieldToMain();
// This is for testing of the error handler... uncomment for fun?
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
@ -147,11 +146,11 @@ void LoadPluginsTask::OnCleanupInThread()
// SaveSinglePluginHelper (Implementations)
// --------------------------------------------------------------------------------------
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid ) :
m_plugstore( L"PluginConf Savestate" )
, m_whereitsat( NULL )
, m_resume( false )
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
: m_plugstore( L"PluginConf Savestate" )
{
m_whereitsat = NULL;
m_resume = false;
m_pid = pid;
m_validstate = SysHasValidState();