Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Lag in the main loop

Credits go to Edgar Reynaldo, Elias, roger levy, and trictonicmp for helping out!
This thread is locked; no one can reply to it. rss feed Print
Lag in the main loop
moisesdias
Member #16,905
October 2018

Hello guys, i'm facing a problem with my program:
I've only write a code for make a circle walk 10 pixels on the screem 60 times per second, but the circle sometimes "jumps", like if it have walked 50 pixels or more instead of 10, I'm going to post my main loop, hope you can help me finding where is the problem

#SelectExpand
1while(!finish) 2 { 3 while(!al_is_event_queue_empty(eventQueue)) 4 { 5 al_wait_for_event(eventQueue, &event); 6 7 if(event.type == ALLEGRO_EVENT_KEY_DOWN) 8 Keys::keyPressed(keys, event.keyboard.keycode); 9 10 if(event.type == ALLEGRO_EVENT_KEY_UP) 11 Keys::keyReleased(keys, event.keyboard.keycode); 12 13 if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 14 finish = true; 15 16 if(event.type == ALLEGRO_EVENT_TIMER) 17 { 18 gamePlay.update(keys, finish); 19 20 draw = true; 21 } 22 } 23 if(draw) 24 { 25 draw = false; 26 gamePlay.drawGame(); 27 } 28} 29 30 31 32void GamePlay::update(bool* keys, bool& finish) 33{ 34 if(posX > 1000) 35 test = false; 36 if (posX < 50) 37 test = true; 38 if(test) 39 posX+=10; 40 else 41 posX-=10; 42} 43 44 45 46void GamePlay::drawGame() 47{ 48 al_draw_filled_circle(posX,posY,50,al_map_rgba(255,0,0,255)); 49 al_flip_display(); 50 al_clear_to_color(al_map_rgb(0,0,0));/**isso afeta o backbuffer**/ 51}

Elias
Member #358
May 2000

Remove the ! before al_is_event_queue_empty(). You want to call al_wait_for_event when the queue is empty and wait until there is an event. The way you have it you would only wait for an event if there already is one, so never... which means your loop just runs through millions of times heating up a CPU core and making everything unstable and jumpy.

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

moisesdias
Member #16,905
October 2018

Hi Elias, thanks for your reply, I tried to change the main loop but unfortunely my drawings are still jumpy.

I tried a lot of new combinations, but none of them seems to solve the problem

#SelectExpand
1while(!finish) 2 { 3 4 while(1) 5 { 6 al_wait_for_event(eventQueue, &event); 7 8 if(event.type == ALLEGRO_EVENT_KEY_DOWN) 9 Keys::keyPressed(keys, event.keyboard.keycode); 10 11 if(event.type == ALLEGRO_EVENT_KEY_UP) 12 Keys::keyReleased(keys, event.keyboard.keycode); 13 14 if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 15 finish = true; 16 17 if(event.type == ALLEGRO_EVENT_TIMER) 18 gamePlay.update(keys, finish); 19 20 if(al_is_event_queue_empty(eventQueue)) 21 gamePlay.drawGame(); 22 } 23 24 }

I also tried a logic with two timers, one for the logic and other for drawing, but it have not worked too.

trictonicmp
Member #16,611
December 2016

#SelectExpand
1//-------------------------------Main loop-------------------------------- 2while (!done) 3{ 4 do 5 { 6 //------------------------Getting events-------------------------- 7 ALLEGRO_EVENT event; 8 al_wait_for_event(eventQueue, &event); 9 10 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 11 done = true; 12 13 14 15 if (event.type == ALLEGRO_EVENT_TIMER) 16 { 17 redraw = true; 18 } 19 20 //-----------------------Getting events END----------------------- 21 } while (!al_is_event_queue_empty(eventQueue)); 22 23 24 25 //-------------------------Redrawing elements------------------------- 26 if (redraw) 27 { 28 al_flip_display(); 29 redraw = false; 30 } 31 //-----------------------Redrawing elements END----------------------- 32 } 33//-----------------------------Main loop END------------------------------

I always use this as my main loop, the only thing that I notice is that your code does not have the "ALLEGRO_EVENT event;" before "al_wait_for_event(eventQueue, &event);"

Elias
Member #358
May 2000

trictonicmp's loop looks right to me. The key points are:

  • call al_wait_for_event when there's nothing else to do

  • only redraw when there was a timer event

  • only redraw when there are no events left in the queue

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

roger levy
Member #2,513
July 2002

Good thread. I'm going to try upgrading Ramen's loop using the advice in here.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Fixing the formatting and making it more condensed :

#SelectExpand
1 bool redraw = true; 2 bool quit = false; 3 4 while (!quit) { 5 if (redraw) { 6 Draw(); 7 redraw = false; 8 } 9 do { 10 ALLEGRO_EVENT ev; 11 al_wait_for_event(q , &ev); 12 if (ev.type == ALLEGRO_EVENT_TIMER) { 13 Update(dt); 14 redraw = true; 15 } 16 /// Handle events here 17 } while (!al_is_event_queue_empty(q)); 18 }

This is pretty much the standard loop, and has been on the wiki for a while. It's just your basic event loop.

moisesdias
Member #16,905
October 2018

Guys, thanks for all the answers, but i'm beginning to get worried about it.. I have removed all the miscellaneous code and I'm going to put here what i'm compiling, it is only three classes, maybe this way it will be easier to find my problem, if there is one:

it's really strange, it only prints a red ball moving from one side to another, but it still give some 'jumps' at every two seconds approximately

edit: if you guys want, you can run this program only with this code below, i don't discard the possibility of the problem be my installation, and not in my code, who knows..

#SelectExpand
1//----------the main class---------- 2 3#include "GameManager.h" 4int main() 5{ 6 GameManager US; 7 return 0; 8} 9 10//----------GameManager header---------- 11 12#ifndef GAMEMANAGER_H 13#define GAMEMANAGER_H 14#include "GamePlay.h" 15 16class GameManager 17{ 18 public: 19 GameManager(); 20 void manage(); 21 22 private: 23 GamePlay gamePlay; 24 25 ALLEGRO_DISPLAY* screen; 26 ALLEGRO_EVENT_QUEUE* eventQueue; 27 ALLEGRO_TIMER* timer; 28 29}; 30 31#endif // GAMEMANAGER_H 32 33 34//----------GameManager source---------- 35 36#include "GameManager.h" 37 38 39GameManager::GameManager() 40{ 41 42 draw = true; 43 finish = false; 44 screen = NULL; 45 eventQueue = NULL; 46 timer = NULL; 47 48 if(!al_init()) 49 finish = true; 50 51 screen = al_create_display(1200,800); 52 53 if(!screen || !al_init_primitives_addon()) 54 finish = true; 55 56 timer = al_create_timer(1.0 / 60); 57 58 eventQueue = al_create_event_queue(); 59 60 al_register_event_source(eventQueue, al_get_timer_event_source(timer)); 61 al_register_event_source(eventQueue, al_get_display_event_source(screen)); 62 63 al_start_timer(timer); 64 65 manage(); 66} 67 68 69 70void GameManager::manage() 71{ 72 bool draw = true; 73 bool finish = false; 74 75 while (!finish) 76 { 77 if (draw) 78 { 79 gamePlay.drawGame(); 80 draw = false; 81 } 82 do 83 { 84 ALLEGRO_EVENT ev; 85 al_wait_for_event(eventQueue, &ev); 86 if (ev.type == ALLEGRO_EVENT_TIMER) 87 { 88 gamePlay.update(); 89 draw = true; 90 } 91 92 // i have also tried to put the gamePlay.updat() here 93 } 94 while (!al_is_event_queue_empty(eventQueue)); 95 } 96} 97 98 99 100//----------GamePlay header---------- 101 102#ifndef GAMEPLAY_H 103#define GAMEPLAY_H 104 105#include <allegro5/allegro.h> 106#include <allegro5/allegro_primitives.h> 107class GamePlay 108{ 109 public: 110 GamePlay(); 111 void update(); 112 void drawGame(); 113 114 private: 115 int posX; 116 int posY; 117 bool test; 118}; 119 120#endif // GAMEPLAY_H 121 122 123//----------GamePlay source---------- 124 125#include "GamePlay.h" 126 127GamePlay::GamePlay() 128{ 129 posX = 500; 130 posY = 500; 131 test = false; 132} 133void GamePlay::update() 134{ 135 if(posX > 1000) 136 test = false; 137 if (posX < 50) 138 test = true; 139 if(test) 140 posX+=10; 141 else 142 posX-=10; 143} 144void GamePlay::drawGame() 145{ 146 al_draw_filled_circle(posX,posY,50,al_map_rgba(255,0,0,255)); 147 al_flip_display(); 148 al_clear_to_color(al_map_rgb(0,0,0)); 149}

Edgar Reynaldo
Member #8,592
May 2007
avatar

If you're going to provide code, provide something that compiles please.

Here's your code fixed up and compiling :

#SelectExpand
1//----------the main class---------- 2 3 4//----------GamePlay header---------- 5 6#ifndef GAMEPLAY_H 7#define GAMEPLAY_H 8 9#include <allegro5/allegro.h> 10#include <allegro5/allegro_primitives.h> 11class GamePlay 12{ 13 public: 14 GamePlay(); 15 void update(); 16 void drawGame(); 17 18 private: 19 int posX; 20 int posY; 21 bool test; 22}; 23 24#endif // GAMEPLAY_H 25 26 27//#include "GameManager.h" 28 29//----------GameManager header---------- 30 31#ifndef GAMEMANAGER_H 32#define GAMEMANAGER_H 33//#include "GamePlay.h" 34 35class GameManager 36{ 37 public: 38 GameManager(); 39 void manage(); 40 41 private: 42 GamePlay gamePlay; 43 44 ALLEGRO_DISPLAY* screen; 45 ALLEGRO_EVENT_QUEUE* eventQueue; 46 ALLEGRO_TIMER* timer; 47 48 bool draw; 49 bool finish; 50}; 51 52#endif // GAMEMANAGER_H 53 54 55//----------GameManager source---------- 56 57//#include "GameManager.h" 58 59 60GameManager::GameManager() 61{ 62 63 draw = true; 64 finish = false; 65 screen = NULL; 66 eventQueue = NULL; 67 timer = NULL; 68 69 if(!al_init()) 70 finish = true; 71 72 if (!al_install_keyboard()) {finish = true;} 73 74 screen = al_create_display(1200,800); 75 76 if(!screen || !al_init_primitives_addon()) 77 finish = true; 78 79 timer = al_create_timer(1.0 / 60); 80 81 eventQueue = al_create_event_queue(); 82 83 al_register_event_source(eventQueue, al_get_timer_event_source(timer)); 84 al_register_event_source(eventQueue, al_get_display_event_source(screen)); 85 al_register_event_source(eventQueue, al_get_keyboard_event_source()); 86 87 al_start_timer(timer); 88 89 manage(); 90} 91 92 93 94void GameManager::manage() 95{ 96 97 while (!finish) 98 { 99 if (draw) 100 { 101 gamePlay.drawGame(); 102 draw = false; 103 } 104 do 105 { 106 ALLEGRO_EVENT ev; 107 al_wait_for_event(eventQueue, &ev); 108 if (ev.type == ALLEGRO_EVENT_TIMER) 109 { 110 gamePlay.update(); 111 draw = true; 112 } 113 else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 114 finish = true; 115 } 116 else if (ev.type == ALLEGRO_EVENT_KEY_DOWN && ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE) { 117 finish = true; 118 } 119 120 // i have also tried to put the gamePlay.updat() here 121 } 122 while (!al_is_event_queue_empty(eventQueue)); 123 } 124} 125 126 127//----------GamePlay source---------- 128 129//#include "GamePlay.h" 130 131GamePlay::GamePlay() 132{ 133 posX = 500; 134 posY = 500; 135 test = false; 136} 137void GamePlay::update() 138{ 139 if(posX > 1000) 140 test = false; 141 if (posX < 50) 142 test = true; 143 if(test) 144 posX+=10; 145 else 146 posX-=10; 147} 148void GamePlay::drawGame() 149{ 150 al_draw_filled_circle(posX,posY,50,al_map_rgba(255,0,0,255)); 151 al_flip_display(); 152 al_clear_to_color(al_map_rgb(0,0,0)); 153} 154 155int main() 156{ 157 GameManager US; 158 return 0; 159}

The resulting exe works fine. The red ball goes back and forth smoothly. There was one glitch on the first run, where it skipped a little, but I assume that was a vsync timing issue.

moisesdias
Member #16,905
October 2018

I'm sorry Edgar, I have removed a lot of classes, I did not see the remaining variables.

So, I don't think that the problem is my computer, maybe I have messed up when installing codeblocks and allegro, but I'm less worried now that it worked fine for you.

do you guys know a recognized tutorial for installation of allegro with any IDE? for windows or linux?

Edgar Reynaldo
Member #8,592
May 2007
avatar

I wouldn't consider it solved yet.

The code listed works fine when graphics takes less than 1.0/FPS, but when it takes longer than that, you will have frame skipping. The solution is to discard all timer events after the first in each iteration.

This will skip all extra timer events in case they pile up, and it will slow down gracefully.

#SelectExpand
1 bool redraw = true; 2 bool quit = false; 3 4 int ticks = 0; 5 while (!quit) { 6 if (redraw) { 7 Draw(); 8 redraw = false; 9 ticks = 0; 10 } 11 do { 12 ALLEGRO_EVENT ev; 13 al_wait_for_event(q , &ev); 14 if (ev.type == ALLEGRO_EVENT_TIMER) { 15 ++ticks; 16 if (ticks == 1) { 17 Update(dt); 18 } 19 redraw = true; 20 } 21 /// Handle events here 22 } while (!al_is_event_queue_empty(q)); 23 }

moisesdias
Member #16,905
October 2018

Edgar, I've tried to put this new variable to avoid updating twice before drawing, but my problem persists, unfortunnely..

I have also tried to leave only the al_flip_display() on my Draw function:

#SelectExpand
1 2void GamePlay::update() 3{ 4 if(posX > 1000) 5 test = false; 6 if (posX < 50) 7 test = true; 8 if(test) 9 posX+=10; 10 else 11 posX-=10; 12 13 al_clear_to_color(al_map_rgb(0,0,0)); 14 al_draw_filled_circle(posX,posY,50,al_map_rgba(255,0,0,255)); 15} 16void GamePlay::drawGame() 17{ 18 al_flip_display(); 19 20} 21 22 23//instead of 24 25 26 27void GamePlay::update() 28{ 29 if(posX > 1000) 30 test = false; 31 if (posX < 50) 32 test = true; 33 if(test) 34 posX+=10; 35 else 36 posX-=10; 37} 38void GamePlay::drawGame() 39{ 40 al_draw_filled_circle(posX,posY,50,al_map_rgba(255,0,0,255)); 41 al_flip_display(); 42 al_clear_to_color(al_map_rgb(0,0,0)); 43}

but it did not help. Damn, I was pretty sure that the ticks variable would solve the issue hahahaha

edit: I've checked my screen refresh rate and it's on 60Hz

Edgar Reynaldo
Member #8,592
May 2007
avatar

do you guys know a recognized tutorial for installation of allegro with any IDE? for windows or linux?

MSVS is pretty easy. All you do is use Nugat to install Allegro. MinGW and CodeBlocks is not super hard either, but takes some work. Linux can be as easy as 'sudo apt-get install liballegro5-dev' if you're on Ubuntu.

but it did not help. Damn, I was pretty sure that the ticks variable would solve the issue hahahaha

edit: I've checked my screen refresh rate and it's on 60Hz

It literally has to move 10 pixels at a time, there's something else going on.

Did you compile and run the code I gave back to you?

moisesdias
Member #16,905
October 2018

Guys, I've found the problem, it was not the code, but an old VGA monitor that I was using with my notebook, I don't know exactly why, but when I disconnect him the circle moves smoothly on the screen

Anyway, thanks Edgar and thanks for all who have replied!!

Go to: