* Improved the framelimiter sleep mode more (less cpu used and more responsive to fps fluctuation near the 60fps line at the same time)

* Added timeBeginPeriod() to improve the Win32 kernel scheduler resolution (improves sleep accuracy and thread responsiveness)
 * hackfixed some code that made exiting pcsx2 "slow" sometimes.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2041 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-10-19 23:13:22 +00:00
parent 7bf94f9db9
commit 27174c3f5b
6 changed files with 77 additions and 40 deletions

View File

@ -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 );

View File

@ -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()
{
}

View File

@ -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 );
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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();