diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index b77cb671c7..9c18e7f7fb 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -73,6 +73,11 @@ namespace Threading // For use in spin/wait loops. extern void SpinWait(); + // Optional implementation to enable hires thread/process scheduler for the operating system. + // Needed by Windows, but might not be relevant to other platforms. + extern void EnableHiresScheduler(); + extern void DisableHiresScheduler(); + // sleeps the current thread for the given number of milliseconds. extern void Sleep( int ms ); diff --git a/common/src/Utilities/Linux/LnxThreads.cpp b/common/src/Utilities/Linux/LnxThreads.cpp index 52a7af97f9..4d6640f458 100644 --- a/common/src/Utilities/Linux/LnxThreads.cpp +++ b/common/src/Utilities/Linux/LnxThreads.cpp @@ -24,38 +24,44 @@ static bool isMultiCore = true; // assume more than one CPU (safer) -namespace Threading +// Note: Apparently this solution is Linux/Solaris only. +// FreeBSD/OsX need something far more complicated (apparently) +void Threading::CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ) { - // Note: Apparently this solution is Linux/Solaris only. - // FreeBSD/OsX need something far more complicated (apparently) - void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ) + const uint numCPU = sysconf( _SC_NPROCESSORS_ONLN ); + if( numCPU > 0 ) { - const uint numCPU = sysconf( _SC_NPROCESSORS_ONLN ); - if( numCPU > 0 ) - { - isMultiCore = numCPU > 1; - x86caps.LogicalCores = numCPU; - x86caps.PhysicalCores = ( numCPU / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU; - } - else - { - // Indeterminate? - x86caps.LogicalCores = 1; - x86caps.PhysicalCores = 1; - } + isMultiCore = numCPU > 1; + x86caps.LogicalCores = numCPU; + x86caps.PhysicalCores = ( numCPU / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU; } - - __forceinline void Sleep( int ms ) + else { - usleep( 1000*ms ); - } - - // For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory - // improve performance and reduce cpu power consumption. - __forceinline void SpinWait() - { - // If this doesn't compile you can just comment it out (it only serves as a - // performance hint and isn't required). - __asm__ ( "pause" ); + // Indeterminate? + x86caps.LogicalCores = 1; + x86caps.PhysicalCores = 1; } } + +__forceinline void Threading::Sleep( int ms ) +{ + usleep( 1000*ms ); +} + +// For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory +// improve performance and reduce cpu power consumption. +__forceinline void Threading::SpinWait() +{ + // If this doesn't compile you can just comment it out (it only serves as a + // performance hint and isn't required). + __asm__ ( "pause" ); +} + +__forceinline void Threading::EnableHiresScheduler() +{ + // Don't know if linux has a customizable scheduler resolution like Windows (doubtful) +} + +__forceinline void Threading::DisableHiresScheduler() +{ +} diff --git a/common/src/Utilities/Windows/WinThreads.cpp b/common/src/Utilities/Windows/WinThreads.cpp index e4961999c3..c511c533d2 100644 --- a/common/src/Utilities/Windows/WinThreads.cpp +++ b/common/src/Utilities/Windows/WinThreads.cpp @@ -60,7 +60,23 @@ namespace Threading // improve performance and reduce cpu power consumption. __forceinline void SpinWait() { - __asm { pause }; + __asm pause; + } + + __forceinline void EnableHiresScheduler() + { + // 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 + // 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 DisableHiresScheduler() + { + timeEndPeriod( 1 ); } } diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 0ac1822c35..fea3b26ce4 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -327,17 +327,16 @@ static __forceinline void frameLimit() if( sDeltaTime >= 0 ) return; // If we're way ahead then we can afford to sleep the thread a bit. + // (note, sleep(1) thru sleep(2) tend to be the least accurate sleeps, and longer + // sleeps tend to be pretty reliable, so that's why the convoluted if/else below) s32 msec = (int)((sDeltaTime*-1000) / (s64)GetTickFrequency()); - if( msec > 2 ) Threading::Sleep( msec - 2 ); + if( msec > 4 ) Threading::Sleep( msec ); + else if( msec > 2 ) Threading::Sleep( 1 ); - // - while( true ) - { - sDeltaTime = GetCPUTicks() - uExpectedEnd; - if( sDeltaTime >= 0 ) break; - Timeslice(); - } + // Sleep is not picture-perfect accurate, but it's actually not necessary to + // maintain a "perfect" lock to uExpectedEnd anyway. if we're a little ahead + // starting this frame, it'll just sleep longer the next to make up for it. :) } static __forceinline void VSyncStart(u32 sCycle) diff --git a/pcsx2/System/SysThreads.cpp b/pcsx2/System/SysThreads.cpp index 6e3d0f8078..0c37642f57 100644 --- a/pcsx2/System/SysThreads.cpp +++ b/pcsx2/System/SysThreads.cpp @@ -460,6 +460,8 @@ void SysCoreThread::CpuExecute() void SysCoreThread::ExecuteTaskInThread() { + Threading::EnableHiresScheduler(); + tls_coreThread = this; m_sem_event.WaitRaw(); @@ -486,6 +488,8 @@ void SysCoreThread::OnResumeInThread( bool isSuspended ) // Invoked by the pthread_exit or pthread_cancel void SysCoreThread::OnCleanupInThread() { + Threading::DisableHiresScheduler(); + if( g_plugins != NULL ) g_plugins->Close(); diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 6959a3793f..b05714a1ad 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -273,15 +273,22 @@ bool Pcsx2App::PrepForExit( bool canCancel ) throw Exception::CancelEvent( "Savestate in progress, cannot close program (close event delayed)" ); } + /* if( canCancel ) { + // TODO: Confirm with the user? + // Problem: Suspend is often slow because it needs to wait until the current EE frame + // has finished processing (if the GS or logging has incurred severe overhead this makes + // closing PCSX2 difficult). A non-blocking suspend with modal dialog might suffice + // however. --air + bool resume = CoreThread.Suspend(); - if( /* TODO: Confirm with the user? */ false ) + if( false ) { if(resume) CoreThread.Resume(); return false; } - } + }*/ m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting ); CleanupMess();