mirror of https://github.com/PCSX2/pcsx2.git
Brand new approach to console logging, should be a lot more efficient, and is relatively deadlock-free. Also fixes most of the scrolling issues from prev versions.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2044 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
f8783a77ab
commit
43bac9c6a9
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
enum ConsoleColors
|
enum ConsoleColors
|
||||||
{
|
{
|
||||||
|
Color_Current = -1,
|
||||||
|
|
||||||
Color_Black = 0,
|
Color_Black = 0,
|
||||||
Color_Red,
|
Color_Red,
|
||||||
Color_Green,
|
Color_Green,
|
||||||
|
@ -27,7 +29,7 @@ enum ConsoleColors
|
||||||
Color_Blue,
|
Color_Blue,
|
||||||
Color_Magenta,
|
Color_Magenta,
|
||||||
Color_Cyan,
|
Color_Cyan,
|
||||||
Color_White
|
Color_White,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use fastcall for the console; should be helpful in most cases
|
// Use fastcall for the console; should be helpful in most cases
|
||||||
|
|
|
@ -22,61 +22,58 @@
|
||||||
#include "implement.h" // win32 pthreads implementations.
|
#include "implement.h" // win32 pthreads implementations.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Threading
|
void Threading::CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU )
|
||||||
{
|
{
|
||||||
void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU )
|
DWORD vProcessCPUs;
|
||||||
|
DWORD vSystemCPUs;
|
||||||
|
|
||||||
|
x86caps.LogicalCores = 1;
|
||||||
|
|
||||||
|
if( !GetProcessAffinityMask (GetCurrentProcess (),
|
||||||
|
&vProcessCPUs, &vSystemCPUs) ) return;
|
||||||
|
|
||||||
|
int CPUs = 0;
|
||||||
|
DWORD bit;
|
||||||
|
|
||||||
|
for (bit = 1; bit != 0; bit <<= 1)
|
||||||
{
|
{
|
||||||
DWORD vProcessCPUs;
|
if (vSystemCPUs & bit)
|
||||||
DWORD vSystemCPUs;
|
CPUs++;
|
||||||
|
|
||||||
x86caps.LogicalCores = 1;
|
|
||||||
|
|
||||||
if( !GetProcessAffinityMask (GetCurrentProcess (),
|
|
||||||
&vProcessCPUs, &vSystemCPUs) ) return;
|
|
||||||
|
|
||||||
int CPUs = 0;
|
|
||||||
DWORD bit;
|
|
||||||
|
|
||||||
for (bit = 1; bit != 0; bit <<= 1)
|
|
||||||
{
|
|
||||||
if (vSystemCPUs & bit)
|
|
||||||
CPUs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
x86caps.LogicalCores = CPUs;
|
|
||||||
if( LogicalCoresPerPhysicalCPU > CPUs) // for 1-socket HTT-disabled machines
|
|
||||||
LogicalCoresPerPhysicalCPU = CPUs;
|
|
||||||
|
|
||||||
x86caps.PhysicalCores = ( CPUs / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU;
|
|
||||||
//ptw32_smp_system = ( x86caps.LogicalCores > 1 ) ? TRUE : FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void Sleep( int ms )
|
x86caps.LogicalCores = CPUs;
|
||||||
{
|
if( LogicalCoresPerPhysicalCPU > CPUs) // for 1-socket HTT-disabled machines
|
||||||
::Sleep( ms );
|
LogicalCoresPerPhysicalCPU = CPUs;
|
||||||
}
|
|
||||||
|
|
||||||
// For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory
|
x86caps.PhysicalCores = ( CPUs / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU;
|
||||||
// improve performance and reduce cpu power consumption.
|
//ptw32_smp_system = ( x86caps.LogicalCores > 1 ) ? TRUE : FALSE;
|
||||||
__forceinline void SpinWait()
|
}
|
||||||
{
|
|
||||||
__asm pause;
|
__forceinline void Threading::Sleep( int ms )
|
||||||
}
|
{
|
||||||
|
::Sleep( ms );
|
||||||
__forceinline void EnableHiresScheduler()
|
}
|
||||||
{
|
|
||||||
// This improves accuracy of Sleep() by some amount, and only adds a negligable amount of
|
// For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory
|
||||||
// overhead on modern CPUs. Typically desktops are already set pretty low, but laptops in
|
// improve performance and reduce cpu power consumption.
|
||||||
// particular may have a scheduler Period of 15 or 20ms to extend battery life.
|
__forceinline void Threading::SpinWait()
|
||||||
|
{
|
||||||
// (note: this same trick is used by most multimedia software and games)
|
__asm pause;
|
||||||
|
}
|
||||||
timeBeginPeriod( 1 );
|
|
||||||
}
|
__forceinline void Threading::EnableHiresScheduler()
|
||||||
|
{
|
||||||
__forceinline void DisableHiresScheduler()
|
// This improves accuracy of Sleep() by some amount, and only adds a negligable amount of
|
||||||
{
|
// overhead on modern CPUs. Typically desktops are already set pretty low, but laptops in
|
||||||
timeEndPeriod( 1 );
|
// particular may have a scheduler Period of 15 or 20ms to extend battery life.
|
||||||
}
|
|
||||||
|
// (note: this same trick is used by most multimedia software and games)
|
||||||
|
|
||||||
|
timeBeginPeriod( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline void Threading::DisableHiresScheduler()
|
||||||
|
{
|
||||||
|
timeEndPeriod( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,18 @@ bool SysThreadBase::Suspend( bool isBlocking )
|
||||||
m_sem_event.Post();
|
m_sem_event.Post();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isBlocking ) m_RunningLock.Wait();
|
if( isBlocking )
|
||||||
|
{
|
||||||
|
if( !m_RunningLock.Wait( wxTimeSpan( 0,0,3,0 ) ) )
|
||||||
|
{
|
||||||
|
// [TODO] : Implement proper deadlock handler here that lets the user continue
|
||||||
|
// to wait, or issue a cancel to the thread.
|
||||||
|
|
||||||
|
throw Exception::ThreadTimedOut( L"Possible deadlock while suspending the " + m_name,
|
||||||
|
m_name + L" is not responding to suspend requests. It may be deadlocked or just running *really* slow."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -403,7 +403,6 @@ public:
|
||||||
// Console / Program Logging Helpers
|
// Console / Program Logging Helpers
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
ConsoleLogFrame* GetProgramLog();
|
ConsoleLogFrame* GetProgramLog();
|
||||||
void ProgramLog_CountMsg();
|
|
||||||
void ProgramLog_PostEvent( wxEvent& evt );
|
void ProgramLog_PostEvent( wxEvent& evt );
|
||||||
void EnableAllLogging() const;
|
void EnableAllLogging() const;
|
||||||
void DisableWindowLogging() const;
|
void DisableWindowLogging() const;
|
||||||
|
|
|
@ -27,14 +27,14 @@ BEGIN_DECLARE_EVENT_TYPES()
|
||||||
DECLARE_EVENT_TYPE(wxEVT_LOG_Write, -1)
|
DECLARE_EVENT_TYPE(wxEVT_LOG_Write, -1)
|
||||||
DECLARE_EVENT_TYPE(wxEVT_LOG_Newline, -1)
|
DECLARE_EVENT_TYPE(wxEVT_LOG_Newline, -1)
|
||||||
DECLARE_EVENT_TYPE(wxEVT_SetTitleText, -1)
|
DECLARE_EVENT_TYPE(wxEVT_SetTitleText, -1)
|
||||||
DECLARE_EVENT_TYPE(wxEVT_SemaphoreWait, -1)
|
DECLARE_EVENT_TYPE(wxEVT_FlushQueue, -1)
|
||||||
END_DECLARE_EVENT_TYPES()
|
END_DECLARE_EVENT_TYPES()
|
||||||
|
|
||||||
DEFINE_EVENT_TYPE(wxEVT_LOG_Write)
|
DEFINE_EVENT_TYPE(wxEVT_LOG_Write)
|
||||||
DEFINE_EVENT_TYPE(wxEVT_LOG_Newline)
|
DEFINE_EVENT_TYPE(wxEVT_LOG_Newline)
|
||||||
DEFINE_EVENT_TYPE(wxEVT_SetTitleText)
|
DEFINE_EVENT_TYPE(wxEVT_SetTitleText)
|
||||||
DEFINE_EVENT_TYPE(wxEVT_DockConsole)
|
DEFINE_EVENT_TYPE(wxEVT_DockConsole)
|
||||||
DEFINE_EVENT_TYPE(wxEVT_SemaphoreWait)
|
DEFINE_EVENT_TYPE(wxEVT_FlushQueue)
|
||||||
|
|
||||||
// C++ requires abstract destructors to exist, even thought hey're abstract.
|
// C++ requires abstract destructors to exist, even thought hey're abstract.
|
||||||
PipeRedirectionBase::~PipeRedirectionBase() throw() {}
|
PipeRedirectionBase::~PipeRedirectionBase() throw() {}
|
||||||
|
@ -226,11 +226,22 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
|
||||||
, m_TextCtrl( *new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
|
, m_TextCtrl( *new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
|
||||||
wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY | wxTE_RICH2 ) )
|
wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY | wxTE_RICH2 ) )
|
||||||
, m_ColorTable( options.FontSize )
|
, m_ColorTable( options.FontSize )
|
||||||
, m_curcolor( DefaultConsoleColor )
|
|
||||||
, m_msgcounter( 0 )
|
, m_pendingFlushes( 0 )
|
||||||
|
, m_WaitingThreadsForFlush( 0 )
|
||||||
|
|
||||||
|
, m_ThawThrottle( 0 )
|
||||||
|
, m_ThawNeeded( false )
|
||||||
|
, m_ThawPending( false )
|
||||||
|
|
||||||
|
, m_QueueColorSection( L"ConsoleLog::QueueColorSection" )
|
||||||
|
, m_QueueBuffer( L"ConsoleLog::QueueBuffer" )
|
||||||
|
, m_CurQueuePos( false )
|
||||||
|
|
||||||
, m_threadlogger( EnableThreadedLoggingTest ? new ConsoleTestThread() : NULL )
|
, m_threadlogger( EnableThreadedLoggingTest ? new ConsoleTestThread() : NULL )
|
||||||
{
|
{
|
||||||
m_TextCtrl.SetBackgroundColour( wxColor( 230, 235, 242 ) );
|
m_TextCtrl.SetBackgroundColour( wxColor( 230, 235, 242 ) );
|
||||||
|
m_TextCtrl.SetDefaultStyle( m_ColorTable[DefaultConsoleColor] );
|
||||||
|
|
||||||
// create Log menu (contains most options)
|
// create Log menu (contains most options)
|
||||||
wxMenuBar *pMenuBar = new wxMenuBar();
|
wxMenuBar *pMenuBar = new wxMenuBar();
|
||||||
|
@ -263,7 +274,6 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
|
||||||
|
|
||||||
// status bar for menu prompts
|
// status bar for menu prompts
|
||||||
CreateStatusBar();
|
CreateStatusBar();
|
||||||
ClearColor();
|
|
||||||
|
|
||||||
SetSize( wxRect( options.DisplayPosition, options.DisplaySize ) );
|
SetSize( wxRect( options.DisplayPosition, options.DisplaySize ) );
|
||||||
Show( options.Visible );
|
Show( options.Visible );
|
||||||
|
@ -281,11 +291,10 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
|
||||||
Connect( wxEVT_MOVE, wxMoveEventHandler(ConsoleLogFrame::OnMoveAround) );
|
Connect( wxEVT_MOVE, wxMoveEventHandler(ConsoleLogFrame::OnMoveAround) );
|
||||||
Connect( wxEVT_SIZE, wxSizeEventHandler(ConsoleLogFrame::OnResize) );
|
Connect( wxEVT_SIZE, wxSizeEventHandler(ConsoleLogFrame::OnResize) );
|
||||||
|
|
||||||
Connect( wxEVT_LOG_Write, wxCommandEventHandler(ConsoleLogFrame::OnWrite) );
|
|
||||||
Connect( wxEVT_LOG_Newline, wxCommandEventHandler(ConsoleLogFrame::OnNewline) );
|
|
||||||
Connect( wxEVT_SetTitleText, wxCommandEventHandler(ConsoleLogFrame::OnSetTitle) );
|
Connect( wxEVT_SetTitleText, wxCommandEventHandler(ConsoleLogFrame::OnSetTitle) );
|
||||||
Connect( wxEVT_DockConsole, wxCommandEventHandler(ConsoleLogFrame::OnDockedMove) );
|
Connect( wxEVT_DockConsole, wxCommandEventHandler(ConsoleLogFrame::OnDockedMove) );
|
||||||
Connect( wxEVT_SemaphoreWait, wxCommandEventHandler(ConsoleLogFrame::OnSemaphoreWait) );
|
|
||||||
|
Connect( wxEVT_FlushQueue, wxCommandEventHandler(ConsoleLogFrame::OnFlushEvent) );
|
||||||
|
|
||||||
if( m_threadlogger != NULL )
|
if( m_threadlogger != NULL )
|
||||||
m_threadlogger->Start();
|
m_threadlogger->Start();
|
||||||
|
@ -297,51 +306,74 @@ ConsoleLogFrame::~ConsoleLogFrame()
|
||||||
wxGetApp().OnProgramLogClosed();
|
wxGetApp().OnProgramLogClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleLogFrame::SetColor( ConsoleColors color )
|
int m_pendingFlushes = 0;
|
||||||
{
|
|
||||||
if( color != m_curcolor )
|
|
||||||
m_TextCtrl.SetDefaultStyle( m_ColorTable[m_curcolor=color] );
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleLogFrame::ClearColor()
|
|
||||||
{
|
|
||||||
if( DefaultConsoleColor != m_curcolor )
|
|
||||||
m_TextCtrl.SetDefaultStyle( m_ColorTable[m_curcolor=DefaultConsoleColor] );
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleLogFrame::Write( const wxString& text )
|
|
||||||
{
|
|
||||||
// remove selection (WriteText is in fact ReplaceSelection)
|
|
||||||
// TODO : Optimize this to only replace selection if some selection
|
|
||||||
// messages have been received since the last write.
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
wxTextPos nLen = m_TextCtrl.GetLastPosition();
|
|
||||||
m_TextCtrl.SetSelection(nLen, nLen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_TextCtrl.AppendText( text );
|
|
||||||
|
|
||||||
// cap at 256k for now...
|
|
||||||
// fixme - 256k runs well on win32 but appears to be very sluggish on linux. Might
|
|
||||||
// need platform dependent defaults here. - air
|
|
||||||
if( m_TextCtrl.GetLastPosition() > 0x40000 )
|
|
||||||
{
|
|
||||||
m_TextCtrl.Remove( 0, 0x10000 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation note: Calls SetColor and Write( text ). Override those virtuals
|
// Implementation note: Calls SetColor and Write( text ). Override those virtuals
|
||||||
// and this one will magically follow suite. :)
|
// and this one will magically follow suite. :)
|
||||||
void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
|
void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
|
||||||
{
|
{
|
||||||
SetColor( color );
|
//#ifdef PCSX2_SEH
|
||||||
Write( text );
|
pthread_testcancel();
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
ScopedLock lock( m_QueueLock );
|
||||||
|
|
||||||
|
if( m_QueueColorSection.GetLength() == 0 )
|
||||||
|
{
|
||||||
|
pxAssertMsg( m_CurQueuePos == 0, "Queue's character position didn't get reset in sync with it's ColorSection table." );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (m_QueueColorSection.GetLength() == 0) || ((color != Color_Current) && (m_QueueColorSection.GetLast().color != color)) )
|
||||||
|
{
|
||||||
|
++m_CurQueuePos; // Don't overwrite the NULL;
|
||||||
|
m_QueueColorSection.Add( ColorSection(color, m_CurQueuePos) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int endpos = m_CurQueuePos + text.Length();
|
||||||
|
m_QueueBuffer.MakeRoomFor( endpos + 1 ); // and the null!!
|
||||||
|
memcpy_fast( &m_QueueBuffer[m_CurQueuePos], text.c_str(), sizeof(wxChar) * text.Length() );
|
||||||
|
m_CurQueuePos = endpos;
|
||||||
|
|
||||||
|
// this NULL may be overwritten if the next message sent doesn't perform a color change.
|
||||||
|
m_QueueBuffer[m_CurQueuePos] = 0;
|
||||||
|
|
||||||
|
// Idle events don't always pass (wx blocks them when moving windows or using menus, for
|
||||||
|
// example). So let's hackfix it so that an alternate message is posted if the queue is
|
||||||
|
// "piling up."
|
||||||
|
|
||||||
|
if( m_pendingFlushes == 0 )
|
||||||
|
{
|
||||||
|
wxCommandEvent evt( wxEVT_FlushQueue );
|
||||||
|
evt.SetInt( 0 );
|
||||||
|
GetEventHandler()->AddPendingEvent( evt );
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_pendingFlushes;
|
||||||
|
|
||||||
|
if( m_pendingFlushes > 32 && !wxThread::IsMain() )
|
||||||
|
{
|
||||||
|
++m_WaitingThreadsForFlush;
|
||||||
|
lock.Unlock();
|
||||||
|
|
||||||
|
if( !m_sem_QueueFlushed.WaitRaw( 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.Lock();
|
||||||
|
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)
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleLogFrame::Newline()
|
void ConsoleLogFrame::Newline()
|
||||||
{
|
{
|
||||||
Write( L"\n" );
|
Write( Color_Current, L"\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleLogFrame::DoClose()
|
void ConsoleLogFrame::DoClose()
|
||||||
|
@ -364,7 +396,7 @@ void ConsoleLogFrame::DockedMove()
|
||||||
// * Logging Events
|
// * Logging Events
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
// Special event recieved from a window we're docked against.
|
// Special event received from a window we're docked against.
|
||||||
void ConsoleLogFrame::OnDockedMove( wxCommandEvent& event )
|
void ConsoleLogFrame::OnDockedMove( wxCommandEvent& event )
|
||||||
{
|
{
|
||||||
DockedMove();
|
DockedMove();
|
||||||
|
@ -466,7 +498,7 @@ void ConsoleLogFrame::OnFontSize( wxMenuEvent& evt )
|
||||||
|
|
||||||
m_conf.FontSize = ptsize;
|
m_conf.FontSize = ptsize;
|
||||||
m_ColorTable.SetFont( ptsize );
|
m_ColorTable.SetFont( ptsize );
|
||||||
m_TextCtrl.SetDefaultStyle( m_ColorTable[m_curcolor] );
|
m_TextCtrl.SetDefaultStyle( m_ColorTable[Color_White] );
|
||||||
|
|
||||||
// TODO: Process the attributes of each character and upgrade the font size,
|
// TODO: Process the attributes of each character and upgrade the font size,
|
||||||
// while still retaining color and bold settings... (might be slow but then
|
// while still retaining color and bold settings... (might be slow but then
|
||||||
|
@ -478,92 +510,113 @@ void ConsoleLogFrame::OnFontSize( wxMenuEvent& evt )
|
||||||
// Logging Events (typically received from Console class interfaces)
|
// Logging Events (typically received from Console class interfaces)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void ConsoleLogFrame::OnWrite( wxCommandEvent& event )
|
|
||||||
{
|
|
||||||
Write( (ConsoleColors)event.GetExtraLong(), event.GetString() );
|
|
||||||
DoMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleLogFrame::OnNewline( wxCommandEvent& event )
|
|
||||||
{
|
|
||||||
Newline();
|
|
||||||
DoMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleLogFrame::OnSetTitle( wxCommandEvent& event )
|
void ConsoleLogFrame::OnSetTitle( wxCommandEvent& event )
|
||||||
{
|
{
|
||||||
SetTitle( event.GetString() );
|
SetTitle( event.GetString() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleLogFrame::OnSemaphoreWait( wxCommandEvent& event )
|
void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& evt )
|
||||||
{
|
{
|
||||||
m_semaphore.Post();
|
ScopedLock locker( m_QueueLock );
|
||||||
|
|
||||||
|
if( m_CurQueuePos != 0 )
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
|
||||||
|
::SendMessage((HWND)m_TextCtrl.GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
|
||||||
|
::SendMessage((HWND)m_TextCtrl.GetHWND(), EM_LINESCROLL, 0, m_TextCtrl.GetNumberOfLines());
|
||||||
|
#endif
|
||||||
|
//m_TextCtrl.Thaw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation note: I tried desperately to move this into wxEVT_IDLE, on the theory that
|
||||||
|
// we don't actually want to wake up pending threads until after the GUI's finished all its
|
||||||
|
// paperwork. But wxEVT_IDLE doesn't work when you click menus or the title bar of a window,
|
||||||
|
// making it pretty well annoyingly useless for just about anything. >_<
|
||||||
|
|
||||||
|
// Workaround: I added a Sleep(1) to the DoWrite method to give the GUI some time to
|
||||||
|
// do its paperwork.
|
||||||
|
|
||||||
|
if( m_WaitingThreadsForFlush > 0 )
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
m_sem_QueueFlushed.Post();
|
||||||
|
} while( --m_WaitingThreadsForFlush > 0 );
|
||||||
|
|
||||||
|
int count = m_sem_QueueFlushed.Count();
|
||||||
|
while( count < 0 ) m_sem_QueueFlushed.Post();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
void ConsoleLogFrame::DoFlushQueue()
|
||||||
// Deadlock protection: High volume logs will over-tax our message pump and cause the
|
|
||||||
// GUI to become inaccessible. The cool solution would be a threaded log window, but wx
|
|
||||||
// is entirely un-safe for that kind of threading. So instead I use a message counter
|
|
||||||
// that stalls non-GUI threads when they attempt to over-tax an already burdened log.
|
|
||||||
// If too many messages get queued up, non-gui threads are stalled to allow the gui to
|
|
||||||
// catch up.
|
|
||||||
void ConsoleLogFrame::CountMessage()
|
|
||||||
{
|
{
|
||||||
long result = _InterlockedIncrement( &m_msgcounter );
|
int len = m_QueueColorSection.GetLength();
|
||||||
|
pxAssert( len != 0 );
|
||||||
|
|
||||||
if( result > 0x20 ) // 0x20 -- arbitrary value that seems to work well (tested on P4 and C2D)
|
// Note, freezing/thawing actually seems to cause more overhead than it solves.
|
||||||
|
// It might be useful if we're posting like dozens of messages, but in our case
|
||||||
|
// we only post 1-4 typically, so better to leave things enabled.
|
||||||
|
//m_TextCtrl.Freeze();
|
||||||
|
|
||||||
|
// Manual InsertionPoint tracking avoids a lot of overhead in SetInsertionPointEnd()
|
||||||
|
wxTextPos insertPoint = m_TextCtrl.GetLastPosition();
|
||||||
|
|
||||||
|
// cap at 256k for now...
|
||||||
|
// fixme - 256k runs well on win32 but appears to be very sluggish on linux (but that could
|
||||||
|
// be a result of my using Xming + CoLinux). Might need platform dependent defaults here. --air
|
||||||
|
if( (insertPoint + m_CurQueuePos) > 0x40000 )
|
||||||
{
|
{
|
||||||
if( !wxThread::IsMain() )
|
int toKeep = 0x40000 - m_CurQueuePos;
|
||||||
|
if( toKeep <= 10 )
|
||||||
{
|
{
|
||||||
// Append an event that'll post up our semaphore. It'll get run "in
|
m_TextCtrl.Clear();
|
||||||
// order" which means when it posts all queued messages will have been
|
insertPoint = 0;
|
||||||
// processed.
|
}
|
||||||
|
else
|
||||||
wxCommandEvent evt( wxEVT_SemaphoreWait );
|
{
|
||||||
GetEventHandler()->AddPendingEvent( evt );
|
int toRemove = 0x40000 - toKeep;
|
||||||
m_semaphore.WaitRaw();
|
if( toRemove < 0x10000 ) toRemove = 0x10000;
|
||||||
|
m_TextCtrl.Remove( 0, toRemove );
|
||||||
|
insertPoint -= toRemove;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Thread Safety note: This function expects to be called from the Main GUI thread
|
m_TextCtrl.SetInsertionPoint( insertPoint );
|
||||||
// only. If called from a thread other than Main, it will generate an assertion failure.
|
|
||||||
//
|
|
||||||
void ConsoleLogFrame::DoMessage()
|
|
||||||
{
|
|
||||||
AllowFromMainThreadOnly();
|
|
||||||
|
|
||||||
int cur = _InterlockedDecrement( &m_msgcounter );
|
for( int i=0; i<len; ++i )
|
||||||
|
|
||||||
// We need to freeze the control if there are more than 2 pending messages,
|
|
||||||
// otherwise the redraw of the console will prevent it from ever being able to
|
|
||||||
// catch up with the rate the queue is being filled, and the whole app could
|
|
||||||
// deadlock. >_<
|
|
||||||
|
|
||||||
if( m_TextCtrl.IsFrozen() )
|
|
||||||
{
|
{
|
||||||
if( cur < 1 )
|
if( m_QueueColorSection[i].color != Color_Current )
|
||||||
m_TextCtrl.Thaw();
|
m_TextCtrl.SetDefaultStyle( m_ColorTable[m_QueueColorSection[i].color] );
|
||||||
}
|
|
||||||
else if( cur >= 3 )
|
|
||||||
{
|
|
||||||
m_TextCtrl.Freeze();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const wxString passin( &m_QueueBuffer[m_QueueColorSection[i].startpoint] );
|
||||||
|
|
||||||
|
m_TextCtrl.WriteText( passin );
|
||||||
|
insertPoint += passin.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_TextCtrl.SetInsertionPoint( insertPoint );
|
||||||
|
|
||||||
|
m_CurQueuePos = 0;
|
||||||
|
m_QueueColorSection.Clear();
|
||||||
|
m_pendingFlushes = 0;
|
||||||
|
|
||||||
|
//m_TextCtrl.ShowPosition( insertPoint );
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleLogFrame* Pcsx2App::GetProgramLog()
|
ConsoleLogFrame* Pcsx2App::GetProgramLog()
|
||||||
{
|
{
|
||||||
return m_ProgramLogBox;
|
return m_ProgramLogBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pcsx2App::ProgramLog_CountMsg()
|
|
||||||
{
|
|
||||||
// New console log object model makes this check obsolete:
|
|
||||||
//if( m_ProgramLogBox == NULL ) return;
|
|
||||||
m_ProgramLogBox->CountMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pcsx2App::ProgramLog_PostEvent( wxEvent& evt )
|
void Pcsx2App::ProgramLog_PostEvent( wxEvent& evt )
|
||||||
{
|
{
|
||||||
// New console log object model makes this check obsolete:
|
// New console log object model makes this check obsolete:
|
||||||
|
@ -645,37 +698,21 @@ template< const IConsoleWriter& secondary >
|
||||||
static void __concall ConsoleToWindow_Newline()
|
static void __concall ConsoleToWindow_Newline()
|
||||||
{
|
{
|
||||||
secondary.Newline();
|
secondary.Newline();
|
||||||
|
((Pcsx2App&)*wxTheApp).GetProgramLog()->Newline();
|
||||||
wxCommandEvent evt( wxEVT_LOG_Newline );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_PostEvent( evt );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_CountMsg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template< const IConsoleWriter& secondary >
|
template< const IConsoleWriter& secondary >
|
||||||
static void __concall ConsoleToWindow_DoWrite( const wxString& fmt )
|
static void __concall ConsoleToWindow_DoWrite( const wxString& fmt )
|
||||||
{
|
{
|
||||||
secondary.DoWrite( fmt );
|
secondary.DoWrite( fmt );
|
||||||
|
((Pcsx2App&)*wxTheApp).GetProgramLog()->Write( th_CurrentColor, fmt );
|
||||||
wxCommandEvent evt( wxEVT_LOG_Write );
|
|
||||||
evt.SetString( fmt );
|
|
||||||
evt.SetExtraLong( th_CurrentColor );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_PostEvent( evt );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_CountMsg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template< const IConsoleWriter& secondary >
|
template< const IConsoleWriter& secondary >
|
||||||
static void __concall ConsoleToWindow_DoWriteLn( const wxString& fmt )
|
static void __concall ConsoleToWindow_DoWriteLn( const wxString& fmt )
|
||||||
{
|
{
|
||||||
secondary.DoWriteLn( fmt );
|
secondary.DoWriteLn( fmt );
|
||||||
|
((Pcsx2App&)*wxTheApp).GetProgramLog()->Write( th_CurrentColor, fmt + L"\n" );
|
||||||
// Implementation note: I've duplicated Write+Newline behavior here to avoid polluting
|
|
||||||
// the message pump with lots of erroneous messages (Newlines can be bound into Write message).
|
|
||||||
|
|
||||||
wxCommandEvent evt( wxEVT_LOG_Write );
|
|
||||||
evt.SetString( fmt + L"\n" );
|
|
||||||
evt.SetExtraLong( th_CurrentColor );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_PostEvent( evt );
|
|
||||||
((Pcsx2App&)*wxTheApp).ProgramLog_CountMsg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void __concall DoWriteFn(const wxString&);
|
typedef void __concall DoWriteFn(const wxString&);
|
||||||
|
|
|
@ -121,26 +121,74 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ColorSection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConsoleColors color;
|
||||||
|
int startpoint;
|
||||||
|
|
||||||
|
ColorSection() {}
|
||||||
|
ColorSection( ConsoleColors _color, int msgptr ) : color(_color), startpoint(msgptr) { }
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConLogConfig& m_conf;
|
ConLogConfig& m_conf;
|
||||||
wxTextCtrl& m_TextCtrl;
|
wxTextCtrl& m_TextCtrl;
|
||||||
ColorArray m_ColorTable;
|
ColorArray m_ColorTable;
|
||||||
ConsoleColors m_curcolor;
|
|
||||||
volatile long m_msgcounter; // used to track queued messages and throttle load placed on the gui message pump
|
|
||||||
|
|
||||||
Semaphore m_semaphore;
|
// this int throttles freeze/thaw of the display, by cycling from -2 to 4, roughly.
|
||||||
|
// (negative values force thaw, positive values indicate thaw is disabled. This is
|
||||||
|
// needed because the wxWidgets Thaw implementation uses a belated paint message,
|
||||||
|
// and if we Freeze on the very next queued message after thawing, the repaint
|
||||||
|
// never happens)
|
||||||
|
int m_ThawThrottle;
|
||||||
|
|
||||||
|
// If a freeze is executed, this is set true (without this, wx asserts)
|
||||||
|
bool m_ThawNeeded;
|
||||||
|
|
||||||
|
// Set true when a Thaw message is sent (avoids cluttering the message pump with redundant
|
||||||
|
// requests)
|
||||||
|
bool m_ThawPending;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Queue State Management Vars
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// This is a counter of the total number of pending flushes across all threads.
|
||||||
|
// If the value exceeds a threshold, threads begin throttling to avoid deadlocking
|
||||||
|
// the GUI.
|
||||||
|
volatile int m_pendingFlushes;
|
||||||
|
|
||||||
|
// This is a counter of the number of threads waiting for the Queue to flush.
|
||||||
|
volatile int m_WaitingThreadsForFlush;
|
||||||
|
|
||||||
|
// Used by threads waiting on the queue to flush.
|
||||||
|
Semaphore m_sem_QueueFlushed;
|
||||||
|
|
||||||
|
// Lock object for accessing or modifying the following three vars:
|
||||||
|
// m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos
|
||||||
|
MutexLockRecursive m_QueueLock;
|
||||||
|
|
||||||
|
// Describes a series of colored text sections in the m_QueueBuffer.
|
||||||
|
SafeList<ColorSection> m_QueueColorSection;
|
||||||
|
|
||||||
|
// Series of Null-terminated strings, each one has a corresponding entry in
|
||||||
|
// m_QueueColorSelection.
|
||||||
|
SafeArray<wxChar> m_QueueBuffer;
|
||||||
|
|
||||||
|
// Current write position into the m_QueueBuffer;
|
||||||
|
int m_CurQueuePos;
|
||||||
|
|
||||||
// Threaded log spammer, useful for testing console logging performance.
|
// Threaded log spammer, useful for testing console logging performance.
|
||||||
ConsoleTestThread* m_threadlogger;
|
// (alternatively you can enable Disasm logging in any recompiler and achieve
|
||||||
|
// a similar effect)
|
||||||
|
ConsoleTestThread* m_threadlogger;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ctor & dtor
|
// ctor & dtor
|
||||||
ConsoleLogFrame( MainEmuFrame *pParent, const wxString& szTitle, ConLogConfig& options );
|
ConsoleLogFrame( MainEmuFrame *pParent, const wxString& szTitle, ConLogConfig& options );
|
||||||
virtual ~ConsoleLogFrame();
|
virtual ~ConsoleLogFrame();
|
||||||
|
|
||||||
virtual void Write( const wxString& text );
|
|
||||||
virtual void SetColor( ConsoleColors color );
|
|
||||||
virtual void ClearColor();
|
|
||||||
virtual void DockedMove();
|
virtual void DockedMove();
|
||||||
|
|
||||||
// Retrieves the current configuration options settings for this box.
|
// Retrieves the current configuration options settings for this box.
|
||||||
|
@ -149,11 +197,8 @@ public:
|
||||||
|
|
||||||
void Write( ConsoleColors color, const wxString& text );
|
void Write( ConsoleColors color, const wxString& text );
|
||||||
void Newline();
|
void Newline();
|
||||||
void CountMessage();
|
|
||||||
void DoMessage();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// menu callbacks
|
// menu callbacks
|
||||||
virtual void OnOpen (wxMenuEvent& event);
|
virtual void OnOpen (wxMenuEvent& event);
|
||||||
virtual void OnClose(wxMenuEvent& event);
|
virtual void OnClose(wxMenuEvent& event);
|
||||||
|
@ -164,14 +209,14 @@ protected:
|
||||||
|
|
||||||
virtual void OnCloseWindow(wxCloseEvent& event);
|
virtual void OnCloseWindow(wxCloseEvent& event);
|
||||||
|
|
||||||
void OnWrite( wxCommandEvent& event );
|
|
||||||
void OnNewline( wxCommandEvent& event );
|
|
||||||
void OnSetTitle( wxCommandEvent& event );
|
void OnSetTitle( wxCommandEvent& event );
|
||||||
void OnDockedMove( wxCommandEvent& event );
|
void OnDockedMove( wxCommandEvent& event );
|
||||||
void OnSemaphoreWait( wxCommandEvent& event );
|
void OnIdleEvent( wxIdleEvent& event );
|
||||||
|
void OnFlushEvent( wxCommandEvent& event );
|
||||||
|
|
||||||
// common part of OnClose() and OnCloseWindow()
|
// common part of OnClose() and OnCloseWindow()
|
||||||
virtual void DoClose();
|
virtual void DoClose();
|
||||||
|
void DoFlushQueue();
|
||||||
|
|
||||||
void OnMoveAround( wxMoveEvent& evt );
|
void OnMoveAround( wxMoveEvent& evt );
|
||||||
void OnResize( wxSizeEvent& evt );
|
void OnResize( wxSizeEvent& evt );
|
||||||
|
|
Loading…
Reference in New Issue