diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 83e5e1820c7..46f0f819954 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -744,7 +744,7 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds) dclick = false; // This used level.time which didn't work outside a level. - nowtime = I_MSTime(); + nowtime = PresentTime.Milliseconds; if (doublebinds != NULL && DClickTime[ev->data1] > nowtime && ev->type == EV_KeyDown) { // Key pressed for a double click diff --git a/src/c_console.cpp b/src/c_console.cpp index c3ab769037a..04f8428b45b 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -529,7 +529,7 @@ static void maybedrawnow (bool tick, bool force) || gamestate == GS_STARTUP)) { static size_t lastprinttime = 0; - size_t nowtime = I_GetTime(); + size_t nowtime = PresentTime.Milliseconds; if (nowtime - lastprinttime > 1 || force) { diff --git a/src/d_main.cpp b/src/d_main.cpp index 5e0eff1d130..885a8dbc623 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -281,7 +281,7 @@ void D_ProcessEvents (void) { M_SetDefaultMode (); } - else if (testingmode <= I_GetTime()) + else if (testingmode <= PresentTime.Tic) { M_RestoreMode (); } @@ -779,7 +779,7 @@ void D_Display () { - unsigned int nowtime = I_FPSTime(); + unsigned int nowtime = PresentTime.Milliseconds; TexMan.UpdateAnimations(nowtime); R_UpdateSky(nowtime); switch (gamestate) @@ -945,7 +945,7 @@ void D_Display () I_FreezeTime(true); screen->WipeEndScreen (); - wipestart = I_MSTime(); + wipestart = PresentTime.Milliseconds; NetUpdate(); // send out any new accumulation do @@ -953,7 +953,7 @@ void D_Display () do { I_WaitVBL(2); - nowtime = I_MSTime(); + nowtime = PresentTime.Milliseconds; diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow. } while (diff < 1); wipestart = nowtime; @@ -1031,7 +1031,7 @@ void D_DoomLoop () lasttic = gametic; I_StartFrame (); } - I_SetFrameTime(); + I_SetupFramePresentTime(); // process one or more tics if (singletics) diff --git a/src/d_net.cpp b/src/d_net.cpp index 12de17c9e4a..5c92a159002 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -532,7 +532,7 @@ void HSendPacket (int node, int len) { PacketStore store; store.message = doomcom; - store.timer = I_GetTime() + ((net_fakelatency / 2) / (1000 / TICRATE)); + store.timer = PresentTime.Tic + ((net_fakelatency / 2) / (1000 / TICRATE)); OutBuffer.Push(store); } else @@ -540,7 +540,7 @@ void HSendPacket (int node, int len) for (unsigned int i = 0; i < OutBuffer.Size(); i++) { - if (OutBuffer[i].timer <= I_GetTime()) + if (OutBuffer[i].timer <= PresentTime.Tic) { doomcom = OutBuffer[i].message; I_NetCmd(); @@ -581,7 +581,7 @@ bool HGetPacket (void) { PacketStore store; store.message = doomcom; - store.timer = I_GetTime() + ((net_fakelatency / 2) / (1000 / TICRATE)); + store.timer = PresentTime.Tic + ((net_fakelatency / 2) / (1000 / TICRATE)); InBuffer.Push(store); doomcom.remotenode = -1; } @@ -591,7 +591,7 @@ bool HGetPacket (void) bool gotmessage = false; for (unsigned int i = 0; i < InBuffer.Size(); i++) { - if (InBuffer[i].timer <= I_GetTime()) + if (InBuffer[i].timer <= PresentTime.Tic) { doomcom = InBuffer[i].message; InBuffer.Delete(i); @@ -783,7 +783,7 @@ void GetPackets (void) // [RH] Get "ping" times - totally useless, since it's bound to the frequency // packets go out at. lastrecvtime[netconsole] = currrecvtime[netconsole]; - currrecvtime[netconsole] = I_MSTime (); + currrecvtime[netconsole] = I_ClockTimeMS(); // check for exiting the game if (netbuffer[0] & NCMD_EXIT) @@ -957,7 +957,7 @@ void NetUpdate (void) } // check time - nowtime = I_GetTime (); + nowtime = PresentTime.Tic; newtics = nowtime - gametime; gametime = nowtime; @@ -1827,12 +1827,10 @@ void TryRunTics (void) // get real tics if (doWait) { - entertic = I_WaitForTic (oldentertics); - } - else - { - entertic = I_GetTime (); + I_WaitForTic(oldentertics); } + + entertic = PresentTime.Tic; realtics = entertic - oldentertics; oldentertics = entertic; @@ -1914,7 +1912,8 @@ void TryRunTics (void) Net_CheckLastReceived (counts); // don't stay in here forever -- give the menu a chance to work - if (I_GetTime () - entertic >= 1) + I_SetupFramePresentTime(); + if (PresentTime.Tic - entertic >= 1) { C_Ticker (); M_Ticker (); @@ -1929,7 +1928,7 @@ void TryRunTics (void) hadlate = false; for (i = 0; i < MAXPLAYERS; i++) players[i].waiting = false; - lastglobalrecvtime = I_GetTime (); //Update the last time the game tic'd over + lastglobalrecvtime = PresentTime.Tic; //Update the last time the game tic'd over // run the count tics if (counts > 0) @@ -1962,9 +1961,9 @@ void Net_CheckLastReceived (int counts) { // [Ed850] Check to see the last time a packet was received. // If it's longer then 3 seconds, a node has likely stalled. - if (I_GetTime() - lastglobalrecvtime >= TICRATE * 3) + if (PresentTime.Tic - lastglobalrecvtime >= TICRATE * 3) { - lastglobalrecvtime = I_GetTime(); //Bump the count + lastglobalrecvtime = PresentTime.Tic; //Bump the count if (NetMode == NET_PeerToPeer || consoleplayer == Net_Arbitrator) { diff --git a/src/dobject.cpp b/src/dobject.cpp index 17b9743f3c2..6ec6d0baaf6 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -615,7 +615,7 @@ void DObject::CheckIfSerialized () const DEFINE_ACTION_FUNCTION(DObject, MSTime) { - ACTION_RETURN_INT(I_MSTime()); + ACTION_RETURN_INT(PresentTime.Milliseconds); } void *DObject::ScriptVar(FName field, PType *type) diff --git a/src/g_game.cpp b/src/g_game.cpp index ec1a7be506a..5cfae250e9f 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2889,7 +2889,7 @@ bool G_CheckDemoStatus (void) int endtime = 0; if (timingdemo) - endtime = I_GetTime () - starttime; + endtime = PresentTime.Tic - starttime; C_RestoreCVars (); // [RH] Restore cvars demo might have changed M_Free (demobuffer); diff --git a/src/g_level.cpp b/src/g_level.cpp index a2b15b02d5e..82d304013d3 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1021,7 +1021,7 @@ void G_DoLoadLevel (int position, bool autosave) if (firstTime) { - starttime = I_GetTime (); + starttime = PresentTime.Tic; firstTime = false; } } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index c900c21d7ef..8063543c288 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -889,8 +889,15 @@ void FGLRenderer::RenderView (player_t* player, unsigned int nowtime) ResetProfilingData(); // Get this before everything else - if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.; - else r_viewpoint.TicFrac = I_GetTimeFrac (&r_viewpoint.FrameTime); + if (cl_capfps || r_NoInterpolate) + { + r_viewpoint.TicFrac = 1.; + } + else + { + r_viewpoint.FrameTime = PresentTime.Tic; + r_viewpoint.TicFrac = PresentTime.TicFrac; + } gl_frameMS = nowtime; P_FindParticleSubsectors (); diff --git a/src/gl/utility/gl_clock.cpp b/src/gl/utility/gl_clock.cpp index 5cd1daa219f..60d330a6821 100644 --- a/src/gl/utility/gl_clock.cpp +++ b/src/gl/utility/gl_clock.cpp @@ -188,7 +188,7 @@ ADD_STAT(rendertimes) { static FString buff; static int lasttime=0; - int t=I_FPSTime(); + int t= I_ClockTimeMS(); if (t-lasttime>1000) { buff.Truncate(0); @@ -226,7 +226,7 @@ void CheckBench() { // if we started the FPS counter ourselves or ran from the console // we need to wait for it to stabilize before using it. - if (waitstart > 0 && I_MSTime() < waitstart + 5000) return; + if (waitstart > 0 && I_ClockTimeMS() < waitstart + 5000) return; FString compose; @@ -257,12 +257,12 @@ CCMD(bench) if (vid_fps == 0) { vid_fps = 1; - waitstart = I_MSTime(); + waitstart = I_ClockTimeMS(); switchfps = true; } else { - if (ConsoleState == c_up) waitstart = I_MSTime(); + if (ConsoleState == c_up) waitstart = I_ClockTimeMS(); switchfps = false; } C_HideConsole (); diff --git a/src/i_time.cpp b/src/i_time.cpp index 908dbdd59fc..c5770c0f203 100644 --- a/src/i_time.cpp +++ b/src/i_time.cpp @@ -45,16 +45,6 @@ // //========================================================================== -static uint64_t FirstFrameStartTime; -static uint64_t CurrentFrameStartTime; -static uint64_t FreezeTime; - -static uint64_t GetClockTimeNS() -{ - using namespace std::chrono; - return (uint64_t)duration_cast(steady_clock::now().time_since_epoch()).count(); -} - static uint64_t MSToNS(unsigned int ms) { return static_cast(ms) * 1'000'000; @@ -75,117 +65,76 @@ static uint64_t TicToNS(int tic) return static_cast(tic) * 1'000'000'000 / TICRATE; } -void I_SetFrameTime() -{ - // Must only be called once per frame/swapbuffers. - // - // Caches all timing information for the current rendered frame so that any - // calls to I_FPSTime, I_MSTime, I_GetTime or I_GetTimeFrac will return - // the same time. - - if (FreezeTime == 0) - { - CurrentFrameStartTime = GetClockTimeNS(); - if (FirstFrameStartTime == 0) - FirstFrameStartTime = CurrentFrameStartTime; - } -} - -void I_WaitVBL(int count) -{ - // I_WaitVBL is never used to actually synchronize to the vertical blank. - // Instead, it's used for delay purposes. Doom used a 70 Hz display mode, - // so that's what we use to determine how long to wait for. +static uint64_t FirstFrameStartTime; +static uint64_t FirstTicTime; +static uint64_t FreezeTime; - std::this_thread::sleep_for(std::chrono::milliseconds(1000 * count / 70)); - I_SetFrameTime(); -} +FramePresentTime PresentTime; -int I_WaitForTic(int prevtic) +void I_SetupFramePresentTime() { - // Waits until the current tic is greater than prevtic. Time must not be frozen. + uint64_t now = I_ClockTimeNS(); - int time; - while ((time = I_GetTime()) <= prevtic) + if (FirstFrameStartTime == 0) { - // The minimum amount of time a thread can sleep is controlled by timeBeginPeriod. - // We set this to 1 ms in DoMain. - int sleepTime = prevtic - time; - if (sleepTime > 2) - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime - 2)); - - I_SetFrameTime(); + FirstFrameStartTime = now; + FirstTicTime = now; } - return time; + PresentTime.Nanoseconds = now - FirstFrameStartTime; + PresentTime.Milliseconds = NSToMS(PresentTime.Nanoseconds); + + if (FreezeTime != 0) + now = FreezeTime; + + int currentTic = NSToTic(now - FirstTicTime); + uint64_t ticStartTime = FirstTicTime + TicToNS(currentTic); + uint64_t ticNextTime = FirstTicTime + TicToNS(currentTic + 1); + + PresentTime.Tic = currentTic + 1; + PresentTime.TicFrac = (now - ticStartTime) / (double)(ticNextTime - ticStartTime); } -uint64_t I_NSTime() +void I_FreezeTime(bool frozen) { - if (FreezeTime == 0) + if (frozen && FreezeTime == 0) { - return CurrentFrameStartTime - FirstFrameStartTime; + FreezeTime = I_ClockTimeNS(); } - else + else if (!frozen && FreezeTime != 0) { - if (FirstFrameStartTime == 0) - { - FirstFrameStartTime = GetClockTimeNS(); - return 0; - } - else - { - return GetClockTimeNS() - FirstFrameStartTime; - } + FirstTicTime += I_ClockTimeNS() - FreezeTime; + FreezeTime = 0; + I_SetupFramePresentTime(); } } -uint64_t I_FPSTimeNS() +void I_WaitForTic(int tic) { - if (FreezeTime == 0) - return CurrentFrameStartTime; - else - return GetClockTimeNS(); -} - -unsigned int I_MSTime() -{ - return NSToMS(I_NSTime()); + while (PresentTime.Tic <= tic) + { + std::this_thread::yield(); + I_SetupFramePresentTime(); + } } -unsigned int I_FPSTime() +void I_WaitVBL(int count) { - return NSToMS(I_FPSTimeNS()); -} + // I_WaitVBL is never used to actually synchronize to the vertical blank. + // Instead, it's used for delay purposes. Doom used a 70 Hz display mode, + // so that's what we use to determine how long to wait for. -int I_GetTime() -{ - return NSToTic(CurrentFrameStartTime - FirstFrameStartTime) + 1; + std::this_thread::sleep_for(std::chrono::milliseconds(1000 * count / 70)); + I_SetupFramePresentTime(); } -double I_GetTimeFrac(uint32_t *ms) +uint32_t I_ClockTimeMS() { - int currentTic = NSToTic(CurrentFrameStartTime - FirstFrameStartTime); - uint64_t ticStartTime = FirstFrameStartTime + TicToNS(currentTic); - uint64_t ticNextTime = FirstFrameStartTime + TicToNS(currentTic + 1); - - if (ms) - *ms = currentTic + 1; - - return (CurrentFrameStartTime - ticStartTime) / (double)(ticNextTime - ticStartTime); + return NSToMS(I_ClockTimeNS()); } -void I_FreezeTime(bool frozen) +uint64_t I_ClockTimeNS() { - if (frozen) - { - FreezeTime = GetClockTimeNS(); - } - else - { - FirstFrameStartTime += GetClockTimeNS() - FreezeTime; - FreezeTime = 0; - I_SetFrameTime(); - } + using namespace std::chrono; + return (uint64_t)duration_cast(steady_clock::now().time_since_epoch()).count(); } - diff --git a/src/i_time.h b/src/i_time.h index 5d15149240f..c76d99a4d09 100644 --- a/src/i_time.h +++ b/src/i_time.h @@ -2,27 +2,28 @@ #include -// Called by D_DoomLoop, sets the time for the current frame -void I_SetFrameTime(); +struct FramePresentTime +{ + uint64_t Nanoseconds; + uint32_t Milliseconds; + double TicFrac; + int Tic; +}; -// Called by D_DoomLoop, returns current time in tics. -int I_GetTime(); +// Time point the frame is expected to be displayed on the monitor +extern FramePresentTime PresentTime; -double I_GetTimeFrac(uint32_t *ms); +void I_SetupFramePresentTime(); -// like I_GetTime, except it waits for a new tic before returning -int I_WaitForTic(int); - -// Freezes tic counting temporarily. While frozen, calls to I_GetTime() -// will always return the same value. This does not affect I_MSTime(). -// You must also not call I_WaitForTic() while freezing time, since the -// tic will never arrive (unless it's the current one). +// Freezes tic counting temporarily. While frozen the present time stays the same. +// You not call I_WaitForTic() while freezing time, since the tic will never arrive (unless it's the current one). void I_FreezeTime(bool frozen); -// [RH] Returns millisecond-accurate time -unsigned int I_MSTime(); -unsigned int I_FPSTime(); +// Wait until the specified tic has been presented. Time must not be frozen. +void I_WaitForTic(int tic); + +// Returns the hardware clock time, in milliseconds +uint32_t I_ClockTimeMS(); -// Nanosecond-accurate time -uint64_t I_NSTime(); -uint64_t I_FPSTimeNS(); +// Returns the hardware clock time, in nanoseconds +uint64_t I_ClockTimeNS(); diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index d3ba14e9255..aed145ced72 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -313,7 +313,7 @@ DEFINE_ACTION_FUNCTION(DVideoModeMenu, SetSelectedSize) OldBits = DisplayBits; NewBits = BitTranslate[DummyDepthCvar]; setmodeneeded = true; - testingmode = I_GetTime() + 5 * TICRATE; + testingmode = PresentTime.Tic + 5 * TICRATE; SetModesMenu (NewWidth, NewHeight, NewBits); ACTION_RETURN_BOOL(true); } diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 3559887fa21..d0831a05eaf 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -957,7 +957,7 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) // none found - we have to build new ones! unsigned int startTime, endTime; - startTime = I_FPSTime (); + startTime = I_ClockTimeMS(); TArray polyspots, anchors; P_GetPolySpots (map, polyspots, anchors); FNodeBuilder::FLevel leveldata = @@ -971,7 +971,7 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) FNodeBuilder builder (leveldata, polyspots, anchors, true); builder.Extract (level); - endTime = I_FPSTime (); + endTime = I_ClockTimeMS(); DPrintf (DMSG_NOTIFY, "BSP generation took %.3f sec (%u segs)\n", (endTime - startTime) * 0.001, level.segs.Size()); buildtime = endTime - startTime; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index fb2f1cdb792..fb25796d1a7 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3938,7 +3938,7 @@ void P_SetupLevel (const char *lumpname, int position) { BuildGLNodes = RequireGLNodes || multiplayer || demoplayback || demorecording || genglnodes; - startTime = I_FPSTime (); + startTime = I_ClockTimeMS(); TArray polyspots, anchors; P_GetPolySpots (map, polyspots, anchors); FNodeBuilder::FLevel leveldata = @@ -3954,7 +3954,7 @@ void P_SetupLevel (const char *lumpname, int position) // if the different machines' am_textured setting differs. FNodeBuilder builder (leveldata, polyspots, anchors, BuildGLNodes); builder.Extract (level); - endTime = I_FPSTime (); + endTime = I_ClockTimeMS(); DPrintf (DMSG_NOTIFY, "BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, level.segs.Size()); oldvertextable = builder.GetOldVertexTable(); reloop = true; diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 8a18c433d30..5f970793243 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -783,7 +783,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor iview = FindPastViewer (viewpoint.camera); - int nowtic = I_GetTime (); + int nowtic = PresentTime.Tic; if (iview->otic != -1 && nowtic > iview->otic) { iview->otic = nowtic; @@ -831,7 +831,8 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor iview->otic = nowtic; } - viewpoint.TicFrac = I_GetTimeFrac (&viewpoint.FrameTime); + viewpoint.FrameTime = PresentTime.Tic; + viewpoint.TicFrac = PresentTime.TicFrac; if (cl_capfps || r_NoInterpolate) { viewpoint.TicFrac = 1.; @@ -982,7 +983,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor if (hom == 3) { - hom = ((I_FPSTime() / 128) & 1) + 1; + hom = ((PresentTime.Milliseconds / 128) & 1) + 1; } if (hom == 1) { @@ -994,7 +995,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor } else if (hom == 4) { - color = (I_FPSTime() / 32) & 255; + color = (PresentTime.Milliseconds / 32) & 255; } else { diff --git a/src/swrenderer/things/r_voxel.cpp b/src/swrenderer/things/r_voxel.cpp index 8f2b80f0bb7..6ed37d2324a 100644 --- a/src/swrenderer/things/r_voxel.cpp +++ b/src/swrenderer/things/r_voxel.cpp @@ -135,7 +135,7 @@ namespace swrenderer int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; if (voxelspin != 0) { - DAngle ang = double(I_FPSTime()) * voxelspin / 1000; + DAngle ang = double(PresentTime.Milliseconds) * voxelspin / 1000; vis->Angle -= ang; } diff --git a/src/v_video.cpp b/src/v_video.cpp index d47ece2015d..1ebfd8ca97a 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -870,7 +870,7 @@ void DFrameBuffer::DrawRateStuff () // Draws frame time and cumulative fps if (vid_fps) { - uint32_t ms = I_FPSTime(); + uint32_t ms = I_ClockTimeMS(); uint32_t howlong = ms - LastMS; if ((signed)howlong >= 0) { @@ -903,7 +903,7 @@ void DFrameBuffer::DrawRateStuff () // draws little dots on the bottom of the screen if (ticker) { - int i = I_GetTime(); + int i = PresentTime.Tic; int tics = i - LastTic; uint8_t *buffer = GetBuffer();