Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » Allegro stops getting messages at high game FPS (possible bug) [5.0.5]

This thread is locked; no one can reply to it. rss feed Print
Allegro stops getting messages at high game FPS (possible bug) [5.0.5]
Max Savenkov
Member #4,613
May 2004
avatar

I encountered a problem when using Allegro under Windows with DIRECTX driver. Sometimes, when I press three keys simultaneously for some time, my game would stop receiving any keyboard events from Allegro queue.

What's even more interesting, if I switch to some other window, I can't switch back to Allegro window: it doesn't receive focus. But game continues, and screen updates correctly.

This only happens if I don't limit my game's FPS, i.e. does not sleep anywhere in my main thread. If I put sleep there or slow game down by other means (like writing something to file every frame), the bug would disappear.

This behaviour started with Allegro 5.0.4 and continued in 5.0.5 (I don't remember seeing it before, but I may be wrong). It happens when FPS are around 150-200 (in debug version of game + debug version of library), completely disappear if FPS are around 60-80.

I shouldn't probably leave my FPS unlimited, but still it's an interesting behaviour: why does events stop coming only if I have high FPS AND press lots of keys? Mind you, it continues even after I release those keys...

So, is it a bug, or more-or-less intended behaviour of Allegro?

I was going to root around in Allegro insides to see if I can provide more information, but pre-built 5.0.5 monolith library does not correspond to 5.0.5 sources somehow, and I don't understand how to build monolith library myself.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Max Savenkov
Member #4,613
May 2004
avatar

It's a big project to paste it all here, but in short it looks like this:

#SelectExpand
1void AllegroInput::Init() 2{ 3 m_queue = al_create_event_queue(); 4 if ( !al_install_keyboard() ) 5 { 6 GetLog().Log( CommonLog(), LL_CRITICAL, "AllegroInput: Failed to install keyboard driver" ); 7 return; 8 } 9} 10 11void AllegroInput::Update() 12{ 13 ALLEGRO_EVENT ev; 14 while( al_get_next_event( m_queue, &ev ) ) 15 { 16 if ( ev.any.type == ALLEGRO_EVENT_KEY_DOWN ) 17 AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_DOWN, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) ); 18 else if ( ev.any.type == ALLEGRO_EVENT_KEY_UP ) 19 AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_UP, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) ); 20 else if ( ev.any.type == ALLEGRO_EVENT_KEY_CHAR && m_repeatTimer <= 0 ) 21 { 22 AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_REPEAT, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) ); 23 // Never mind that, there is more code to deal with it in real application. 24 m_repeatTimer = 100; 25 } 26 } 27 28 ALLEGRO_KEYBOARD_STATE state; 29 al_get_keyboard_state( &state ); 30 31 for ( int i = 0; i < ALLEGRO_KEY_MAX; ++i ) 32 { 33 if ( al_key_down( &state, i ) && m_keyStates[ i ] ) 34 AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_PRESSED, i, 0, 0 ) ); 35 36 m_keyStates[ i ] = al_key_down( &state, i ) != 0; 37 } 38} 39 40void Game::Run() 41{ 42 while( !m_stop ) 43 { 44 // Frame length timer 45 cycleTimer.restart(); 46 47 // Calculate dt for cycle 48 float dt = dtTimer.elapsed() ; 49 dtTimer.restart(); 50 51 // Get input events from Allegro and stuff 'em into my own queue in my own format 52 input.Update(); 53 // Feed my events to various handlers 54 ProcessInput( input ); 55 56 // Do object updates'n'stuff 57 m_currentState.Update( dt ); 58 59 // Render stuff 60 m_currentState.Render( render ); 61 62 // Copy to screen 63 render.Flip(); 64 65 // Get frame length 66 float cycleTime = cycleTimer.elapsed(); 67 if ( cycleTime < 1 / 60.0f ) 68 { 69 if ( m_frameLimit ) 70 sleep( (1.0f / 60.0f - cycleTime)*1000 ); 71 } 72 }

A log placed into Input::Update will dutifully print incoming events until bug is triggered. Then, while Update is executed, nothing will be printed. BTW, sometimes, after a while the bug may go away (i.e. events begin to appear again), but sometimes it does not. It seems to be related to how long I hold down keys before releasing them. It almost looks like Allegro message processing thread is getting swamped with messages, without having time to process them...

Edgar Reynaldo
Member #8,592
May 2007
avatar

   float cycleTime = cycleTimer.elapsed();
   if ( cycleTime < 1 / 60.0f )
   {
      if ( m_frameLimit )
         sleep( (1.0f / 60.0f - cycleTime)*1000 );
   }

Check the link for sleep - it rests for that many seconds...

So, if your frame takes less than 1/60 of a second and m_frameLimit is non zero, then you will rest for somewhere between 0 and 1000/60 seconds.

However, that is not the behaviour you described in your first post - unresponsive input when there is no rest.

Max Savenkov
Member #4,613
May 2004
avatar

It's actually a _sleep, which takes milliseconds.

Trezker
Member #1,739
December 2001
avatar

I use al_rest in my programs.

Edgar Reynaldo
Member #8,592
May 2007
avatar

I encountered a problem when using Allegro under Windows with DIRECTX driver. Sometimes, when I press three keys simultaneously for some time, my game would stop receiving any keyboard events from Allegro queue.

Try the OpenGL driver and see if it persists - I think the Windows keyboard input module is independent of DX and OpenGL though.

Quote:

What's even more interesting, if I switch to some other window, I can't switch back to Allegro window: it doesn't receive focus. But game continues, and screen updates correctly.

Is this only after pressing a 3 key combination, or just in general?

Quote:

This only happens if I don't limit my game's FPS, i.e. does not sleep anywhere in my main thread. If I put sleep there or slow game down by other means (like writing something to file every frame), the bug would disappear.

This behaviour started with Allegro 5.0.4 and continued in 5.0.5 (I don't remember seeing it before, but I may be wrong). It happens when FPS are around 150-200 (in debug version of game + debug version of library), completely disappear if FPS are around 60-80.

There's no point trying to display more frames than your monitors refresh rate. Set a timer to the refresh rate, and set a redraw flag when it ticks.

Just a guess, but you might be causing a starvation issue with the event queue. If you always have a lock (necessary to maintain integrity of the queue) on it by constantly checking it, then the input thread may never have a chance to fill the queue with new events.

It's actually a _sleep, which takes milliseconds.

Why doesn't the underscore show up?

Max Savenkov
Member #4,613
May 2004
avatar

Try the OpenGL driver and see if it persists - I think the Windows keyboard input module is independent of DX and OpenGL though.

Can try it with OpenGL, since this driver automagically locks FPS to my refresh rate. Does it have vsync enabled by default? Or is it normal OpenGL behaviour?

Quote:

There's no point trying to display more frames than your monitors refresh rate. Set a timer to the refresh rate, and set a redraw flag when it ticks.

Well, I mostly used unlimited FPS as a means to gauge game performance: how much free time per frame I still have, how much improvement does an optimization bring and so on.

Quote:

Just a guess, but you might be causing a starvation issue with the event queue. If you always have a lock (necessary to maintain integrity of the queue) on it by constantly checking it, then the input thread may never have a chance to fill the queue with new events.

While this is possible, I don't understand why does this happens in this specific way. I think I'll dig around some more when I have more time.

Quote:

Why doesn't the underscore show up?

Actually, it's "system.Sleep()", a call to my wrapper, but I simplified code when posting here to remove unimportant details.

Still, does anyone know how to compile monolith library? I see no such option in standard Allegro distribution...

Edgar Reynaldo
Member #8,592
May 2007
avatar

Can try it with OpenGL, since this driver automagically locks FPS to my refresh rate. Does it have vsync enabled by default? Or is it normal OpenGL behaviour?

You can try disabling vsync - I don't know what the default value is - it may be on, or program specific. al_set_new_display_option(ALLEGRO_VSYNC , 2 , ALLEGRO_REQUIRE);

Quote:

Well, I mostly used unlimited FPS as a means to gauge game performance: how much free time per frame I still have, how much improvement does an optimization bring and so on.

al_get_time would give you the measurements necessary to perform the same calculations without actually running full bore.

Quote:

Actually, it's "system.Sleep()", a call to my wrapper, but I simplified code when posting here to remove unimportant details.

Since I was mislead by it, I wouldn't call it an unimportant detail...

Quote:

Still, does anyone know how to compile monolith library? I see no such option in standard Allegro distribution...

cmake doesn't support a monolith build. You could make your own static monolith with 'ar' by linking all the static libraries together though. No way to build a dynamic monolith without relinking all the allegro objects though. Why not just use the singular dlls instead? There shouldn't be any difference in the code, just the way it is linked to.

Max Savenkov
Member #4,613
May 2004
avatar

OK, the bug disappeared just as mysteriously as it has appeared. Probably wasn't my code or Allegro at all, but some external factor 0_o.

But in process of examining it, I learned how to disabled vsync for OpenGL, and made two discoveries:

1) Using OpenGL instead of DirectX speeds my game up by considerable amount (if I disable FPS limit, it goes from ~800 with DIRECTX to ~1200 with OpenGL in Release mode). Woo-hoo, cool.

2) If I ENABLE vsync instead of using my own frame-limiter, game becomes choppy, despite constant 60FPS rate.

I can't understand why 2 happens (I don't understand why 1 happens too, but it makes me happy, so I don't worry about it :) ). Why does unlimited FPS + al_rest feels better than vsynced version, despite the same FPS and the same delta-times for frames?!

Edgar Reynaldo
Member #8,592
May 2007
avatar

Re 2) It's probably a synchronization issue. Your logic and drawing may take just long enough to go just past the next retrace so when it vsync's, it has to wait an entire refresh until it displays. That or your redraw timer starts very late in the refresh period. You can try synchronizing your timer with vsync like so :

float MONITOR_HZ = 60.0;
ALLEGRO_TIMER* redraw_timer = al_create_timer(1.0/MONITOR_HZ);
//...
al_wait_for_vsync();
al_start_timer(redraw_timer);

Hopefully, that should give your logic and drawing code as much time as possible between refreshes.

Elias
Member #358
May 2000

As long as you don't use very high resolution and/or sub-pixel precision you'll always have choppiness because the monitor is updated a fixed amount of times (as low as 60 times per second usually). The easiest solution is to simply ignore it as it will be hard to notice when a lot of things move on the screen. If your game has something like a single big slow moving ball moving around it can be a real problem though.

--
"Either help out or stop whining" - Evert

Go to: