Big FPS problem
Francis Langlais

So I have this game that I'm porting from Allegro 4.4 to Allegro 5.0. I was a completely new to Allegro and fairly new to coding in general before but now I think I got it (a little bit). So, the game was running at 30 FPS on Allegro 4.4 and after getting almost everything up and running in Allegro 5.0, my FPS counter is at 4-5.

The game is almost 4 000 line and I don't really know where to look for improvement.
I attached a .zip with all the code if some one want to have a quick look it would be greatly appreciated!

There is

Edgar Reynaldo

Make sure you create all your bitmaps after creating the display so they are video bitmaps. Also make sure you're not loading bitmaps on every frame.

Francis Langlais

Can I just create the display and pass it to a function like this?

  display = al_create_display(SCREEN_WIDTH, SCREEN_HEIGHT);

Or is there a way to create a video bitmap before creating the display?

Edgar Reynaldo

Can I just create the display and pass it to a function like this?

Yeah, if you need it.


Or is there a way to create a video bitmap before creating the display?

Not really, but you can create them first and then upload them later by using al_convert_bitmaps

Francis Langlais

I tried passing the display to a function and I'm still at 4-5 FPS and when particles are suppose to be loaded it drops to 1 frame every 5 to 10 sec or until the particle fade by itself. The particle thing was not bothering anything before.

For the particles, I'm using al_put_pixel(). I guess I do want the bitmap on which I draw my particle to be a memory bitmap in that case? But when I use al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP); my FPS drop down to 1 every 2 or 3 seconds.

The bitmap I use al_put_pixel() is a sub bitmap of a bitmap I use to draw the whole scene and I have a question about sub bitmap : if you al_draw_bitmap(parent), do the child is drawn at the same time?

I will try to change my main function so I don't have to pass my display but still create it before everything.

PS: I'm sorry I was learning Allegro while working on this and I think that I did a lot of things wrong at the beginning like this sub bitmap thing that I still don't know why I use a sub bitmap and drawing my scene at the beginning of my rendering sequence.


Passing display to function, may work, but is actually wrong.

Once upon a time I drew to the screen. I thought the performance was ok, but wanted to speed up things. I drew to bitmaps and blitted to the screen at the end. This speeded up things dramatically. Even if you only draw rare things, performance is much better.

Francis Langlais

This is what I do right now.

I draw everything on a bitmap and then draw this one to the screen. I just do it in a strange way where my al_draw_bitmap is at the top of my rendering function so I kind of draw a frame late. It's no big deal since no one have control over what happen in the game (every user code a robot to fight in an arena and then we let them loose) but I kinda want it to feel right.

Edgar Reynaldo

al_put_pixel is pretty slow. al_draw_pixel may be faster. You could also speed it up by locking the bitmap before calling your pixel functions and then unlock it when done.
See al_lock_bitmap.

The bitmap I use al_put_pixel() is a sub bitmap of a bitmap I use to draw the whole scene and I have a question about sub bitmap : if you al_draw_bitmap(parent), do the child is drawn at the same time?

Yes, drawing the parent draws any sub bitmaps it may have, unless they're clipped.

Francis Langlais

@Edgar Reynaldo
You, my friend, are a genius!
So when I lock my bitmap to use al_put_pixel my game goes up to 19 FPS (from what I have seen from the counter) but when I don't lock the bitmap it is at 5-6 FPS. Is there a way to lock my bitmap and still be able to draw on them or is it just poor coding from me?

Right now I'm about to simply re-write the whole graphic of my game but if there is a quick fix before I do so I'll try the quick fix before.

RPG Hacker

Should I always lock my bitmap before drawing everything on them?

No, not at all. All drawing operations are executed directly on the GPU. When you lock your bitmap, what happens is that the bitmap's memory is mapped to the CPU. Obviously you don't need that. Since drawing operations happen on the GPU, you also want your memory on the GPU. Depending on how you set up your bitmap, locking needs to copy data from the GPU to the CPU all the time, so it could even slow down your application dramatically when called too often.

The reason you need to lock your bitmap at all is because al_put_pixel is not a drawing operation. It instead just directly modifies the pixel data in the memory of your bitmap. To do this, it needs the bitmap on the CPU, though, and because of this, al_put_pixel locks and then unlocks your bitmap whenever it's called, so naturally, if you call it too many times, it causes the bitmap to be locked and unlocked many times, which means a lot of data will be sent back and forth between GPU and CPU, causing major slowdown. By calling al_lock_bitmap before calling al_put_pixel, what basically happens is that you map the memory to the CPU in advance and tell al_put_pixel not to lock and unlock the bitmap itself, therefore you can call al_put_pixel on the same bitmap multiple times, but the bitmap will only be locked and unlocked once and therefore it will only get data from the GPU once and send data back to the GPU once, potentially speeding up the process a lot (depending on the situation).

You can speed this up even more by only locking the bitmap for write access rather than read/write access. That way the application doesn't have to read data from the GPU to the CPU (which is quite slow), but only has to send data from the CPU to the GPU. So if you don't need to preserve the contents of your bitmap, it is a good idea to set this flag. Also, if you recreating your bitmap every frame, you can speed up locking of your bitmap even further by specifying a certain flag on your bitmap. I think it's called "discard" or "no preserve" or something like that.

Now whether you use al_put_pixel or al_draw_pixel usually depends on the situation. If you're only drawing a few pixels onto the bitmap, then al_draw_pixel is usually the better choice, because few draw calls barely matter for the performance at all, especially when they're only drawing small 1-pixel-quads. If you're drawing a lot of pixels, though, and especially if you're modifying ALL pixels, then al_lock_bitmap + al_put_pixel is usually the better choice, because, while it needs to move a big memory block from/to GPU, it only needs to do so once, rather than sending thousands of individual draw commands to the GPU. So in this case, the al_put_pixel approach could actually be faster.

Francis Langlais

@RPG Hacker
Thank you for the explanation.

But there is still something weird. Why is my FPS goes up to 20 FPS when I have particle and I lock/unlock but goes down to 5-6 FPS when there is no particle?

EDIT: I am drawing a good amount of text using al_draw_textf. Can it be where my FPS goes?

EDIT2: I manage to make it work. The funny part about the solution is that what was causing my FPS to be so low was my FPS counter. I don't know why and I don't even care right now.

RPG Hacker

Why is my FPS goes up to 20 FPS when I have particle and I lock/unlock but goes down to 5-6 FPS when there is no particle?

Can you explain what exactly you're doing by putting a code snippet here?


I am drawing a good amount of text using al_draw_textf. Can it be where my FPS goes?

I wouldn't think so. I don't think drawing text is that slow. Unless you're literally drawing a ridiculous amount of text (which I can't even imagine in a game at all), I don't think text is too dramatic.

Francis Langlais

so my rendering function looked like this except for the line 102-104 were not commented.

1void RenderScene(t_LL listOfRobots, t_LL listOfDeadRobots, t_LL listOfWeapons, 2 char *messageToWrite) { 3 int j, drawX, drawY, drawX2, drawY2, range; 4 float angle, radians; 5 ROBOT *robot; 6 WEAPON *weapon; 7 ALLEGRO_FONT *font = al_create_builtin_font(); 8 9 /*DRWAING THE SCREEN BITMAP*/ 10 al_draw_bitmap(fullScreen, 0, 0, 0); 11 /*DRAWING THE BACKGROUND ARENA*/ 12 al_set_target_bitmap(fullScreen); 13 al_draw_bitmap(backgroundImg, 0, 0, 0); 14 /*DRAWING BLACK BACKGROUND FOR STATUS MESSAGE*/ 15 al_set_target_bitmap(message); 16 al_clear_to_color(al_map_rgb(0, 0, 0)); 17 /*FILLING ARENA BITMAP*/ 18 al_set_target_bitmap(arena); 19 al_clear_to_color( 20 al_map_rgb(COLOR_ARENA_RED, COLOR_ARENA_GREEN, COLOR_ARENA_BLUE)); 21 22 //Draw the sensor graphics 23 ForeachLL_M(listOfRobots, robot) 24 { 25 26 for (j = 0; j < MAX_SENSORS; j++) 27 if (robot->sensorArray[j].on && robot->sensorArray[j].powered) 28 switch (robot->sensorArray[j].type) { 29 case SENSOR_RADAR: 30 drawX = robot->x * PX_PER_CM - robot->sensorArray[j].drawX; 31 drawY = ARENA_HEIGHT_PX - robot->y * PX_PER_CM 32 - robot->sensorArray[j].drawY; 33 al_set_target_bitmap(robot->sensorArray[j].image); 34 al_draw_bitmap(robot->sensorArray[j].image, drawX, drawY, 0); 35 al_clear_to_color(robot->color); 36 break; 37 case SENSOR_RANGE: 38 angle = -robot->sensorArray[j].angle + robot->heading; 39 radians = angle * DEG_PER_RAD; 40 drawX = robot->x * PX_PER_CM; 41 drawY = ARENA_HEIGHT_PX - robot->y * PX_PER_CM; 42 range = robot->sensorArray[j].data; 43 if (range == -1) 44 range = 80; 45 drawX2 = drawX + cos(radians) * range * PX_PER_CM; 46 drawY2 = drawY - sin(radians) * range * PX_PER_CM; 47 al_set_target_bitmap(arena); 48 al_draw_line(drawX, drawY, drawX2, drawY2, 49 robot->color, 1); 50 al_draw_filled_circle(drawX2, drawY2, 2, 51 robot->color); 52 break; 53 case SENSOR_NONE: 54 default: 55 break; 56 } 57 } 58 59 //Draw the robots and their related status information. 60 ForeachLL_M(listOfRobots, robot) 61 { 62 //Draw the robot in its shield. 63 drawX = robot->x * PX_PER_CM - SHIELD_BMP_SZ / 2; 64 drawY = ARENA_HEIGHT_PX - robot->y * PX_PER_CM - SHIELD_BMP_SZ / 2; 65 al_set_target_bitmap(arena); 66 al_draw_bitmap(robot->image, drawX, drawY, 0); 67 68 } 69 70 //Draw robot information in the status area. 71 ForeachLL_M(listOfRobots, robot) 72 DrawStatusText(robot, message, 0); 73 ForeachLL_M(listOfDeadRobots, robot) 74 DrawStatusText(robot, message, 1); 75 76 //Draw the weapons 77 ForeachLL_M(listOfWeapons, weapon) 78 { 79 switch (weapon->type) { 80 case WEAPON_LASER: 81 drawX = weapon->x * PX_PER_CM - LASER_BMP_SZ / 2; 82 drawY = ARENA_HEIGHT_PX - weapon->y * PX_PER_CM - LASER_BMP_SZ / 2; 83 break; 84 case WEAPON_MISSILE: 85 drawX = weapon->x * PX_PER_CM - MISSILE_BMP_SZ / 2; 86 drawY = ARENA_HEIGHT_PX - weapon->y * PX_PER_CM 87 - MISSILE_BMP_SZ / 2; 88 break; 89 default: 90 break; 91 } 92 93 al_set_target_bitmap(arena); 94 al_draw_bitmap(weapon->image, drawX, drawY, 0); 95 } 96 97 al_lock_bitmap(arena, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READWRITE); 98 DrawAParticles(arena); 99 al_unlock_bitmap(arena); 100 101 /*THIS KILL THE FPS. DON'T USE IT OR TRY TO FIX IT*/ 102 //Draw the system message, if any. 103// al_set_target_bitmap(message); 104// al_draw_textf(font, al_map_rgb(255, 255, 255), 2, 741, 0, messageToWrite); 105 106 return; 107}

My particle drawing look like this where I lock/unlock my bitmap:

void DrawAParticles(ALLEGRO_BITMAP *bmp) {
  int drawX, drawY;
  PARTICLE *tempParticle;                   //Particle use for list iteration.
  ForeachLL_M(particleList, tempParticle)
  //Iteration macro.
    drawX = tempParticle->x * PX_PER_CM;
    drawY = ARENA_HEIGHT_PX - tempParticle->y * PX_PER_CM;
    al_put_pixel(drawX, drawY, tempParticle->color);

By locking the bitmap the way I did (line 97-99 of my rendering function) I was preventing the program from writing all the text using al_draw_textf on the parent bitmap of the one I locked.

The way I managed to make it work was to create a sub-bitmap of the same parent only for the text which result in having a black rectangle without text because I was drawing it outside of the bitmap. Then I just brought back the text one part at the time and when I brought my FPS counter text it drop the FPS to what it was before.

The string I use to draw my FPS counter was messageToWrite which I passed to the function and is done using sprintf(). One of my co-worker think that it's because messageToWrite didn't have the /0 character.

Now everything is working fine thanks to you guys!

ALLEGRO_FONT *font = al_create_builtin_font();

I think this is the cause of your problems. You're recreating the builtin font every time you render the screen and then you leak it.

As the name suggests, al_create_builtin_font() doe not just return a pointer to a builtin font, it creates the builtin font and then returns a pointer. You should do this only once and keep it in some state variable, and free the font on program exit.

Francis Langlais

I did create it each time because it was not working if I created it when I was creating all my ALLEGRO_BITMAP and ALLEGRO_SAMPLE so I am creating a new ALLEGRO_FONT that I store in the same ALLEGRO_FONT *font every time.

Edgar Reynaldo

You need to pass the pointer to the font into your function. That way you can load it outside of your function just once.

Francis Langlais

Even if I static ALLEGRO_FONT *font; in my header?

Edgar Reynaldo

Well, that will work too.

Thread #615592. Printed from