Port the async thread to a SDL timer

SDL implements timers via threads and it lets us easily aim at
a 60Hz frequency.
This commit is contained in:
dhewg 2011-12-20 20:30:20 +01:00
parent 5a052e846f
commit 7865e432a7
7 changed files with 37 additions and 183 deletions

View file

@ -26,6 +26,8 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL.h>
#include "sys/platform.h"
#include "idlib/containers/HashTable.h"
#include "idlib/LangDict.h"
@ -218,12 +220,13 @@ private:
#ifdef ID_WRITE_VERSION
idCompressor * config_compressor;
#endif
SDL_TimerID async_timer;
};
idCommonLocal commonLocal;
idCommon * common = &commonLocal;
/*
==================
idCommonLocal::idCommonLocal
@ -248,6 +251,8 @@ idCommonLocal::idCommonLocal( void ) {
#ifdef ID_WRITE_VERSION
config_compressor = NULL;
#endif
async_timer = NULL;
}
/*
@ -2777,12 +2782,31 @@ void idCommonLocal::SetMachineSpec( void ) {
com_videoRam.SetInteger( vidRam );
}
static unsigned int async_tick;
static unsigned int AsyncTimer(unsigned int interval, void *) {
common->Async();
Sys_TriggerEvent(TRIGGER_EVENT_ONE);
async_tick++;
// calculate the next interval to get as close to 60fps as possible
unsigned int now = SDL_GetTicks();
unsigned int tick = async_tick * 1000 / 60;
if (now >= tick)
return 1;
return tick - now;
}
/*
=================
idCommonLocal::Init
=================
*/
void idCommonLocal::Init( int argc, char **argv ) {
SDL_Init(SDL_INIT_TIMER);
Sys_InitThreads();
try {
@ -2879,6 +2903,12 @@ void idCommonLocal::Init( int argc, char **argv ) {
catch( idException & ) {
Sys_Error( "Error during initialization" );
}
async_tick = (SDL_GetTicks() >> 4) + 1;
async_timer = SDL_AddTimer(16, AsyncTimer, NULL);
if (!async_timer)
Sys_Error("Error while starting the async timer");
}
@ -2888,9 +2918,13 @@ idCommonLocal::Shutdown
=================
*/
void idCommonLocal::Shutdown( void ) {
com_shuttingDown = true;
if (async_timer) {
SDL_RemoveTimer(async_timer);
async_timer = NULL;
}
idAsyncNetwork::server.Kill();
idAsyncNetwork::client.Shutdown();

View file

@ -59,66 +59,6 @@ void Sys_InitScanTable( void ) {
common->DPrintf( "TODO: Sys_InitScanTable\n" );
}
/*
=================
Sys_AsyncThread
=================
*/
THREAD_RETURN_TYPE Sys_AsyncThread( void * ) {
int now;
int next;
int want_sleep;
// multi tick compensate for poor schedulers (Linux 2.4)
int ticked, to_ticked;
now = Sys_Milliseconds();
ticked = now >> 4;
while (1) {
// sleep
now = Sys_Milliseconds();
next = ( now & 0xFFFFFFF0 ) + 0x10;
want_sleep = ( next-now-1 ) * 1000;
if ( want_sleep > 0 ) {
usleep( want_sleep ); // sleep 1ms less than true target
}
// compensate if we slept too long
now = Sys_Milliseconds();
to_ticked = now >> 4;
// show ticking statistics - every 100 ticks, print a summary
#if 0
#define STAT_BUF 100
static int stats[STAT_BUF];
static int counter = 0;
// how many ticks to play
stats[counter] = to_ticked - ticked;
counter++;
if (counter == STAT_BUF) {
Sys_DebugPrintf("\n");
for( int i = 0; i < STAT_BUF; i++) {
if ( ! (i & 0xf) ) {
Sys_DebugPrintf("\n");
}
Sys_DebugPrintf( "%d ", stats[i] );
}
Sys_DebugPrintf("\n");
counter = 0;
}
#endif
while ( ticked < to_ticked ) {
common->Async();
ticked++;
Sys_TriggerEvent( TRIGGER_EVENT_ONE );
}
// thread exit
pthread_testcancel();
}
return (THREAD_RETURN_TYPE) 0;
}
/*
==============
Sys_DefaultSavePath

View file

@ -978,23 +978,6 @@ static OSErr DoRegCodeDialog( char* ioRegCode1 )
return regCodeInfo.okPressed ? (OSErr)noErr : (OSErr)userCanceledErr;
}
/*
=================
Sys_AsyncThread
=================
*/
THREAD_RETURN_TYPE Sys_AsyncThread( void * ) {
while ( 1 ) {
usleep( 16666 );
common->Async();
Sys_TriggerEvent( TRIGGER_EVENT_ONE );
pthread_testcancel();
}
return (THREAD_RETURN_TYPE) 0;
}
#if defined(__ppc__)
/*

View file

@ -90,9 +90,7 @@ void Posix_Exit(int ret) {
}
// at this point, too late to catch signals
Posix_ClearSigs();
if ( asyncThread.threadHandle ) {
Sys_DestroyThread( asyncThread );
}
// process spawning. it's best when it happens after everything has shut down
if ( exit_spawn[0] ) {
Sys_DoStartProcess( exit_spawn, false );
@ -542,7 +540,6 @@ Posix_EarlyInit
===============
*/
void Posix_EarlyInit( void ) {
memset( &asyncThread, 0, sizeof( asyncThread ) );
exit_spawn[0] = '\0';
Posix_InitSigs();
// set the base time
@ -563,7 +560,6 @@ void Posix_LateInit( void ) {
#ifndef ID_DEDICATED
common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
#endif
Posix_StartAsyncThread( );
}
/*

View file

@ -49,9 +49,6 @@ void Posix_Exit( int ret );
void Posix_SetExit(int ret); // override the exit code
void Posix_SetExitSpawn( const char *exeName ); // set the process to be spawned when we quit
void Posix_StartAsyncThread( void );
extern xthreadInfo asyncThread;
bool Posix_AddKeyboardPollEvent( int key, bool state );
bool Posix_AddMousePollEvent( int action, int value );
@ -62,6 +59,4 @@ void Posix_Shutdown( void );
void Sys_FPE_handler( int signum, siginfo_t *info, void *context );
void Sys_DoStartProcess( const char *exeName, bool dofork = true ); // if not forking, current process gets replaced
THREAD_RETURN_TYPE Sys_AsyncThread( void * );
#endif

View file

@ -139,28 +139,6 @@ const char* Sys_GetThreadName( int *index ) {
return "main";
}
/*
=========================================================
Async Thread
=========================================================
*/
xthreadInfo asyncThread;
/*
=================
Posix_StartAsyncThread
=================
*/
void Posix_StartAsyncThread() {
if ( asyncThread.threadHandle == 0 ) {
Sys_CreateThread( Sys_AsyncThread, NULL, THREAD_NORMAL, asyncThread, "Async", g_threads, &g_thread_count );
} else {
common->Printf( "Async thread already running\n" );
}
common->Printf( "Async thread started\n" );
}
/*
==================
Posix_InitPThreads

View file

@ -819,76 +819,6 @@ void Sys_In_Restart_f( const idCmdArgs &args ) {
Sys_InitInput();
}
/*
==================
Sys_AsyncThread
==================
*/
static THREAD_RETURN_TYPE Sys_AsyncThread( void *parm ) {
int wakeNumber;
int startTime;
startTime = Sys_Milliseconds();
wakeNumber = 0;
while ( 1 ) {
#ifdef WIN32
// this will trigger 60 times a second
int r = WaitForSingleObject( hTimer, 100 );
if ( r != WAIT_OBJECT_0 ) {
OutputDebugString( "idPacketServer::PacketServerInterrupt: bad wait return" );
}
#endif
#if 0
wakeNumber++;
int msec = Sys_Milliseconds();
int deltaTime = msec - startTime;
startTime = msec;
char str[1024];
sprintf( str, "%i ", deltaTime );
OutputDebugString( str );
#endif
common->Async();
}
return (THREAD_RETURN_TYPE) 0;
}
/*
==============
Sys_StartAsyncThread
Start the thread that will call idCommon::Async()
==============
*/
void Sys_StartAsyncThread( void ) {
// create an auto-reset event that happens 60 times a second
hTimer = CreateWaitableTimer( NULL, false, NULL );
if ( !hTimer ) {
common->Error( "idPacketServer::Spawn: CreateWaitableTimer failed" );
}
LARGE_INTEGER t;
t.HighPart = t.LowPart = 0;
SetWaitableTimer( hTimer, &t, USERCMD_MSEC, NULL, NULL, TRUE );
Sys_CreateThread( Sys_AsyncThread, NULL, THREAD_ABOVE_NORMAL, threadInfo, "Async", g_threads, &g_thread_count );
#ifdef SET_THREAD_AFFINITY
// give the async thread an affinity for the second cpu
SetThreadAffinityMask( (HANDLE)threadInfo.threadHandle, 2 );
#endif
if ( !threadInfo.threadHandle ) {
common->Error( "Sys_StartAsyncThread: failed" );
}
}
/*
================
Sys_AlreadyRunning
@ -1212,8 +1142,6 @@ int main(int argc, char *argv[]) {
}
#endif
Sys_StartAsyncThread();
// hide or show the early console as necessary
if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
Sys_ShowConsole( 1, true );