Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Strange frame skipping

This thread is locked; no one can reply to it. rss feed Print
Strange frame skipping
Frank Drebin
Member #2,987
December 2002
avatar

Hey guys,

please take a look at the following demonstration code and tell me if you get the same result as I do (tested on Win10):

#SelectExpand
1#include <allegro5/allegro.h> 2 3int delay=0; 4bool draw=0; 5bool quit=0; 6 7int main(int argc,char** argv) 8{ 9 al_init(); 10 al_install_mouse(); 11 12 ALLEGRO_EVENT event; 13 ALLEGRO_MOUSE_STATE mouse; 14 ALLEGRO_DISPLAY* display=al_create_display(640,480); 15 ALLEGRO_EVENT_QUEUE* queue=al_create_event_queue(); 16 ALLEGRO_TIMER* timer=al_create_timer(0.01); 17 al_start_timer(timer); 18 al_register_event_source(queue,al_get_timer_event_source(timer)); 19 20 while (!quit) 21 { 22 al_wait_for_event(queue,&event); 23 24 if (event.timer.source==timer) 25 { 26 draw=0; 27 if (delay>0) delay--; 28 29 al_get_mouse_state(&mouse); 30 if ((al_mouse_button_down(&mouse,1)) && (delay==0)) 31 { 32 draw=1; 33 delay=10; 34 } 35 if (al_mouse_button_down(&mouse,2)) quit=1; 36 } 37 38 if (al_is_event_queue_empty(queue)) 39 { 40 al_clear_to_color(al_map_rgb(0,0,0)); 41 if (draw) al_clear_to_color(al_map_rgb(0,128,0)); 42 al_flip_display(); 43 } 44 } 45 46 al_destroy_display(display); 47 al_destroy_event_queue(queue); 48 al_destroy_timer(timer); 49 50 return 0; 51}

So what this code should do is to show a green blinking screen if you hold down the left mouse button. When I hold down the left mouse button for some seconds, release it and start all over I get:
hold down mouse button - blinking screen
hold down mouse button - blinking screen
hold down mouse button - no blinking screen
hold down mouse button - no blinking screen
hold down mouse button - blinking screen
hold down mouse button - blinking screen

Any idea what's going on here?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

You should always redraw on the timer event, and then set redraw to false.

Easier logic would be :

#SelectExpand
1while (have_events) { 2 if (timer_event) { 3 if (blink) { 4 green = !green; 5 } 6 redraw = true; 7 } 8 if (mouse_event) { 9 blink = mouse_button_down(); 10 } 11} 12if (redraw) { 13 draw(); 14 redraw = false; 15}

Frank Drebin
Member #2,987
December 2002
avatar

OK, your approach would avoid this effect.

However, as I understand it (maybe I'm missing something :P ), using this approach drawing a frame will never be skipped once the timer event occured. But if you run this loop on a slow computer that can't handle all draw calls you want to have frames skipped until the logic catches up. E.g. an explosion that last only one frame happens but cannot be drawn instantly then the explosion shouldn't be drawn 10 frames later.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

The approach you used is bad because if you have multiple timer events in the queue, they can skip the draw = 1 call and you don't turn green. If you get a timer event (a draw timer) then redraw. Process all the remaining events first and limit your logical update call to once per graphic frame. That will slow down gracefully if you get too many timer events, and won't skip any frames. Or process all your logic at once, thus possibly skipping frames and then redraw.

Izual
Member #2,756
September 2002
avatar

You could also make it without polling the mouse. With events only.

I have edited your code to add extra color to the flashing effect. Added another timer to the mix.
And this will also skip frames when your computer is slow:

#SelectExpand
1#include <stdbool.h> 2#include <stdio.h> 3 4#include "allegro5/allegro.h" 5 6enum 7 { 8 MY_DELAY = 10 9 }; 10 11int delay; 12 13bool draw = false; 14bool quit = false; 15bool b1 = false; 16 17ALLEGRO_COLOR color; 18 19int main(int argc,char** argv) 20 { 21 al_init(); 22 al_install_mouse(); 23 24 ALLEGRO_EVENT event; 25 26 ALLEGRO_DISPLAY* display=al_create_display(640,480); 27 ALLEGRO_EVENT_QUEUE* queue=al_create_event_queue(); 28 29 ALLEGRO_TIMER* draw_timer = al_create_timer( 1.0 / 60.0 ); 30 ALLEGRO_TIMER* other_timer = al_create_timer( 1.0 / 3.0 ); 31 32 al_register_event_source(queue,al_get_timer_event_source(draw_timer)); 33 al_register_event_source(queue,al_get_timer_event_source(other_timer)); 34 35 al_register_event_source( queue, al_get_mouse_event_source() ); 36 37 al_start_timer( draw_timer ); 38 al_start_timer( other_timer ); 39 40 while( !quit ) 41 { 42 al_wait_for_event(queue,&event); 43 44 switch( event.type ) 45 { 46 case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: 47 if( event.mouse.button == 1 ) 48 { 49 b1 = true; 50 delay = MY_DELAY; 51 color = al_map_rgb( 0, 128, 0 ); 52 } 53 54 if( event.mouse.button == 2 ) 55 quit = true; 56 break; 57 58 case ALLEGRO_EVENT_MOUSE_BUTTON_UP: 59 if( event.mouse.button == 1 ) 60 b1 = false; 61 break; 62 63 case ALLEGRO_EVENT_TIMER: 64 // Some other timer to do someting: 65 if( event.timer.source == other_timer ) 66 { 67 printf( "Other timer ticked! Count = %ld\n", event.timer.count ); 68 } 69 70 // Handle draw timer: 71 if( event.timer.source == draw_timer ) 72 { 73 draw = true; 74 75 // Handle delay: 76 if( b1 ) 77 { 78 if( delay > 0 ) 79 delay--; 80 else 81 { 82 unsigned char r, g, b; 83 84 delay = MY_DELAY; 85 86 al_unmap_rgb( color, &r, &g, &b ); 87 88 if( g != 0 ) 89 color = al_map_rgb( 128, 0, 0 ); 90 else 91 color = al_map_rgb( 0, 128, 0 ); 92 } 93 } 94 } 95 break; 96 97 default: 98 /* 99 // Unhandled events: 100 printf( "Unhandled event type %d!\n" ); 101 */ 102 break; 103 } 104 105 // Draw stuff if we can: 106 if( draw && al_is_event_queue_empty( queue ) ) 107 { 108 draw = false; 109 110 if( b1 ) 111 al_clear_to_color( color ); 112 else 113 al_clear_to_color( al_map_rgb( 0, 0, 0 ) ); 114 115 al_flip_display(); 116 } 117 } 118 119 al_destroy_display(display); 120 al_destroy_event_queue(queue); 121 al_destroy_timer(draw_timer); 122 al_destroy_timer( other_timer ); 123 124 return 0; 125}

Frank Drebin
Member #2,987
December 2002
avatar

Edgar, I see pros and cons for both approaches. I'm migrating from Allegro 4 where I am used to have frames skipped How_can_I_make_my_game_run_at_the_same_speed_on_any_computer_ and when using Allegro 4 this maked sense for me. I'm going to rethink that now working with Allegro 5...

By the way, I still don't understand why in my approach the flashing is not visible sometimes event when some frames are skipped. If you modify the example and add one line to indicate frame skipping like this:

if (al_is_event_queue_empty(queue))
{
   al_clear_to_color(al_map_rgb(0,0,0));
   if (draw) al_clear_to_color(al_map_rgb(0,128,0));
   al_flip_display();
}
else printf("Frame skipped\n");

you'll see that frames are skipped rarely, but the flashing occurs 10 times per second so at a much higher frequency than frames are skipped so that's the strange thing for me.

Izul, your approach looks quite similar to what Edgar suggested...

[EDIT]
To make it more clear that something strange is happening, try this code:

#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro.h> 3int delay=0; 4bool draw=0; 5bool quit=0; 6 7int main(int argc,char** argv) 8{ 9 al_init(); 10 al_install_mouse(); 11 ALLEGRO_EVENT event; 12 ALLEGRO_MOUSE_STATE mouse; 13 ALLEGRO_DISPLAY* display=al_create_display(640,480); 14 ALLEGRO_EVENT_QUEUE* queue=al_create_event_queue(); 15 ALLEGRO_TIMER* timer=al_create_timer(0.01); 16 al_start_timer(timer); 17 al_register_event_source(queue,al_get_timer_event_source(timer)); 18 19 while (!quit) 20 { 21 al_wait_for_event(queue,&event); 22 23 if (event.timer.source==timer) 24 { 25 draw=0; 26 if (delay>0) delay--; 27 28 al_get_mouse_state(&mouse); 29 if ((al_mouse_button_down(&mouse,1)) && (delay==0)) 30 { 31 draw=1; 32 delay=10; 33 } 34 35 if (al_mouse_button_down(&mouse,2)) quit=1; 36 } 37 38 if (al_is_event_queue_empty(queue)) 39 { 40 al_clear_to_color(al_map_rgb(0,0,0)); 41 printf("."); 42 if (draw) 43 { 44 al_clear_to_color(al_map_rgb(0,128,0)); 45 printf("D"); 46 } 47 al_flip_display(); 48 } 49 else printf("FRAME SKIPPED\n"); 50 } 51 52 al_destroy_display(display); 53 al_destroy_event_queue(queue); 54 al_destroy_timer(timer); 55 return 0; 56}

What I see is that "D" is printed to the console without blinking screen ???

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

You have two options - run one graphical frame per logical frame, slowing down gracefully, or run one graphical frame for all logic ticks, skipping frames.

If there is more than one timer event when delay reaches zero, draw will be set to 1 in the first event, and then set to 0 in the second event, as if it never happened. You're skipping frames when you don't need to.

I ran your latest example. I think it's probably just a case of unsynced output. I get 'D.......D.......D.......' too, and the green flash isn't visible sometimes.

Frank Drebin
Member #2,987
December 2002
avatar

OK guys, I think I figured it out by playing around with the ALLEGRO_VSYNC option: Everything is actually drawn, however, sometimes it is not visible on the screen because Vsync is disabled by default. Therefore it may or may not happen that the green screen that should be shown for one frame only is "skipped" by the monitor refreshing.

In any case, thanks for your input :D

[EDIT]

I think it's probably just a case of unsynced output

OK somebody was faster than me...

Chris Katko
Member #1,881
January 2002
avatar

Aliasing:

{"name":"alias.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/5\/d54a0a82e57d58d59ccbe5a77e9e25a5.png","w":477,"h":248,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/5\/d54a0a82e57d58d59ccbe5a77e9e25a5"}alias.png

Notice how if you look at the red dots, it appears to be a lower frequency.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Go to: