From 7865e432a73512a5868c99a897ce03cbf07042b3 Mon Sep 17 00:00:00 2001 From: dhewg Date: Tue, 20 Dec 2011 20:30:20 +0100 Subject: [PATCH] Port the async thread to a SDL timer SDL implements timers via threads and it lets us easily aim at a 60Hz frequency. --- neo/framework/Common.cpp | 38 ++++++++++++++++- neo/sys/linux/main.cpp | 60 --------------------------- neo/sys/osx/DOOMController.mm | 17 -------- neo/sys/posix/posix_main.cpp | 6 +-- neo/sys/posix/posix_public.h | 5 --- neo/sys/posix/posix_threads.cpp | 22 ---------- neo/sys/win32/win_main.cpp | 72 --------------------------------- 7 files changed, 37 insertions(+), 183 deletions(-) diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 8a54fbc7..fe17791a 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -26,6 +26,8 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#include + #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(); diff --git a/neo/sys/linux/main.cpp b/neo/sys/linux/main.cpp index 1eb406d2..6fa4c8b9 100644 --- a/neo/sys/linux/main.cpp +++ b/neo/sys/linux/main.cpp @@ -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 diff --git a/neo/sys/osx/DOOMController.mm b/neo/sys/osx/DOOMController.mm index dc2bb977..08c41010 100644 --- a/neo/sys/osx/DOOMController.mm +++ b/neo/sys/osx/DOOMController.mm @@ -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__) /* diff --git a/neo/sys/posix/posix_main.cpp b/neo/sys/posix/posix_main.cpp index 314cbaf2..3269f096 100644 --- a/neo/sys/posix/posix_main.cpp +++ b/neo/sys/posix/posix_main.cpp @@ -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( ); } /* diff --git a/neo/sys/posix/posix_public.h b/neo/sys/posix/posix_public.h index b50b3e3d..de5e8680 100644 --- a/neo/sys/posix/posix_public.h +++ b/neo/sys/posix/posix_public.h @@ -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 diff --git a/neo/sys/posix/posix_threads.cpp b/neo/sys/posix/posix_threads.cpp index cf38f2fc..f372d1a4 100644 --- a/neo/sys/posix/posix_threads.cpp +++ b/neo/sys/posix/posix_threads.cpp @@ -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 diff --git a/neo/sys/win32/win_main.cpp b/neo/sys/win32/win_main.cpp index dd09a649..00d7c0d9 100644 --- a/neo/sys/win32/win_main.cpp +++ b/neo/sys/win32/win_main.cpp @@ -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 );