Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Delay on event queue while on thread

This thread is locked; no one can reply to it. rss feed Print
Delay on event queue while on thread
Juanse
Member #21,477
October 2021
avatar

Hello everyone, hope you are doing good. This is my first post here so please tell me if I should do something different when posting questions.
I'm having troubles when working with an event queue on a POSIX thread. Basically, I get about a three seconds delay between me moving the cursor to create an event and the program acknowledging this event. This happens for any kind of event, not just from the mice. There's more code than what I'm about to show you, but it really shouldn't be a problem since it's running on the main thread, other than that I'm clueless on what is going on. Keep in mind, when I run this as standalone code, the allegro window closes instantly. Please help :(

#SelectExpand
1void * game_loop(void * arg) 2{ 3 al_set_new_window_title("Just Dance! Game"); 4 al_set_new_display_refresh_rate(fr); 5 al_set_new_display_flags(ALLEGRO_WINDOWED); 6 game = al_create_display(gw, gh); 7 8 if (game == nullptr) 9 { 10 perror("Error reservando display.\n"); 11 exit(-1); 12 } 13 14 if (!al_show_mouse_cursor(game) || !al_set_system_mouse_cursor(game, ALLEGRO_SYSTEM_MOUSE_CURSOR_ARROW)) 15 { 16 perror("Error mostrando cursor.\n"); 17 exit(-1); 18 } 19 20 queue = al_create_event_queue(); 21 if (queue == nullptr) 22 { 23 perror("Error creando cola de eventos.\n"); 24 exit(-1); 25 } 26 27 al_register_event_source(queue, al_get_keyboard_event_source()); 28 al_register_event_source(queue, al_get_mouse_event_source()); 29 30 al_clear_to_color(al_map_rgb(255, 255, 255)); 31 32 al_flip_display(); 33 34 al_wait_for_event(g.queue, &g.in_event); 35 36 pthread_exit(NULL); 37}

Chris Katko
Member #1,881
January 2002
avatar

1) IIRC. drawing has to be on the main thread no matter what. It's a D3D/OpenGL limitation.

2) is game_loop called once (and "has a loop inside it"), or called every frame? Because I don't see a loop, or, you're doing setup every single frame? e.g. you would usually never be calling "setup event source" and "clear to color" right next to each other.

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

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Some things to keep in mind when working with Allegro 5 and threads.

ALLEGRO_EVENT_QUEUEs are thread safe and protected by a mutex.

Displays are NOT, and they have thread local storage (TLS). This means a display will act differently when running on a thread other than the one it was created on. You can still do this, but you need to bind the display to a single thread at a time. You can do this with http://docs.liballeg.org/graphics.html#al_set_target_backbuffer .

Juanse
Member #21,477
October 2021
avatar

Thanks for answering quickly.

Chris, I'm sorry but I don't really know what IIRC is. The only drawing functions I'm using on this thread are al_clear_to_color and al_draw_bitmap. This game_loop function is only called once and a loop would be running on it, yes, but I cut it out for testing this code, my idea was to see this delay on a single loop, so when I get a signal I exit the code.

Edgar, I'm not working with displays on other threads, matter of fact I'm not even using allegro on the main thread, so I think this shouldn't be a problem right?

I know that what I'm doing might be confusing, but basically this is for a college project, which didn't need to be a game but I chose to, and I was required to work with another graphic interface. This interface was very limiting so I decided to do a "mash-up" with allegro, which I was already familiar with. This is why I run sort of a complete allegro code inside a thread, and I hoped it would work. If the problem is the fact that I'm using allegro on a thread then I'm kinda screwed.

DanielH
Member #934
January 2001
avatar

IIRC - If I Recall Correctly

Can you include loop code? Maybe your initialization also.

Juanse
Member #21,477
October 2021
avatar

Yeah sure.

For context, Element is a class with a bitmap and not much else. The Element.draw function is at the bottom. Background inherits from Element.

#SelectExpand
1void * game_loop(void * arg) 2{ 3 Element mA1("media/mA1.png"); 4 Element mA2("media/mA3.png"); 5 Element mA3("media/mA5.png"); 6 Element mA4("media/mA7.png"); 7 8 al_set_new_window_title("Just Dance! Game"); 9 al_set_new_display_refresh_rate(fr); 10 al_set_new_display_flags(ALLEGRO_WINDOWED); 11 ALLEGRO_DISPLAY *game = al_create_display(gw, gh); 12 13 if (game == nullptr) 14 { 15 perror("Error reservando display.\n"); 16 exit(-1); 17 } 18 19 if (!al_show_mouse_cursor(game) || !al_set_system_mouse_cursor(game, ALLEGRO_SYSTEM_MOUSE_CURSOR_ARROW)) 20 { 21 perror("Error mostrando cursor.\n"); 22 exit(-1); 23 } 24 25 ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue(); 26 if (queue == nullptr) 27 { 28 perror("Error creando cola de eventos.\n"); 29 exit(-1); 30 } 31 32 al_register_event_source(queue, al_get_keyboard_event_source()); 33 al_register_event_source(queue, al_get_mouse_event_source()); 34 35 Element *elements = (Element *)malloc(sizeof(Element) * 4); 36 37 *elements = mA1; 38 *(elements + 1) = mA2; 39 *(elements + 2) = mA3; 40 *(elements + 3) = mA4; 41 42 Background *background = new Background("media/background.png"); 43 44 bool flag = true; 45 46 do 47 { 48 al_clear_to_color(al_map_rgb(255, 255, 255)); 49 50 background->draw(0, 0); 51 52 elements[0].draw(gh * 3 / 5, gh * 7 / 16, gh / 8, gh / 8); 53 elements[1].draw(gh * 7 / 16, gh * 3 / 5, gh / 8, gh / 8); 54 elements[2].draw(gh * 11 / 40, gh * 7 / 16, gh / 8, gh / 8); 55 elements[3].draw(gh * 7 / 16, gh * 11 / 40, gh / 8, gh / 8); 56 57 al_flip_display(); 58 59 al_wait_for_event(queue, in_event); 60 61 switch (g.in_event.type) 62 { 63 case ALLEGRO_KEY_DOWN: 64 switch (g.in_event.keyboard.keycode) 65 { 66 case ALLEGRO_KEY_ESCAPE: 67 flag = false; 68 break; 69 } 70 break; 71 } 72 73 } while(flag); 74 75 pthread_exit(NULL); 76} 77 78void Element::draw (int x, int y) const 79{ 80 al_draw_bitmap(image, x, y, 0); 81} 82 83void Element::draw (int x, int y, int win, int hin) const 84{ 85 al_draw_scaled_bitmap(image, 0, 0, w, h, x, y, win, hin, 0); 86}

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Juanse
Member #21,477
October 2021
avatar

You are right, my bad. Let me clarify, I made the mistake while copying my code to the response, when compiling I got these things right.

g.queue in the first example is the same as queue, I just cut off a Game class I made for simplicity's sake.

On the second one I totally forgot the switch for the events, I'm adding it as an edit.

If you think it may be part of the problem, I can write iteration of the code where I don't use classes so that everything is inside this game_loop function.

DanielH
Member #934
January 2001
avatar

This is the issue with only seeing part of the process.

From what I see, you are only processing one event per loop iteration. That in itself will slow down your program. How many events took place before you clicked the mouse?

If you get any event, process all events until queue is empty before continuing loop.
Or skip the drawing process until queue is empty.

Also, I'm not an expert, but I believe that bitmaps should be loaded after display is created. This was to ensure they have the same format. If not, this can slow down a bit. Not quite sure on that one though. Please correct me if I'm wrong.

-------------------
Lastly, just a question. Sometimes I see code and question motives on using one way over another.

In your code, you create 4 static Elements.

    Element mA1("media/mA1.png");
    Element mA2("media/mA3.png");
    Element mA3("media/mA5.png");
    Element mA4("media/mA7.png");

Later you copied them to 4 dynamic Elements?

    *elements = mA1;
    *(elements + 1) = mA2;
    *(elements + 2) = mA3;
    *(elements + 3) = mA4;

Why copies?

Juanse
Member #21,477
October 2021
avatar

Ok, thanks for the suggestion, next time I'm going to read the whole event queue before exiting the loop. However, I don't think that's the problem here, since what I'm doing is just opening the program and pressing escape once, so I should be getting just one event, and there's consistently a three second delay from when I press escape to when the program actually acknowledges it. The drawing is almost instant, the events are the problem.

Also the bitmap thing shouldn't be the problem, I have run the program declaring the elements after creating the display and it was the same thing.

About the dynamic/static elements... I've modified the program so many times to figure out what's going on that I don't even know what's the point of that declaration. I'm polishing everything when I get the event "engine"(?) running.

By the way, when I run this program as a standalone, meaning I don't use threads, it works perfectly, so I'm 99% sure the problem is using events with threads. I just don't know if there's a solution to that, and I don't really have a workaround to using threads :-/.

Either way I'm really thankful for you guys helping me.

Edit: I've used the words program and problem like a million times at this point, I'm not a native speaker, sorry.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Juanse
Member #21,477
October 2021
avatar

Yeah, sure. The program is compiled with QT creator, hopefully you are familiar with it, if not ask me anything you need. For the program to run correctly, you need to paste the media folder inside the QT build folder for the project. The game_loop function is inside game.cpp.
Sorry for the delay, I was quite busy this weekend.

The .zip is attached here.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I believe the delay is coming from extranneous events. In your game_loop function, you draw g once for every event. If there are a lot of mouse events it could be stuck drawing for several seconds.

Always drain the event queue by processing all events.

do {
   al_wait_for_event(g.queue , g.in_event);
   if (g.in_event.type == ALLEGRO_EVENT_KEYBOARD && g.in_event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
      break;
   }
} while (!al_is_event_queue_empty(g.queue));

Juanse
Member #21,477
October 2021
avatar

Ok, I'm going to try that and update, thank you.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Chris Katko
Member #1,881
January 2002
avatar

I feel like an Allegro function called "how many events are left" (is there one)? that we could just have newbies post the results of, would identify 95% of these delay cases. And an assert in debug mode when the queue hits 1000 events stored because you'd never do that in 99.9999% of games.

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

torhu
Member #2,727
September 2002
avatar

That could make sense, but I suppose it would require keeping track of the count to avoid traversing the linked list of events every time the function is called, etc. Or if someone stores the number, unaware that it it's changed by a background thread, etc...

Maybe just a log message in debug mode when the event count passes 100, 1000, etc. would be helpeful?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Juanse
Member #21,477
October 2021
avatar

Again, sorry for the late reply, I'm having a though month with college.

I changed the game loop code to this one, and now the allegro window completely freezes, it doesn't respond to the escape key.

#SelectExpand
1void * game_loop(void * arg) 2{ 3 Allegro al; 4 Game g; 5 6 bool flag = true; 7 8 do 9 { 10 g.draw(); 11 do 12 { 13 al_wait_for_event(g.queue, &g.in_event); 14 switch (g.in_event.type) 15 { 16 case ALLEGRO_KEY_DOWN: 17 switch (g.in_event.keyboard.keycode) { 18 case ALLEGRO_KEY_ESCAPE: 19 flag = false; 20 break; 21 } 22 break; 23 } 24 } while (!al_is_event_queue_empty(g.queue)); 25 } while(flag); 26 27 emit ((menu *)arg)->game_ended(); 28 29 sleep(1); 30 31 pthread_exit(NULL); 32}

Izual
Member #2,756
September 2002
avatar

There is a mistake in your code. Change ALLEGRO_KEY_DOWN to ALLEGRO_EVENT_KEY_DOWN.

See event system and key codes for reference.

Edit: Added some links to documentation.

Juanse
Member #21,477
October 2021
avatar

Yeah you are right, sorry about that. I changed that and the program doesn't freeze anymore, but the delay is still present. Basically, the problem isn't that the queue is filled with events.

DanielH
Member #934
January 2001
avatar

You could add this

#SelectExpand
1void * game_loop(void * arg) 2{ 3 Allegro al; 4 Game g; 5 6 bool flag = true; 7 8 do 9 { 10 g.draw(); 11 do 12 { 13 al_wait_for_event(g.queue, &g.in_event); 14 switch (g.in_event.type) 15 { 16 case ALLEGRO_KEY_DOWN: 17 switch (g.in_event.keyboard.keycode) { 18 case ALLEGRO_KEY_ESCAPE: 19 flag = false; 20 break; 21 } 22 break; 23 } 24 25 /**************************************/ 26 if (flag) al_flush_event_queue(g.queue); 27 /**************************************/ 28 29 } while (!al_is_event_queue_empty(g.queue)); 30 } while(flag); 31 32 emit ((menu *)arg)->game_ended(); 33 34 sleep(1); 35 36 pthread_exit(NULL); 37}

Juanse
Member #21,477
October 2021
avatar

Same outcome :(

DanielH
Member #934
January 2001
avatar

What are you doing in the other thread? Are you using a lot of cycles? How about resting a bit.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Try littering your code with al_get_time calls. See how much time each portion of code is taking. That's the only way to find your delay.

I notice in your thread that you initialize allegro, create a game, and create a background that is cloned. That all takes at least some time. The delay may not be in the event queue at all.

A word of warning - globals are not good programming. You can use the data pointer in your thread process to pass anything you want into the thread.

EDIT
You also have a memory leak. new Background calls the Element constructor since it is derived from element. The element constructor loads the bitmap. Then your background constructor overwrites it with a clone of another bitmap you load, leaking memory.

Go to: