From 786f40193b60d6078e9d8a38925758e0ab8e6b00 Mon Sep 17 00:00:00 2001 From: Matthew Kilgore Date: Mon, 11 Mar 2024 12:53:42 -0400 Subject: [PATCH 1/3] Make Linux GetTicks() start from zero Since the GetTicks() is visible in the logging, it's useful to have it start from zero rather than an arbitrary number. --- internal/c/libqb.cpp | 2 ++ internal/c/libqb/include/datetime.h | 8 ++++++++ internal/c/libqb/src/datetime.cpp | 10 +++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index bc8cfbff0..a9129b33e 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -29468,6 +29468,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(); } From cdbfb94c61070d6f0e932519e4800465c4bf5b97 Mon Sep 17 00:00:00 2001 From: Matthew Kilgore Date: Mon, 25 Mar 2024 00:14:27 -0400 Subject: [PATCH 2/3] Fix GLUT redraw timing Currently the GLUT thread draws the screen too slowly, Ex. The default is supposed to be 60 FPS, but it will always draw a bit slower than 60 FPS. This is because the current logic simply inserts delays for the entire length of a frame, not taking into account how long it took us to render the last frame. The new code uses GetTicks() to measure how much time has passed since the last render, which then lets us calculate the exact amount of delay until the next frame. We additionally then measure how long the delay lasted vs. what we asked for (since any delay we do only has a minimum guarentee, it will ocassionally last a bit longer) and adjust based on that as well. The result is a perfect 60 FPS as long as rendering is quick enough. If the rendering falls behind (Ex. a slow _GL SUB is in the program) then we'll start skipping frames to get back on track. Fixes: #408 --- internal/c/libqb.cpp | 60 ++++++++++++++--------- internal/c/libqb/src/glut-main-thread.cpp | 8 --- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index a9129b33e..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 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); From 15f7988101aac8b4100b7d29ab546d6a89e1f954 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 02:11:56 +0000 Subject: [PATCH 3/3] Automatic update of ./internal/source --- internal/source/main.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/source/main.txt b/internal/source/main.txt index 98233b720..0f0ffc569 100644 --- a/internal/source/main.txt +++ b/internal/source/main.txt @@ -128636,7 +128636,7 @@ sub__resize( 1 ,NULL); if(!qbevent)break;evnt(23787,964,"ide_methods.bas");}while(r); } S_37650:; -if ((((func__resize()|*_FUNC_IDE2_LONG_FORCERESIZE))&(-(((float)(FUNC_TIMEELAPSEDSINCE(__SINGLE_QB64_UPTIME)))>((float)( 1.5E+0 )))))||is_error_pending()){ +if ((((func__resize()|*_FUNC_IDE2_LONG_FORCERESIZE))&(-(FUNC_TIMEELAPSEDSINCE(__SINGLE_QB64_UPTIME)> 1.5E+0 )))||is_error_pending()){ if(qbevent){evnt(23787,966,"ide_methods.bas");if(r)goto S_37650;} S_37651:; if ((-(*__LONG_IDESUBWINDOW!= 0 ))||is_error_pending()){ @@ -133112,7 +133112,7 @@ qbs_cleanup(qbs_tmp_base,0); if(!qbevent)break;evnt(23787,2510,"ide_methods.bas");}while(r); } S_39247:; -if ((qbs_cleanup(qbs_tmp_base,(-(((double)(*_FUNC_IDE2_DOUBLE_T))>((double)((*__DOUBLE_HELP_SEARCH_TIME+ 1 )))))|(-(((double)(*_FUNC_IDE2_DOUBLE_T))<((double)(*__DOUBLE_HELP_SEARCH_TIME))))|(((-(*_FUNC_IDE2_LONG_K==*_FUNC_IDE2_LONG_OLDK))&(-(__STRING_HELP_SEARCH_STR->len== 1 ))))))||is_error_pending()){ +if ((qbs_cleanup(qbs_tmp_base,(-(*_FUNC_IDE2_DOUBLE_T>(*__DOUBLE_HELP_SEARCH_TIME+ 1 )))|(-(*_FUNC_IDE2_DOUBLE_T<*__DOUBLE_HELP_SEARCH_TIME))|(((-(*_FUNC_IDE2_LONG_K==*_FUNC_IDE2_LONG_OLDK))&(-(__STRING_HELP_SEARCH_STR->len== 1 ))))))||is_error_pending()){ if(qbevent){evnt(23787,2511,"ide_methods.bas");if(r)goto S_39247;} S_39248:; if ((-(*_FUNC_IDE2_LONG_K==*_FUNC_IDE2_LONG_OLDK))||is_error_pending()){ @@ -135301,7 +135301,7 @@ S_39871:; if (((-(*_FUNC_IDE2_LONG_OLD__ASCII_CHR_046__MX==*__LONG_MX))&(-(*_FUNC_IDE2_LONG_OLD__ASCII_CHR_046__MY==*__LONG_MY)))||is_error_pending()){ if(qbevent){evnt(23787,3076,"ide_methods.bas");if(r)goto S_39871;} S_39872:; -if ((-(((float)(FUNC_TIMEELAPSEDSINCE(&(pass4460=*_FUNC_IDE2_DOUBLE_LAST__ASCII_CHR_046__TBCLICK))))>((float)( 0.5E+0 ))))||is_error_pending()){ +if ((-(FUNC_TIMEELAPSEDSINCE(&(pass4460=*_FUNC_IDE2_DOUBLE_LAST__ASCII_CHR_046__TBCLICK))> 0.5E+0 ))||is_error_pending()){ if(qbevent){evnt(23787,3077,"ide_methods.bas");if(r)goto S_39872;} do{ goto LABEL_REGULARTEXTBOX_CLICK; @@ -138504,7 +138504,7 @@ do{ *_FUNC_IDE2_SINGLE_CHECK__ASCII_CHR_046__TABSTOP=(*__LONG_IDECX- 1 )/ ((long double)(*_FUNC_IDE2_LONG_X)); if(!qbevent)break;evnt(23787,4132,"ide_methods.bas");}while(r); S_41009:; -if ((-(((float)(*_FUNC_IDE2_SINGLE_CHECK__ASCII_CHR_046__TABSTOP))==((float)(func_fix_double(*_FUNC_IDE2_SINGLE_CHECK__ASCII_CHR_046__TABSTOP)))))||is_error_pending()){ +if ((-(*_FUNC_IDE2_SINGLE_CHECK__ASCII_CHR_046__TABSTOP==func_fix_double(*_FUNC_IDE2_SINGLE_CHECK__ASCII_CHR_046__TABSTOP)))||is_error_pending()){ if(qbevent){evnt(23787,4133,"ide_methods.bas");if(r)goto S_41009;} S_41010:; if ((-((*__LONG_IDECX-*_FUNC_IDE2_LONG_X)<( 1 )))||is_error_pending()){ @@ -148867,7 +148867,7 @@ do{ *(int8*)(((char*)_SUB_DEBUGMODE_UDT_VWATCHPANEL)+(35))= 1 ; if(!qbevent)break;evnt(23787,7118,"ide_methods.bas");}while(r); S_44314:; -if ((-(((float)(FUNC_TIMEELAPSEDSINCE(_SUB_DEBUGMODE_SINGLE_LASTPANELCLICK)))<((float)( 0.3E+0 ))))||is_error_pending()){ +if ((-(FUNC_TIMEELAPSEDSINCE(_SUB_DEBUGMODE_SINGLE_LASTPANELCLICK)< 0.3E+0 ))||is_error_pending()){ if(qbevent){evnt(23787,7119,"ide_methods.bas");if(r)goto S_44314;} do{ *(int8*)(((char*)_SUB_DEBUGMODE_UDT_VWATCHPANEL)+(32))= 0 ; @@ -156791,7 +156791,7 @@ S_46618:; if ((*__LONG_MCLICK&(-(*_FUNC_IDEVARIABLEWATCHBOX_LONG_FOCUS== 2 )))||is_error_pending()){ if(qbevent){evnt(23787,9272,"ide_methods.bas");if(r)goto S_46618;} S_46619:; -if (((-(((float)(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEVARIABLEWATCHBOX_SINGLE_LASTCLICK)))<((float)( 0.3E+0 ))))&(-(*_FUNC_IDEVARIABLEWATCHBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[0])+((array_check((*_FUNC_IDEVARIABLEWATCHBOX_LONG_VARLISTBOX)-_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[4],_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ +if (((-(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEVARIABLEWATCHBOX_SINGLE_LASTCLICK)< 0.3E+0 ))&(-(*_FUNC_IDEVARIABLEWATCHBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[0])+((array_check((*_FUNC_IDEVARIABLEWATCHBOX_LONG_VARLISTBOX)-_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[4],_FUNC_IDEVARIABLEWATCHBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ if(qbevent){evnt(23787,9273,"ide_methods.bas");if(r)goto S_46619;} S_46620:; if (((-(*_FUNC_IDEVARIABLEWATCHBOX_LONG_DOUBLECLICKTHRESHOLD> 0 ))&(-(*__LONG_MX>=(*(int32*)(((char*)_FUNC_IDEVARIABLEWATCHBOX_UDT_P)+(0))+*_FUNC_IDEVARIABLEWATCHBOX_LONG_DOUBLECLICKTHRESHOLD)))&(-(*__LONG_IDEDEBUGMODE> 0 )))||is_error_pending()){ @@ -159324,7 +159324,7 @@ S_47371:; if ((*__LONG_MCLICK&(-(*_FUNC_IDEELEMENTWATCHBOX_LONG_FOCUS== 1 )))||is_error_pending()){ if(qbevent){evnt(23787,9989,"ide_methods.bas");if(r)goto S_47371;} S_47372:; -if (((-(((float)(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEELEMENTWATCHBOX_SINGLE_LASTCLICK)))<((float)( 0.3E+0 ))))&(-(*_FUNC_IDEELEMENTWATCHBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[0])+((array_check((*_FUNC_IDEELEMENTWATCHBOX_LONG_VARLISTBOX)-_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[4],_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ +if (((-(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEELEMENTWATCHBOX_SINGLE_LASTCLICK)< 0.3E+0 ))&(-(*_FUNC_IDEELEMENTWATCHBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[0])+((array_check((*_FUNC_IDEELEMENTWATCHBOX_LONG_VARLISTBOX)-_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[4],_FUNC_IDEELEMENTWATCHBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ if(qbevent){evnt(23787,9990,"ide_methods.bas");if(r)goto S_47372;} S_47373:; if ((-(*_FUNC_IDEELEMENTWATCHBOX_LONG_SINGLEELEMENTSELECTION== 0 ))||is_error_pending()){ @@ -160891,7 +160891,7 @@ S_47798:; if ((*__LONG_MCLICK&(-(*_FUNC_IDECALLSTACKBOX_LONG_FOCUS== 1 )))||is_error_pending()){ if(qbevent){evnt(23787,10399,"ide_methods.bas");if(r)goto S_47798;} S_47799:; -if (((-(((float)(FUNC_TIMEELAPSEDSINCE(_FUNC_IDECALLSTACKBOX_SINGLE_LASTCLICK)))<((float)( 0.3E+0 ))))&(-(*_FUNC_IDECALLSTACKBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[0])+((array_check(( 1 )-_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[4],_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ +if (((-(FUNC_TIMEELAPSEDSINCE(_FUNC_IDECALLSTACKBOX_SINGLE_LASTCLICK)< 0.3E+0 ))&(-(*_FUNC_IDECALLSTACKBOX_LONG_CLICKEDITEM==*(int32*)(((char*)_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[0])+((array_check(( 1 )-_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[4],_FUNC_IDECALLSTACKBOX_ARRAY_UDT_O[5]))*89+64)))))||is_error_pending()){ if(qbevent){evnt(23787,10400,"ide_methods.bas");if(r)goto S_47799;} do{ goto LABEL_SETIDECY; @@ -165980,7 +165980,7 @@ do{ *_FUNC_IDEHBAR_SINGLE_P=(*_FUNC_IDEHBAR_LONG_I- 1 )/ ((long double)((*_FUNC_IDEHBAR_LONG_N- 1 ))); if(!qbevent)break;evnt(23787,11775,"ide_methods.bas");}while(r); S_49275:; -if ((-(((float)(*_FUNC_IDEHBAR_SINGLE_P))<((float)( 0.5E+0 ))))||is_error_pending()){ +if ((-(*_FUNC_IDEHBAR_SINGLE_P< 0.5E+0 ))||is_error_pending()){ if(qbevent){evnt(23787,11776,"ide_methods.bas");if(r)goto S_49275;} do{ *_FUNC_IDEHBAR_LONG_X2=*_FUNC_IDEHBAR_LONG_X+ 1 ; @@ -169100,7 +169100,7 @@ sf_mem_lock=mem_lock_tmp; sf_mem_lock->type=3; if (is_error_pending()) goto exit_subfunc; S_50106:; -if ((-(((float)(*_FUNC_TIMEELAPSEDSINCE_SINGLE_STARTTIME))>((float)(func_timer(NULL,0)))))||is_error_pending()){ +if ((-(*_FUNC_TIMEELAPSEDSINCE_SINGLE_STARTTIME>func_timer(NULL,0)))||is_error_pending()){ if(qbevent){evnt(23787,12549,"ide_methods.bas");if(r)goto S_50106;} do{ *_FUNC_TIMEELAPSEDSINCE_SINGLE_STARTTIME=*_FUNC_TIMEELAPSEDSINCE_SINGLE_STARTTIME- 86400 ; @@ -177213,7 +177213,7 @@ do{ *_FUNC_IDEVBAR_SINGLE_P=(*_FUNC_IDEVBAR_LONG_I- 1 )/ ((long double)((*_FUNC_IDEVBAR_LONG_N- 1 ))); if(!qbevent)break;evnt(23787,14729,"ide_methods.bas");}while(r); S_52338:; -if ((-(((float)(*_FUNC_IDEVBAR_SINGLE_P))<((float)( 0.5E+0 ))))||is_error_pending()){ +if ((-(*_FUNC_IDEVBAR_SINGLE_P< 0.5E+0 ))||is_error_pending()){ if(qbevent){evnt(23787,14730,"ide_methods.bas");if(r)goto S_52338;} do{ *_FUNC_IDEVBAR_LONG_Y2=*_FUNC_IDEVBAR_LONG_Y+ 1 ; @@ -194684,7 +194684,7 @@ do{ *_FUNC_IDEASCIIBOX_LONG_FOCUS= 1 ; if(!qbevent)break;evnt(23787,19199,"ide_methods.bas");}while(r); S_57244:; -if (((-(((float)(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEASCIIBOX_SINGLE_LASTCLICK)))<=((float)( 0.3E+0 ))))&(-(*_FUNC_IDEASCIIBOX_LONG_LASTCLICKON==*_FUNC_IDEASCIIBOX_LONG_I)))||is_error_pending()){ +if (((-(FUNC_TIMEELAPSEDSINCE(_FUNC_IDEASCIIBOX_SINGLE_LASTCLICK)<= 0.3E+0 ))&(-(*_FUNC_IDEASCIIBOX_LONG_LASTCLICKON==*_FUNC_IDEASCIIBOX_LONG_I)))||is_error_pending()){ if(qbevent){evnt(23787,19200,"ide_methods.bas");if(r)goto S_57244;} do{ *_FUNC_IDEASCIIBOX_LONG_RELAUNCH= -1 ; @@ -203588,7 +203588,7 @@ if(!qbevent)break;evnt(23787,1169,"wiki_methods.bas");}while(r); } S_59826:; dl_continue_6855:; -}while((!(-(((float)((func_timer( 0.001E+0 ,0|1))))>((float)((*_FUNC_WIKIDLPAGE_DOUBLE_ST+*_FUNC_WIKIDLPAGE_DOUBLE_TIO))))))&&(!is_error_pending())); +}while((!(-((func_timer( 0.001E+0 ,0|1))>(*_FUNC_WIKIDLPAGE_DOUBLE_ST+*_FUNC_WIKIDLPAGE_DOUBLE_TIO))))&&(!is_error_pending())); dl_exit_6855:; if(qbevent){evnt(23787,1170,"wiki_methods.bas");if(r)goto S_59826;} }