diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index 78e8ff4ed..6248c5e57 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -875,6 +875,7 @@ elseif(APPLE) sys/sys_local.cpp sys/posix/posix_net.cpp sys/posix/posix_main.cpp + sys/posix/posix_shared.cpp ) set(src_sys_core @@ -927,6 +928,7 @@ else() sys/sys_local.cpp sys/posix/posix_net.cpp sys/posix/posix_main.cpp + sys/posix/posix_shared.cpp sys/linux/main.cpp ) diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 87d9df69c..2963f1a65 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -97,6 +97,9 @@ idCVar com_timestampPrints( "com_timestampPrints", "0", CVAR_SYSTEM, "print time idCVar com_timescale( "timescale", "1", CVAR_SYSTEM | CVAR_FLOAT, "scales the time", 0.1f, 10.0f ); idCVar com_makingBuild( "com_makingBuild", "0", CVAR_BOOL | CVAR_SYSTEM, "1 when making a build" ); idCVar com_updateLoadSize( "com_updateLoadSize", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "update the load size after loading a map" ); +idCVar com_asyncClient ( "com_asyncClient", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_ARCHIVE, "run client and renderer asynchronous" ); +idCVar com_renderFPS( "com_renderFPS", "300", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "frames per second to render" ); +idCVar com_clientFPS( "com_clientFPS", "60", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "times per second the game is called" ); idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." ); @@ -2385,52 +2388,112 @@ idCommonLocal::Frame ================= */ void idCommonLocal::Frame( void ) { - try { + // Slow things a little bit down. + unsigned long long spintime = Sys_Microseconds(); + + while ( 1 ) { +#if defined (__GNUC__) && (__i386 || __x86_64__) + asm("pause"); +#elif defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__ARM_ARCH_6K__) + asm("yield"); +#endif + + if ( Sys_Microseconds() >= ( spintime - 5 ) ) { + break; + } + } + + // Calculate timings. + static unsigned long long oldframetime; + unsigned long long newframetime = Sys_Microseconds(); + unsigned long long frametime = newframetime - oldframetime; + oldframetime = newframetime; - // pump all the events - Sys_GenerateEvents(); + static int clientdelta = 1000000; + static int renderdelta = 1000000; - // write config file if anything changed - WriteConfiguration(); + bool clientframe = true; + bool renderframe = true; - // change SIMD implementation if required - if ( com_forceGenericSIMD.IsModified() ) { - InitSIMD(); + if ( cvarSystem->GetCVarBool( "com_asyncClient" ) == true ) { + clientdelta += frametime; + renderdelta += frametime; + + if ( clientdelta < ( 1000000 / cvarSystem->GetCVarInteger( "com_clientFPS" ) ) ) { + clientframe = false; + } else { + clientdelta = 0; } - eventLoop->RunEventLoop(); + if ( renderdelta < ( 1000000 / cvarSystem->GetCVarInteger( "com_renderFPS" ) ) ) { + renderframe = false; + } else { + renderdelta = 0; + } + } - com_frameTime = com_ticNumber * USERCMD_MSEC; + // Early return if no work has to be done. + if ( !clientframe && !renderframe ) { + return; + } - idAsyncNetwork::RunFrame(); + // Rund the game. + try { + if ( clientframe ) { + // Pump the events. + Sys_GenerateEvents(); + + // Write config file (if anything changed). + WriteConfiguration(); + + // Change SIMD implementation if required. + if ( com_forceGenericSIMD.IsModified() ) { + InitSIMD(); + } + + eventLoop->RunEventLoop(); + com_frameTime = com_ticNumber * USERCMD_MSEC; + idAsyncNetwork::RunFrame(); + } if ( idAsyncNetwork::IsActive() ) { if ( idAsyncNetwork::serverDedicated.GetInteger() != 1 ) { - session->GuiFrameEvents(); - session->UpdateScreen( false ); + if ( clientframe ) { + session->GuiFrameEvents(); + } + + if ( renderframe ) { + session->UpdateScreen( false ); + } } } else { - session->Frame(); + if ( clientframe ) { + session->Frame(); + } - // normal, in-sequence screen update - session->UpdateScreen( false ); + if ( renderframe ) { + // normal, in-sequence screen update + session->UpdateScreen( false ); + } } - // report timing information - if ( com_speeds.GetBool() ) { - static int lastTime; - int nowTime = Sys_Milliseconds(); - int com_frameMsec = nowTime - lastTime; - lastTime = nowTime; - Printf( "frame:%i all:%3i gfr:%3i rf:%3i bk:%3i\n", com_frameNumber, com_frameMsec, time_gameFrame, time_frontend, time_backend ); - time_gameFrame = 0; - time_gameDraw = 0; - } + if ( clientframe ) { + // report timing information + if ( com_speeds.GetBool() ) { + static int lastTime; + int nowTime = Sys_Milliseconds(); + int com_frameMsec = nowTime - lastTime; + lastTime = nowTime; + Printf( "frame:%i all:%3i gfr:%3i rf:%3i bk:%3i\n", com_frameNumber, com_frameMsec, time_gameFrame, time_frontend, time_backend ); + time_gameFrame = 0; + time_gameDraw = 0; + } - com_frameNumber++; + com_frameNumber++; - // set idLib frame number for frame based memory dumps - idLib::frameNumber = com_frameNumber; + // set idLib frame number for frame based memory dumps + idLib::frameNumber = com_frameNumber; + } } catch( idException & ) { diff --git a/neo/framework/Common.h b/neo/framework/Common.h index c0400bb47..dd45bab4f 100644 --- a/neo/framework/Common.h +++ b/neo/framework/Common.h @@ -73,6 +73,9 @@ extern idCVar com_showAsyncStats; extern idCVar com_showSoundDecoders; extern idCVar com_makingBuild; extern idCVar com_updateLoadSize; +extern idCVar com_asyncClient; +extern idCVar com_renderFPS; +extern idCVar com_clientFPS; extern int time_gameFrame; // game logic time extern int time_gameDraw; // game present time diff --git a/neo/framework/Session.cpp b/neo/framework/Session.cpp index e1fee6220..93357c4b8 100644 --- a/neo/framework/Session.cpp +++ b/neo/framework/Session.cpp @@ -2606,12 +2606,16 @@ void idSessionLocal::Frame() { minTic = latchedTicNumber; } - while( 1 ) { + if ( cvarSystem->GetCVarBool( "com_asyncClient" ) == true ) { latchedTicNumber = com_ticNumber; - if ( latchedTicNumber >= minTic ) { - break; + } else { + while( 1 ) { + latchedTicNumber = com_ticNumber; + if ( latchedTicNumber >= minTic ) { + break; + } + Sys_WaitForEvent( TRIGGER_EVENT_ONE ); } - Sys_WaitForEvent( TRIGGER_EVENT_ONE ); } if ( authEmitTimeout ) { diff --git a/neo/sys/posix/posix_shared.cpp b/neo/sys/posix/posix_shared.cpp new file mode 100644 index 000000000..f13ec3b6d --- /dev/null +++ b/neo/sys/posix/posix_shared.cpp @@ -0,0 +1,98 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include +#include + +/* +================ +Sys_Microseconds +================ +*/ +unsigned long long Sys_Microseconds() { +#ifdef __APPLE__ + // OSX didn't have clock_gettime() until recently, so use Mach's clock_get_time() + // instead. fortunately its mach_timespec_t seems identical to POSIX struct timespec + // so lots of code can be shared + clock_serv_t cclock; + mach_timespec_t now; + static mach_timespec_t first; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &now); + mach_port_deallocate(mach_task_self(), cclock); + +#else // not __APPLE__ - other Unix-likes will hopefully support clock_gettime() + + struct timespec now; + static struct timespec first; +#ifdef _POSIX_MONOTONIC_CLOCK + clock_gettime(CLOCK_MONOTONIC, &now); +#else + clock_gettime(CLOCK_REALTIME, &now); +#endif + +#endif // not __APPLE__ + + if(first.tv_sec == 0) + { + long long nsec = now.tv_nsec; + long long sec = now.tv_sec; + // set back first by 1ms so neither this function nor Sys_Milliseconds() + // (which calls this) will ever return 0 + nsec -= 1000000; + if(nsec < 0) + { + nsec += 1000000000ll; // 1s in ns => definitely positive now + --sec; + } + + first.tv_sec = sec; + first.tv_nsec = nsec; + } + + long long sec = now.tv_sec - first.tv_sec; + long long nsec = now.tv_nsec - first.tv_nsec; + + if(nsec < 0) + { + nsec += 1000000000ll; // 1s in ns + --sec; + } + + return sec*1000000ll + nsec/1000ll; +} + +/* +================ +Sys_Milliseconds +================ +*/ +unsigned int Sys_Milliseconds() { + return (int)(Sys_Microseconds()/1000ll); +} diff --git a/neo/sys/sys_public.h b/neo/sys/sys_public.h index fbac1a886..5eda327d2 100644 --- a/neo/sys/sys_public.h +++ b/neo/sys/sys_public.h @@ -115,9 +115,11 @@ void Sys_DebugVPrintf( const char *fmt, va_list arg ); // NOTE: due to SDL_TIMESLICE this is very bad portability karma, and should be completely removed void Sys_Sleep( int msec ); -// Sys_Milliseconds should only be used for profiling purposes, -// any game related timing information should come from event timestamps -unsigned int Sys_Milliseconds( void ); +// Sys_Milliseconds and Sys_Microseconds are used in the main loop and fpr +// profiling purposes. any game related timing information should come from +// event timestamps +unsigned long long Sys_Microseconds( void ); +unsigned int Sys_Milliseconds( void ); // returns a selection of the CPUID_* flags int Sys_GetProcessorId( void ); diff --git a/neo/sys/threads.cpp b/neo/sys/threads.cpp index a0fe7c8db..a7f3fe09f 100644 --- a/neo/sys/threads.cpp +++ b/neo/sys/threads.cpp @@ -53,15 +53,6 @@ void Sys_Sleep(int msec) { SDL_Delay(msec); } -/* -================ -Sys_Milliseconds -================ -*/ -unsigned int Sys_Milliseconds() { - return SDL_GetTicks(); -} - /* ================== Sys_InitThreads diff --git a/neo/sys/win32/win_shared.cpp b/neo/sys/win32/win_shared.cpp index 7c6f09e26..0fae8fa03 100644 --- a/neo/sys/win32/win_shared.cpp +++ b/neo/sys/win32/win_shared.cpp @@ -101,3 +101,38 @@ Sys_SetPhysicalWorkMemory void Sys_SetPhysicalWorkMemory( int minBytes, int maxBytes ) { ::SetProcessWorkingSetSize( GetCurrentProcess(), minBytes, maxBytes ); } + +/* +================ +Sys_Microseconds +================ +*/ +unsigned long long Sys_Microseconds() { + static LARGE_INTEGER freq = { 0 }; + static LARGE_INTEGER base = { 0 }; + + if (!freq.QuadPart) + { + QueryPerformanceFrequency(&freq); + } + + if (!base.QuadPart) + { + QueryPerformanceCounter(&base); + base.QuadPart -= 1001; + } + + LARGE_INTEGER cur; + QueryPerformanceCounter(&cur); + + return (cur.QuadPart - base.QuadPart) * 1000000 / freq.QuadPart; +} + +/* +================ +Sys_Milliseconds +================ +*/ +unsigned int Sys_Milliseconds() { + return (int)(Sys_Microseconds()/1000ll); +}