mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-03-13 22:32:32 +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 "sys/platform.h"
|
||||||
#include "idlib/containers/HashTable.h"
|
#include "idlib/containers/HashTable.h"
|
||||||
#include "idlib/LangDict.h"
|
#include "idlib/LangDict.h"
|
||||||
|
@ -218,12 +220,13 @@ private:
|
||||||
#ifdef ID_WRITE_VERSION
|
#ifdef ID_WRITE_VERSION
|
||||||
idCompressor * config_compressor;
|
idCompressor * config_compressor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SDL_TimerID async_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
idCommonLocal commonLocal;
|
idCommonLocal commonLocal;
|
||||||
idCommon * common = &commonLocal;
|
idCommon * common = &commonLocal;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==================
|
==================
|
||||||
idCommonLocal::idCommonLocal
|
idCommonLocal::idCommonLocal
|
||||||
|
@ -248,6 +251,8 @@ idCommonLocal::idCommonLocal( void ) {
|
||||||
#ifdef ID_WRITE_VERSION
|
#ifdef ID_WRITE_VERSION
|
||||||
config_compressor = NULL;
|
config_compressor = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
async_timer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2777,12 +2782,31 @@ void idCommonLocal::SetMachineSpec( void ) {
|
||||||
com_videoRam.SetInteger( vidRam );
|
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
|
idCommonLocal::Init
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
void idCommonLocal::Init( int argc, char **argv ) {
|
void idCommonLocal::Init( int argc, char **argv ) {
|
||||||
|
SDL_Init(SDL_INIT_TIMER);
|
||||||
|
|
||||||
Sys_InitThreads();
|
Sys_InitThreads();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -2879,6 +2903,12 @@ void idCommonLocal::Init( int argc, char **argv ) {
|
||||||
catch( idException & ) {
|
catch( idException & ) {
|
||||||
Sys_Error( "Error during initialization" );
|
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 ) {
|
void idCommonLocal::Shutdown( void ) {
|
||||||
|
|
||||||
com_shuttingDown = true;
|
com_shuttingDown = true;
|
||||||
|
|
||||||
|
if (async_timer) {
|
||||||
|
SDL_RemoveTimer(async_timer);
|
||||||
|
async_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
idAsyncNetwork::server.Kill();
|
idAsyncNetwork::server.Kill();
|
||||||
idAsyncNetwork::client.Shutdown();
|
idAsyncNetwork::client.Shutdown();
|
||||||
|
|
||||||
|
|
|
@ -59,66 +59,6 @@ void Sys_InitScanTable( void ) {
|
||||||
common->DPrintf( "TODO: Sys_InitScanTable\n" );
|
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
|
Sys_DefaultSavePath
|
||||||
|
|
|
@ -978,23 +978,6 @@ static OSErr DoRegCodeDialog( char* ioRegCode1 )
|
||||||
return regCodeInfo.okPressed ? (OSErr)noErr : (OSErr)userCanceledErr;
|
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__)
|
#if defined(__ppc__)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -90,9 +90,7 @@ void Posix_Exit(int ret) {
|
||||||
}
|
}
|
||||||
// at this point, too late to catch signals
|
// at this point, too late to catch signals
|
||||||
Posix_ClearSigs();
|
Posix_ClearSigs();
|
||||||
if ( asyncThread.threadHandle ) {
|
|
||||||
Sys_DestroyThread( asyncThread );
|
|
||||||
}
|
|
||||||
// process spawning. it's best when it happens after everything has shut down
|
// process spawning. it's best when it happens after everything has shut down
|
||||||
if ( exit_spawn[0] ) {
|
if ( exit_spawn[0] ) {
|
||||||
Sys_DoStartProcess( exit_spawn, false );
|
Sys_DoStartProcess( exit_spawn, false );
|
||||||
|
@ -542,7 +540,6 @@ Posix_EarlyInit
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
void Posix_EarlyInit( void ) {
|
void Posix_EarlyInit( void ) {
|
||||||
memset( &asyncThread, 0, sizeof( asyncThread ) );
|
|
||||||
exit_spawn[0] = '\0';
|
exit_spawn[0] = '\0';
|
||||||
Posix_InitSigs();
|
Posix_InitSigs();
|
||||||
// set the base time
|
// set the base time
|
||||||
|
@ -563,7 +560,6 @@ void Posix_LateInit( void ) {
|
||||||
#ifndef ID_DEDICATED
|
#ifndef ID_DEDICATED
|
||||||
common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
|
common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
|
||||||
#endif
|
#endif
|
||||||
Posix_StartAsyncThread( );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -49,9 +49,6 @@ void Posix_Exit( int ret );
|
||||||
void Posix_SetExit(int ret); // override the exit code
|
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_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_AddKeyboardPollEvent( int key, bool state );
|
||||||
bool Posix_AddMousePollEvent( int action, int value );
|
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_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
|
void Sys_DoStartProcess( const char *exeName, bool dofork = true ); // if not forking, current process gets replaced
|
||||||
|
|
||||||
THREAD_RETURN_TYPE Sys_AsyncThread( void * );
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -139,28 +139,6 @@ const char* Sys_GetThreadName( int *index ) {
|
||||||
return "main";
|
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
|
Posix_InitPThreads
|
||||||
|
|
|
@ -819,76 +819,6 @@ void Sys_In_Restart_f( const idCmdArgs &args ) {
|
||||||
Sys_InitInput();
|
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
|
Sys_AlreadyRunning
|
||||||
|
@ -1212,8 +1142,6 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Sys_StartAsyncThread();
|
|
||||||
|
|
||||||
// hide or show the early console as necessary
|
// hide or show the early console as necessary
|
||||||
if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
|
if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
|
||||||
Sys_ShowConsole( 1, true );
|
Sys_ShowConsole( 1, true );
|
||||||
|
|
Loading…
Reference in a new issue