mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-03-12 22:02:08 +00:00
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:
parent
5a052e846f
commit
7865e432a7
7 changed files with 37 additions and 183 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__)
|
||||
|
||||
/*
|
||||
|
|
|
@ -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( );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in a new issue