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);
+}