From 69b8976c661a79007e29332a12f6afca95a02c5f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Apr 2020 23:43:29 +0200 Subject: [PATCH] - GZDoom timer code added. This may come in handy later. --- source/core/utility/i_time.cpp | 115 ++++++++++++++++++++++++++++++++- source/core/utility/i_time.h | 23 +++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/source/core/utility/i_time.cpp b/source/core/utility/i_time.cpp index d43dc1b7a..a4177e191 100644 --- a/source/core/utility/i_time.cpp +++ b/source/core/utility/i_time.cpp @@ -36,8 +36,6 @@ #include #include #include "i_time.h" -#include "c_cvars.h" -#include "printf.h" //========================================================================== // @@ -45,7 +43,12 @@ // //========================================================================== -static double TimeScale = 1.0; +static uint64_t FirstFrameStartTime; +static uint64_t CurrentFrameStartTime; +static uint64_t FreezeTime; +int GameTicRate; + +double TimeScale = 1.0; static uint64_t GetClockTimeNS() { @@ -53,11 +56,82 @@ static uint64_t GetClockTimeNS() return (uint64_t)((duration_cast(steady_clock::now().time_since_epoch()).count()) * (uint64_t)(TimeScale * 1000)); } +static uint64_t MSToNS(unsigned int ms) +{ + return static_cast(ms) * 1'000'000; +} + static uint64_t NSToMS(uint64_t ns) { return static_cast(ns / 1'000'000); } +static int NSToTic(uint64_t ns) +{ + return static_cast(ns * GameTicRate / 1'000'000'000); +} + +static uint64_t TicToNS(int tic) +{ + return static_cast(tic) * 1'000'000'000 / GameTicRate; +} + +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_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. + + std::this_thread::sleep_for(std::chrono::milliseconds(1000 * count / 70)); + I_SetFrameTime(); +} + +int I_WaitForTic(int prevtic) +{ + // Waits until the current tic is greater than prevtic. Time must not be frozen. + + int time; + while ((time = I_GetTime()) <= prevtic) + { + // Windows-specific note: + // The minimum amount of time a thread can sleep is controlled by timeBeginPeriod. + // We set this to 1 ms in DoMain. + + const uint64_t next = FirstFrameStartTime + TicToNS(prevtic + 1); + const uint64_t now = I_nsTime(); + + if (next > now) + { + const uint64_t sleepTime = NSToMS(next - now); + + if (sleepTime > 2) + { + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime - 2)); + } + } + + I_SetFrameTime(); + } + + return time; +} + uint64_t I_nsTime() { return GetClockTimeNS(); @@ -68,3 +142,38 @@ uint64_t I_msTime() return NSToMS(I_nsTime()); } +uint64_t I_msTimeFS() // from "start" +{ + return (FirstFrameStartTime == 0) ? 0 : NSToMS(I_nsTime() - FirstFrameStartTime); +} + +int I_GetTime() +{ + return NSToTic(CurrentFrameStartTime - FirstFrameStartTime); +} + +double I_GetTimeFrac() +{ + int currentTic = NSToTic(CurrentFrameStartTime - FirstFrameStartTime); + uint64_t ticStartTime = FirstFrameStartTime + TicToNS(currentTic); + uint64_t ticNextTime = FirstFrameStartTime + TicToNS(currentTic + 1); + + return (CurrentFrameStartTime - ticStartTime) / (double)(ticNextTime - ticStartTime); +} + +void I_FreezeTime(bool frozen) +{ + if (frozen) + { + assert(FreezeTime == 0); + FreezeTime = GetClockTimeNS(); + } + else + { + assert(FreezeTime != 0); + FirstFrameStartTime += GetClockTimeNS() - FreezeTime; + FreezeTime = 0; + I_SetFrameTime(); + } +} + diff --git a/source/core/utility/i_time.h b/source/core/utility/i_time.h index 8c4274942..47d726de7 100644 --- a/source/core/utility/i_time.h +++ b/source/core/utility/i_time.h @@ -2,8 +2,31 @@ #include +extern int GameTicRate; +extern double TimeScale; + +// Called by D_DoomLoop, sets the time for the current frame +void I_SetFrameTime(); + +// Called by D_DoomLoop, returns current time in tics. +int I_GetTime(); + +double I_GetTimeFrac(); + +// 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. +// You must also 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 uint64_t I_msTime(); +// [SP] Returns millisecond-accurate time from start +uint64_t I_msTimeFS(); + // Nanosecond-accurate time uint64_t I_nsTime();