- Online Community Forums » Programming Questions » Hiccups in main loop that get worse over time

This thread is locked; no one can reply to it. rss feed Print
Hiccups in main loop that get worse over time
roger levy
Member #2,513
July 2002

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.

Forth source:

\ 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.

\ Values
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

\ Flags
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
    display al_set_target_backbuffer ;

[defined] dev [if]
    variable (catch)
    : 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 ;
: std
    -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?
    eventq al_flush_event_queue
    [defined] dev [if] interact on [then] then
  etype ALLEGRO_EVENT_DISPLAY_CLOSE = if  0 ExitProcess  then
    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
    evt ALLEGRO_KEYBOARD_EVENT-keycode @ case
      <alt>    of  false to alt?  endof
      <altgr>  of  false to alt?  endof
  then ;

: 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
    then ;

variable newfs
: ?fs
    ?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> )  ( -- )

: ok
            std  ?system  'go try drop  ?step  ?render
            eventq evt al_get_next_event 0=  breaking? or
        until  ?render  \ again for sans timer
    breaking? until
    ok/ ;

:noname  0 0 0.5 clear-to-color ; >code  to 'render

Chris Katko
Member #1,881
January 2002

What OS and what specific version of Allegro?

Thanks! Definitely an interesting problem...

“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs

roger levy
Member #2,513
July 2002

Allegro 5.2.3, Windows 10

Member #7,827
October 2006

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.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

roger levy
Member #2,513
July 2002

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.

Member #7,827
October 2006

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?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

roger levy
Member #2,513
July 2002

-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.

Go to: