diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index bc8cfbff0..209f000e6 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -27227,49 +27227,61 @@ void GLUT_SPECIAL_FUNC(int key, int x, int y) { } void GLUT_SPECIALUP_FUNC(int key, int x, int y) { GLUT_key_special(key, 0); } -#ifdef QB64_WINDOWS -void GLUT_TIMER_EVENT(int ignore) { - libqb_process_glut_queue(); +static int64_t lastTick = 0; +static double deltaTick = 0; -# ifdef QB64_GLUT - glutPostRedisplay(); - int32 msdelay = 1000.0 / max_fps; - Sleep(4); - msdelay -= 4; // this forces GLUT to relinquish some CPU time to other threads but still allow for _FPS 100+ - if (msdelay < 1) - msdelay = 1; - glutTimerFunc(msdelay, GLUT_TIMER_EVENT, 0); -# endif -} -#else void GLUT_IDLEFUNC() { libqb_process_glut_queue(); -# ifdef QB64_MACOSX -# ifdef DEPENDENCY_DEVICEINPUT +#ifdef QB64_MACOSX +# ifdef DEPENDENCY_DEVICEINPUT // must be in same thread as GLUT for OSX QB64_GAMEPAD_POLL(); //[[[[NSApplication sharedApplication] mainWindow] standardWindowButton:NSWindowCloseButton] setEnabled:YES]; -# endif # endif +#endif -# ifdef QB64_GLUT +#ifdef QB64_GLUT +# ifdef QB64_LINUX if (x11_lock_request) { x11_locked = 1; x11_lock_request = 0; while (x11_locked) Sleep(1); } +# endif + int64_t curTime = GetTicks(); + + // This is how long the frame took to render + int64_t elapsed = curTime - lastTick; + + // Calculate out the error between how long the frame was 'supposed' to take vs. how long it actually took. + deltaTick += ((double)1000 / max_fps) - (double)elapsed; + + lastTick = curTime; + + // If the error is positive, we sleep for that period of time. + if (deltaTick > 0) { + int32 msdelay = deltaTick; + Sleep(msdelay); + + int64_t sleepTime = GetTicks(); + + // Subtract off the time we spent sleeping. This should leave deltaTick at zero or slightly negative. + // If it ends up negative, then we'll sleep less next frame to compensate + deltaTick -= sleepTime - lastTick; + lastTick = sleepTime; + } else { + // If we fall behind by a full frame or more, then skip to the next one + while (deltaTick < -((double)1000 / max_fps)) + deltaTick += ((double)1000 / max_fps); + } glutPostRedisplay(); - int32 msdelay = 1000.0 / max_fps; - if (msdelay < 1) - msdelay = 1; - Sleep(msdelay); -# endif -} #endif +} + #ifdef QB64_MACOSX # include @@ -29468,6 +29480,8 @@ int32 func__scaledheight() { return environment_2d__screen_scaled_height; } extern void set_dynamic_info(); int main(int argc, char *argv[]) { + clock_init(); + #if defined(QB64_LINUX) && defined(X11) XInitThreads(); diff --git a/internal/c/libqb/include/datetime.h b/internal/c/libqb/include/datetime.h index b4c4a6c9e..c8223e614 100644 --- a/internal/c/libqb/include/datetime.h +++ b/internal/c/libqb/include/datetime.h @@ -4,6 +4,14 @@ #include #include "qbs.h" +#ifdef QB64_LINUX +// Initializes the clock returned by 'GetTicks()' so that it starts from zero +// Should be called at the very beginning of the program +void clock_init(); +#else +static inline void clock_init() { } +#endif + int64_t GetTicks(); double func_timer(double accuracy, int32_t passed); diff --git a/internal/c/libqb/src/datetime.cpp b/internal/c/libqb/src/datetime.cpp index f7ca516de..bab6234e0 100644 --- a/internal/c/libqb/src/datetime.cpp +++ b/internal/c/libqb/src/datetime.cpp @@ -37,10 +37,18 @@ static int64_t orwl_gettime(void) { #endif #ifdef QB64_LINUX +static int64_t initial_tick = 0; + +void clock_init() { + // When GetTicks() is called here initial_tick is zero, so as a result + // GetTicks() returns the original value of the clock. + initial_tick = GetTicks(); +} + int64_t GetTicks() { struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); - return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; + return (tp.tv_sec * 1000 + tp.tv_nsec / 1000000) - initial_tick; } #elif defined QB64_MACOSX int64_t GetTicks() { return orwl_gettime(); } diff --git a/internal/c/libqb/src/glut-main-thread.cpp b/internal/c/libqb/src/glut-main-thread.cpp index 0efb40aa8..0b2233bf3 100644 --- a/internal/c/libqb/src/glut-main-thread.cpp +++ b/internal/c/libqb/src/glut-main-thread.cpp @@ -43,11 +43,7 @@ void GLUT_MOTION_FUNC(int x, int y); void GLUT_PASSIVEMOTION_FUNC(int x, int y); void GLUT_RESHAPE_FUNC(int width, int height); -#ifdef QB64_WINDOWS -void GLUT_TIMER_EVENT(int ignore); -#else void GLUT_IDLEFUNC(); -#endif #ifdef CORE_FREEGLUT void GLUT_MOUSEWHEEL_FUNC(int wheel, int direction, int x, int y); @@ -97,11 +93,7 @@ static void initialize_glut(int argc, char **argv) { glutDisplayFunc(GLUT_DISPLAY_REQUEST); -#ifdef QB64_WINDOWS - glutTimerFunc(8, GLUT_TIMER_EVENT, 0); -#else glutIdleFunc(GLUT_IDLEFUNC); -#endif glutKeyboardFunc(GLUT_KEYBOARD_FUNC); glutKeyboardUpFunc(GLUT_KEYBOARDUP_FUNC);