I've got a program and it runs fine, but I when I added a moving star background the program starts to lag. The stars don't lag and the mouse doesn't hesitate but clicking the blocks in the game lags horribly. The overall while loop seems to run at a constant speed per my std::cout commands. It's just the mouse clicks or keyboard entry. If I commnent the function call out, other than looking strange, it runs and functions fine.
If you wouldn't mind, look this over and let me know where I've messed up. Thanks.
1. Huge issue is al_convert_mask_to_alpha. It should be done once right after the bitmap is loaded or created. That function goes through every pixel. Does a comparison to your color and sets alpha to zero if there is a match. EVERY pixel. Very time consuming.
2. is target restored? Don't set it to another bitmap if not restored back to original bitmap.
3. SEPARATE logic and drawing. Logic timing should be consistent. Drawing should be done when it can and when all logic is done.
My preferred order
1. process all input
2. do all logic based on current input states (set to process consitently at 30 times per second using a timer).
3. do all drawing when all done with logic (FPS is based on how many times I can still draw the screen per second)
4. goto 1.
DanielH is right about al_convert_mask_to_alpha, it should only be called once.
Other than that, the issue is not in the code you've shown.
Thanks guys. I thought I was probably calling a single call over and over. I did the same thing with random number seed.
Daniel,
I have a few lines of setting target to buffer and back to target etc. I'm not sure why. If I take out the "target" bitmap, the process doesn't work. My limited logic tells me I should be able to make the buffer the target bitmap and run with it, but if I don't flip back and forth, some of the scaling commands don't work. In short, I don't understand how that process works so I'm dropping copied code and if it works I just go with it. I'm sure I'm misusing at some level.
The "target" is the current bitmap ALL drawing is done to. Yes, set it to your buffer. Then set is back to the display's buffer to draw your buffer to the display's buffer.
or set your buffer then set to display's buffer. No need to store current target.
ALLEGRO_BITMAP *al_get_backbuffer(ALLEGRO_DISPLAY *display);
I do the first just in case the display is not my final target. It usually always is.
Thx all,
So Daniel, I don't necessarily need a "target" bitmap?
I'm trying to understand why one would need to write to buffer_one, then write buffer_one to buffer_too, then buffer_too to the display? Seems like buffer_too is an extra step.
Can you give me an example of why you would need to do that?
You can do all your drawing to the display or all your drawing to a bitmap then draw that bitmap to the display. Depends on what you want.
Personally, I have a buffer bitmap of pre-determined, non-changing size. I do all drawing to it. Then I scale it to whatever size the display is.
Why? 1. if the display size never changed then I might draw only to display. 2. I don't want the display to determine my graphics sizes. 16x16 tiles might be too small to display without scaling. Same goes for any fonts I have. I prefer a set size buffer for that reason. Then scale to any size needed at once.
Thanks Daniel,
One more question concerning buffers. I've been drawing to a buffer bitmap and then flipping it to the display. Are you saying the display already has a buffer by default? Or is drawing to the display literally drawing to the screen directly?
Thanks.
Yes, by default, the display has a main bitmap and the back buffer bitmap.
You draw to the back buffer then flip them. You can remove the back buffer with an ALLEGRO_SINGLE_BUFFER option.
If you set single buffer then you don't need to call al_flip_display as there is nothing to flip.
Daniel thanks for the insight. I think I'm going to try Option 2.
Sounds great. If you don't need to change target to another bitmap then you don't need to call al_set_target_bitmap(al_get_backbuffer(display));
All,
Ok, I moved my al_convert_mask_to_alpha() to where I initialize the bitmap files. I removed all references of going back and forth with target / buffer bitmaps. The visual aspect of the program looks great. The moving star background works well. However, my lag is still present and it grows longer between clicks.
My question is, is there something that would cause the event queue to lag? I’ve put in std::cout print outs at different locations in the program. Granted not scientific or exact, but it does expose variations. At program start, each time through the game loop the timer is checked (timer event) so looking at my output I have a one to one condition, maybe an occasional one to two.
“Loop Check”
“Timer Check”
“Loop Check”
“Timer Check”
“Loop Check”
“Timer Check”
(Loop check is located just underneath the while() statement. The Timer check is the first item in the if(event.type == ALLEGRO_EVENT_TIMER) section)
Then as I start clicking as part of the game, the gap between them grows to one Timer Check to five or six Loop Checks. If I stop clicking after 5-10 sec it goes back to one to one. It’s almost like the event queue, which would include key down and mouse clicks slows down or doesn’t samples as fast. I must point out, if you have the patience, the games functions as it should. It's just the mouse click results lag horribly.
While the star code earlier in the thread may not be problematic, when I comment it out my lag issue goes away. Maybe it’s the bitmaps being used with in the star code.
Any ideas / comments are welcome. If I don’t hear back, have a great 4th holiday.
Is it possible to post the code? At least the loop and event sections?
I suspect the issue is there.
You're likely drawing once for every mouse or keyboard event. That's a no-no. Post your event handling code.
I'll send the loop data. First I want to ask a follow up on my original question.
I thought I no longer needed a target bitmap and I could work with the buffer bitmap only. But when I commented the target code out, my screen won't change from the title screen. If I choose Setup, the screen changes fine. If I choose Play, it doesn't. Below is the code for drawing the game play itself. Can you tell me why I would still need the target code? I'm thinking I started using it when I started working with the scale command. Maybe it requires it. Let me know what you think. Thanks.
ALL drawing functions draw to the target bitmap. What's your target? Is it the display's back buffer or your buffer?
If you don't use a buffer and always draw to the display then you don't need to mess with the targeting.
If you use a buffer and want to draw to that buffer then you need to change the current target to that buffer. Draw to the buffer. Then change the current target again back to previous target (the display's back buffer). Then draw your buffer to the display.
Scaling is tricky if you don't use a buffer. You would have to scale each bitmap drawn to the display and calculate the scaled position. Or use transforms, which is what the scale functions use in the background.
I find it so much easier to use a buffer and scale it once after I draw everything to it.
This is a situation where I copied the scale code & altered for my needs without understanding what is going on.
If I comment out both lines 3 & 9, the program won't run. Or the screen won't change from the title screen.
I don't write to target (Line 3)so I'm not sure why it's needed. In line 4 I set the targeted bitmap to buffer. I draw in star background(), display verbiage(), & draw cards() functions. I assume they are drawing to the buffer bitmap since it's the last set as the targeted bitmap. Upon returning I scale the buffer bitmap and flip. The actual target bit map in line 3 isn't used at all.
In any case, I vaguely remember when looking into the scale command, someone saying I needed to switch the targeted bitmap to a second bitmap (target bitmap in this case) before scaling the buffer bitmap. It's like they are saying a targeted bitmap can't be scaled. This maybe the case, but I'm not understanding why.
Thanks
*UPDATE*
Ok, I researched further. As I understand, the scale command scales the bitmap listed in the command (buffer) to a second bitmap that is currently targeted. The the flip writes the targeted bitmap to the display. I thought buffer bitmap was being flipped. The targeted bitmap (target in this case) in my program is the scaled version of the buffer bitmap. Now I get it I think.
BTW using names like target and buffer in the program makes sense, but not very helpful when trying to explain your program flow. I think I need to come up with a new naming scheme.
There's a bit of confusion on 'target' especially. It's just a bitmap. By default it is the display's back buffer bitmap.
I didn't make the names
Target = Destination
Buffer = temp storage
In allegro 4 and previous, you could specify the target as a parameter
void blit(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height);
in this function dest is the target bitmap of the draw call.
In allegro 5, target is implied
void al_draw_bitmap(ALLEGRO_BITMAP *source, float dx, float dy, int flags);
Like I said, you don't need your bitmap if you draw directly to the display back bitmap.
void display_print(CARDS Cards[],int nError,int nGameOver,int nTurnPos,int nPlayer[]) { // draw to display's back buffer star_background(); display_verbiage(nError,nGameOver,nTurnPos,nPlayer); draw_cards(Cards); // swap front and back buffers al_flip_display(); }
Thanks Daniel.
At some point in this thread you and Edgar asked for my game loop code so you could see the various events. Keep in mind, I didn't write it with the intent of sharing with others. If there are process improvements that I can make, I'm excited to hear them. I'm always open to learn from the more experienced. If your just critiquing my coding technique, I already know it's sloppy .
This code is in its own file and function. In main() I show title screen and wait for choice of Play, Setup, & Quit. If Play is chosen, we end up here.
Thanks for taking the time to look it over.
Couple things I see
I'll preface this by saying this is my opinion.
Your braces don't match. The end brace for timer is labeled // end while. That puts the draw call inside the timer code.
if(event.type == ALLEGRO_EVENT_TIMER) { if(bDraw == true) nRest--; if(bDraw == true && nRest == 0) bDraw = false; } // end timer // Never link drawing to a timer. NEVER. Timers are for logic. display_print(Cards,nError,nGameOver,nTurnPos,nPlayer); // drawing should happen every time something changes on the screen. // a boolean dirty variable can be used if it's dirty and needs a refresh // that should be set by the logic not the timer. dirty = (did_something_change)
That is probably most of your lag problem ... maybe. I don't like waiting for events.
the other lag I see is that you are doing a ton of logic during an event. That can't be helped unless you make some variables to store your input.
The timer should regulate the logic not your input events. That way the logic is running at the same speed regardless the computer speed. Input events are there to set your keyboard/mouse state variables (if you had them). They should be short and fast. They should not to tie up processing speed by doing logic in the events themselves.
All you need is 2 extra variables
bool mouse_is_being_pressed = false; bool mouse_was_pressed_since_i_last_did_logic = false;
These are set in your mouse button up/down events.
if(event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { mouse_is_being_pressed = mouse_was_pressed_since_i_last did_logic = true; } if(event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { mouse_is_being_pressed = false; }
Your mouse_axes event code is short enough it's fine. Then all the logic code you currently have in your button up/down should be put in the timer. or another function to keep clean.
if(event.type == ALLEGRO_EVENT_TIMER) { if (mouse_was_pressed_since_i_last_did_logic) { // do your code that checks } else { } mouse_was_pressed_since_i_last_did_logic = false; }
add the same for keyboard if you need to
Thanks Daniel. I'll look and see how I can rearrange. However I did find one thing confusing (shock huh?). When I first wrote a small program to test an idea for a moving star background. I notice the stars looks great until I started clicking the mouse. Then it would hang for a split second or speed up. At that time I was told on this site, can't remember by who, to put the drawing in the timer event so it would be uniformed. The thought being the time event is going to be true with each click of the clock. Does this make sense or did I misunderstand? Or is it a personal choice type thing?
Thanks again for all the help.
I have heard that too and I've been trying to stop it. They think they can get uniform control by putting the drawing in the timer.
The logic handles control not the drawing.
The logic should be in the timer if you want uniformity. The drawing should either be done every loop or using the dirty boolean like I said.
Try putting the draw call outside of timer for the time being and see if that helps at all.
There are a couple other little things:
That's if it's an int. If it's a bool then you can use:
if (!bTurn) {nTurn = !nTurn; bTurn = false;}
No big deal, but I find it funny you didn't use a switch for the event types, but went with ifs. Then for something small with 2 choices you used a switch. Just a personal thing.
---
One thing you could to to decrease processing time. Multiplcation is expensive. Avoid if necessary or do some precalculations. Add a few extra variables to Cards[] and store already calculated values.
if(nPos_x >= (Cards[count].fTX*fPercent_Diff_WD) && nPos_y >= (Cards[count].fTY*fPercent_Diff_HT)) { if(nPos_x <= (Cards[count].fBX*fPercent_Diff_WD) && nPos_y <= (Cards[count].fBY*fPercent_Diff_HT))
A good event loop is the basis of all allegro programs.
Really take it to heart, because when you don't follow a proper event loop you get all kinds of problems in your program like drawing lag and input lag.
Daniel, I think the program has a larger issue. I moved the draw command but it didn't help, maybe even increased the lag. I'll dig deeper.
Edgar, I appreciate the insight. I'll look into it. I wouldn't dream of questioning someone who knows far more than I but someone on here stated part of my original issue was using if else. They suggested using ifs only. Does it really matter either way? Thanks.
The only difference between if and else if is when you want to chain conditions.
Here both will execute.
if (a) { } if (b) { }
Here the second is only triggered if !a is true. if (a) { } else if (b) { }
if/else if/switch they're all the same pretty much, but the small differences are what separates them.
Premature optimization is not good. Even a decade old computer can handle millions of multiplications per second. You're not coming anywhere near a bottleneck in performance, so write the code the way it is easiest to understand.
All,
So, beating the program lag some more, I’ve run into a weird situation that doesn’t make sense to me. I can accept and admit the flow isn’t the best. I’m also sure if I restructured the program it might make my lag go away. What I can’t understand is the reason for the lag based on the testing below.
Here is the short code.
If I comment out the Still command, my lag goes away.
If I uncomment the Still command and comment out stars (y1) my lag goes away.
If I uncomment the stars(y1) command and comment out stars (y2) my lag goes away.
It’s like having 3 bitmaps drawn is causing the lag.
If I leave the bitmap drawings and comment out the math, still lags
If I comment out all ref to y1 and y2, and use an actual number in place of y1 and y2. I still have the lag.
If I put everything back to original and replace one of the stars with a different bitmap. So that I have 3 unique bitmaps. I still have the lag.
I replaced both bitmaps Stars and Still with smaller, different bitmaps, I still have the lag.
I’m not that stressed over it because I can do away with the Still bmp or I can rewrite a big section of code to clear the lag. I was just curious if this made sense to anyone as to why this would happen?
Nothing seems unusual in that bit of code. It has to be something else. Unless they are huge bitmaps or memory bitmaps.
Were they loaded/create before or after display?
I'd like to see entire project if it's not too much to ask.
Daniel,
I’ll pull the data together. I tried one more test. I got to thinking it may be computer specific. So I ran the program on a faster machine. The lag wasn’t as bad, but like the previous machine, the lag increased the longer I ran the program. Both machines were running Windows. The first was Win 10 and the second Win 11.
I then compiled and ran the program on the second machine under Linux / Kubuntu. It ran great. So I’m not sure if it’s a Windows thing or poor programming. Likely the latter.
I’ll work on the program files.
Do you know if this site allows compressed files? Otherwise if will be about 10-15 small files.
** UPDATE ** Try to add files. Site doesn't seem to allow anything but text files. Not sure how to get the pictures and ttf files to you. Let me know if I've missing something.
Don't call display_print inside the if ev.type == ALLEGRO_EVENT_TIMER call. Just set redraw to true and use up the rest of the events like it shows above.
Thanks Edgar. That's similar to Daniel's advice. Can you explain reason for that? Here is why I ask.
ev.timer
{
display print
}
Vs
ev.timer
{
set bool variable to true
}
if(bool)
{
display print
}
I'm not seeing the difference. Every time in timer your going to see bool back to true and the next if condition will print. I'm not disagreeing, just asking for educational purposes. Thanks.
We've mentioned it before, but you need to load bitmaps AFTER creating the display.
Order matters
If there is not display to connect the bitmaps to then they become memory bitmaps and not video bitmaps.
Memory bitmaps - memory stored on computer - slow drawing due to slow transfer to GPU
Video bitmaps - memory stored on the GPU - fast because it's already there
Options:
1. load after display
2. load before display but right after display is created you convert them to video bitmaps
void al_convert_memory_bitmaps();
----
The drawing in the timer event is problematic. Look at bitmap.c (in the examples folder). It uses a refresh variable to set if drawing needs to occurs. Yes, timer sets it every tic. However, drawing is skipped if other events are already in the queue and needed to be processed. Your not skipping drawing. drawing should be last and only if everything is done since it takes the longest to process.
Mouse events, display events, ...
Thanks Edgar. That's similar to Daniel's advice. Can you explain reason for that? Here is why I ask.
I'm not seeing the difference. Every time in timer your going to see bool back to true and the next if condition will print. I'm not disagreeing, just asking for educational purposes. Thanks.
There's a big difference in a proper event loop that uses up all the events in the queue before performing a redraw. You're essentially getting backed up events in your queue that make it slower and slower because it's responding to timer events that happened in the past. It never catches up to the present.
Thanks guys, I think it's starting to sink in. I'll do some rearranging and see what happens. I appreciate the insight.
No problem. Let us know if our suggestions did anything to improve performance.
I managed to run your program and did a few things to get rid of the lag (as suggested by the other guys):
Moved create display before bitmap creation in a_init():
init_confirm(al_init(), "allegro"); init_confirm(al_install_keyboard(), "keyboard"); init_confirm(al_install_mouse(), "mouse"); init_confirm(al_init_font_addon(), "font_addon"); init_confirm(al_init_ttf_addon(), "ttf_addon"); init_confirm(al_init_primitives_addon(), "primitives"); init_confirm(al_init_image_addon(), "image addon"); disp = al_create_display(640, 480); //BEFORE BITMAP CREATION! init_confirm(disp, "display");
And I added a redrawScreen bool to main, which gets set in the timer event:
if(event.type == ALLEGRO_EVENT_TIMER) { redrawScreen = true; } // end timer event check
Finally I added a new condition to the end of the while(!bQuite) loop:
if (redrawScreen) { redrawScreen = false; al_set_target_bitmap(buffer); al_clear_to_color(COLOR::BLACK); al_draw_bitmap(titlescr, 0, 0, 0); al_set_target_bitmap(target); // have to return target bitmap to target or flipping wont work al_draw_scaled_bitmap(buffer, 0, 0, nBufferWD, nBufferHT, 0, 0, nDispWD, nDispHT, 0); al_flip_display(); }
After those steps the lag was gone. Maybe a good starting point if you're still working on a fix.
Thanks for taking the time you guys. I'll work through the changes in the next few days so that I understand what is taking place. I'll keep you updated. I appreciate you all looking at this with me.
** UPDATE **
I move the display creation before the before the bitmaps. That solved the problem. I'm going to continue work on cleaning up the code. I'm going to try using a redraw/bool approach as suggested. Thanks again.