Hiccups in main loop that get worse over time
My main loop uses a frame timer which I turn off when the display loses focus and re-enable when it comes back. I've noticed a problem where if I step away for an hour or so and come back, I get these long hiccups (stalls) that seem to get longer the longer I wasn't paying attention to the window. I'm not sure if it happens whether or not the display has lost focus, but I may try testing it with the window focused tonight. Has anyone run into this? It doesn't make much sense. I don't know what the system is doing during these stalls. I realized that during these stalls, frames still seem to be processed, just not displayed. So the effect is extreme frame skipping every second for a moment and then smooth action for about a second and then another skip and so on.
\ Universal main loop
\ Skips rendering frames if logic takes too long (up to 4 frames are skipped)
\ You can disable the frame timer to save CPU when making editors etc
\ When the window is switched away from the timer will be disabled, and re-enabled when
\ it regains focus.
\ The loop has some common controls:
\ F12 - break the loop
\ ALT-F4 - quit the process
\ ALT-ENTER - toggle fullscreen
\ TILDE - toggles a flag called INFO, doesn't do anything on its own.
0 value #frames \ frame counter.
0 value renderr
0 value steperr
0 value alt? \ part of fix for alt-enter bug when game doesn't have focus
0 value breaking?
0 value lag \ completed ticks
0 value 'go
0 value 'step
0 value 'render
variable info \ enables debugging mode display
variable allowwin allowwin on
variable fs \ is fullscreen enabled?
variable interact \ if on, cmdline will receive keys. check if false before doing game input, if needed.
create fse /ALLEGRO_ANY_EVENT /allot \ fullscreen event
#999 constant EVENT_FULLSCREEN
: poll pollKB pollJoys [defined] dev [if] pause [then] ;
: break true to breaking? ;
: -break false to breaking? ;
: unmount ( -- )
0 0 displayw displayh al_set_clipping_rectangle
ALLEGRO_ADD ALLEGRO_ALPHA ALLEGRO_INVERSE_ALPHA ALLEGRO_ADD ALLEGRO_ONE ALLEGRO_ONE al_set_separate_blender
display al_set_target_backbuffer ;
[defined] dev [if]
: try dup -exit sp@ cell+ >r code> catch (catch) ! r> sp! (catch) @ ;
: try dup -exit call 0 ;
\ : alt? evt ALLEGRO_KEYBOARD_EVENT-modifiers @ ALLEGRO_KEYMOD_ALT and ;
etype ALLEGRO_EVENT_DISPLAY_RESIZE = if
-timer display al_acknowledge_resize +timer \ we have to turn off the timer to avoid a race condition
\ where bitmaps aren't recreated before trying to draw to them
etype ALLEGRO_EVENT_DISPLAY_SWITCH_OUT = if -timer -audio then
etype ALLEGRO_EVENT_DISPLAY_SWITCH_IN = if clearkb +timer +audio false to alt?
[defined] dev [if] interact on [then] then
etype ALLEGRO_EVENT_DISPLAY_CLOSE = if 0 ExitProcess then
etype ALLEGRO_EVENT_KEY_DOWN = if
evt ALLEGRO_KEYBOARD_EVENT-keycode @ case
<alt> of true to alt? endof
<altgr> of true to alt? endof
<enter> of alt? -exit fs toggle endof
<f4> of alt? -exit 0 ExitProcess endof
<f12> of break endof
<tilde> of alt? -exit info toggle endof
etype ALLEGRO_EVENT_KEY_UP = if
evt ALLEGRO_KEYBOARD_EVENT-keycode @ case
<alt> of false to alt? endof
<altgr> of false to alt? endof
: fsflag fs @ allowwin @ not or ;
: ?fserr 0= if fs off " Fullscreen is not supported by your driver." alert then ;
variable winx variable winy
: ?poswin \ save/restore window position when toggling in and out of fullscreen
display al_get_display_flags ALLEGRO_FULLSCREEN_WINDOW and if
fs @ 0= if r> call display winx @ winy @ al_set_window_position then
fs @ if display winx winy al_get_window_position then
?poswin display ALLEGRO_FULLSCREEN_WINDOW fsflag al_toggle_display_flag ?fserr
fs @ newfs @ <> if
fse EVENT_FULLSCREEN emit-user-event
fs @ newfs ! ;
defer ?overlay ' noop is ?overlay \ render ide
defer ?system ' noop is ?system \ system events
: render unmount 'render try to renderr ;
: step 'step try to steperr ;
: update? timer? if lag dup -exit drop then eventq al_is_event_queue_empty lag 4 >= or ;
: wait eventq evt al_wait_for_event ;
: ?render update? -exit 1 +to #frames ?fs render unmount ?overlay al_flip_display
0 to lag ;
: ?step etype ALLEGRO_EVENT_TIMER = if 1 +to lag poll step then ;
: /ok resetkb -break >display +timer render ;
: ok/ eventq al_flush_event_queue -timer >ide -break ;
: render> r> to 'render ; ( -- <code> ) ( -- )
: step> r> to 'step ; ( -- <code> ) ( -- )
: go> r> to 'go 0 to 'step ; ( -- <code> ) ( -- )
std ?system 'go try drop ?step ?render
eventq evt al_get_next_event 0= breaking? or
until ?render \ again for sans timer
:noname 0 0 0.5 clear-to-color ; >code to 'render
What OS and what specific version of Allegro?
Thanks! Definitely an interesting problem...
Allegro 5.2.3, Windows 10
Try switching between OpenGL and Direct3D. Not sure how you do that in Elvish though...
I'll try reproducing it with ex_draw_bitmap 's event loop, although I'm not sure how well that maps to yours.
The principle is pretty simple. Create a timer using the refresh rate retrieved from Allegro. In the event loop, when the timer event fires do game logic and increment a "lag" variable for frame-skipping. When all events are processed, or if lag is over 4, do a render, and reset lag.
Any luck on your end? I feel like I'll need to learn a bit of Forth to verify that it's not your code. Have you tried calling al_stop_timer instead of just setting timer to false?
Thread #616994. Printed from Allegro.cc
-TIMER and +TIMER actually do stop and restart the timer. Sorry, I didn't include their definitions. (If you don't already know this about Forth, there are no reserved characters except for white space, so those are actually the names of functions I've written.)
What I've done is add event logging so the next time I experience the issue (which I haven't yet, strangely) I'll be able to see if a bunch of events are being generated during a hiccup, which is my current theory.