From a94ec5dc3371b2c6b1547903dd35e67216b98872 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Fri, 13 Jan 2023 13:02:58 +0100 Subject: [PATCH] Implemented real-time clock, allowing for precise timekeeping and framerate-independent game updates. --- display.c | 2 +- main.c | 3 ++- time.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- time.h | 35 ++++++++++++++++++++++++++++- 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/display.c b/display.c index d16daad..86c057f 100644 --- a/display.c +++ b/display.c @@ -55,7 +55,7 @@ void display_init() // ISR subsystem to the kernel ResetGraph(0); - SetVideoMode(MODE_NTSC); + //SetVideoMode(MODE_NTSC); // Define display environments, first on top and second on bottom SetDefDispEnv(&disp[0], 0, 0, SCREENWIDTH, SCREENHEIGHT); diff --git a/main.c b/main.c index 2613dc3..051f846 100644 --- a/main.c +++ b/main.c @@ -24,9 +24,9 @@ world_t world; // Init function void init(void) { - time_init(); input_init(); display_init(); + time_init(); //asset_loadTexture(tim_e1m1, NULL); @@ -49,6 +49,7 @@ int main(int argc, const char *argv[]) // Draw stuff world_draw(&world); + FntPrint(-1, "Time: %d, ticks = %d, delta time = %d, fps = %d\n", time_getRealTime() >> 12, time_getFrameNumber(), time_getDeltaTime(), time_getFrameRate() >> 8); FntPrint(-1, "Camera pos = (%d, %d, %d) rot = (%d, %d, %d)\n", cam_pos.vx, cam_pos.vy, cam_pos.vz, cam_rot.vx, cam_rot.vy, cam_rot.vz); FntFlush(-1); diff --git a/time.c b/time.c index a3aebcb..a11ab59 100644 --- a/time.c +++ b/time.c @@ -1,19 +1,82 @@ #include "common.h" #include "time.h" -static int frameNumber; +#include + +static volatile u_long realTime; // Real time in 20,12 format (4096 ticks per second) +static u_long frameNumber; // Number of game ticks +static u_long frameStartTime; // Real time at which the current frame began +static u_short frameDeltaTime; // Real time since last game tick in 4,12 format + +static u_short avgFrameRate; // Average framerate over the last 16 frames in 8,8 format +static u_long measureStartTime; + +static void timer_callback() +{ + realTime += 4; +} void time_init() { + realTime = 0; frameNumber = 0; + frameStartTime = 0; + frameDeltaTime = 1; // Prevent division by zero for any code using this number on the first frame + + avgFrameRate = 0; + measureStartTime = 0; + + // Set the timer such that we get exactly 1024 interrupts per second on both PAL and NTSC (roughly once per millisecond) + // This allows the game speed to be completely framerate and video mode independent + const int counter = 4096; + + EnterCriticalSection(); + SetRCnt(RCntCNT2, counter, RCntMdINTR); + TIMER_CTRL(2) = 0x1E58; + InterruptCallback(6, timer_callback); + StartRCnt(RCntCNT2); + ChangeClearRCnt(2, 0); + ExitCriticalSection(); } void time_tick() { ++frameNumber; + + u_long currTime = realTime; + if ((frameNumber & 0xF) == 0) + { + avgFrameRate = (u_short)((0x10 << 20) / (currTime - measureStartTime)); + measureStartTime = currTime; + } + + frameDeltaTime = (u_short)(currTime - frameStartTime); + + // Start the next frame + frameStartTime = currTime; } -int time_getFrameNumber() +u_long time_getRealTime() +{ + return realTime; +} + +u_long time_getFrameNumber() { return frameNumber; } + +u_long time_getFrameTime() +{ + return frameStartTime; +} + +u_short time_getDeltaTime() +{ + return frameDeltaTime; +} + +u_short time_getFrameRate() +{ + return avgFrameRate; +} diff --git a/time.h b/time.h index 98a15b1..5891661 100644 --- a/time.h +++ b/time.h @@ -4,6 +4,39 @@ void time_init(); void time_tick(); -int time_getFrameNumber(); +/** + * @brief Get the real-time clock value. + * Note that this value will constantly update and change, so don't expect this value to remain consistent during a single frame. + * @return The real-time clock value in 20,12 format. + */ +u_long time_getRealTime(); + +/** + * @brief Get the current frame count. + * This is the number of frames that were counted, i.e. the number of main loop iterations since application start. + * @return The current frame's number in 32,0 format. + */ +u_long time_getFrameNumber(); + +/** + * @brief Get the current frame start time. + * The real-time value at which the current frame was started. Unlike time_getRealTime(), this value remains consistent for the entire frame. + * @return The current frame's start time in 20,12 format. + */ +u_long time_getFrameTime(); + +/** + * @brief Get the delta time between frames. + * The amount of real time passed between the last frame and the current frame. Use this for framerate-independent time stepping. + * @return The frame delta time in 4,12 format. + */ +u_short time_getDeltaTime(); + +/** + * @brief Get the average frame rate. + * The average framerate calculated over 16 frames. This gets updated periodically so isn't always entirely current. + * @return The average frame rate in 8,8 format. + */ +u_short time_getFrameRate(); #endif // __TIME_H__