How to structure the program for a Sleep() or al_rest()
Xenowar

Hello folks, after giving up on this for a while I'm trying to get my game going again. However, I still have the problem of enormous processor usage and I read I have to place a convenient sleep function in the loop, but since I have never done that, I'm not sure about the structure of my loop and where and how to place the Sleep() or al_rest(). It would be nice if you could help me out with that!

Here is the code:

#SelectExpand
1 while(1) { 2 ALLEGRO_EVENT ev; 3 al_wait_for_event(event_queue, &ev); 4 5 if(ev.type == ALLEGRO_EVENT_TIMER && ev.timer.source == timer) { 6 redraw = true; 7 } 8 9 if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 10 return 1; 11 } 12 13 if(ev.type == ALLEGRO_EVENT_KEY_DOWN) { 14 if(ev.keyboard.keycode==ALLEGRO_KEY_UP) { 15 16 } 17 if(ev.keyboard.keycode==ALLEGRO_KEY_DOWN) { 18 19 } 20 if(ev.keyboard.keycode==ALLEGRO_KEY_LEFT) { 21 left=true; 22 } 23 if(ev.keyboard.keycode==ALLEGRO_KEY_RIGHT) { 24 right=true; 25 } 26 } 27 if(ev.type == ALLEGRO_EVENT_KEY_UP){ 28 if(ev.keyboard.keycode==ALLEGRO_KEY_UP) { 29 30 } 31 if(ev.keyboard.keycode==ALLEGRO_KEY_DOWN) { 32 33 } 34 if(ev.keyboard.keycode==ALLEGRO_KEY_LEFT) { 35 left=false; 36 } 37 if(ev.keyboard.keycode==ALLEGRO_KEY_RIGHT) { 38 right=false; 39 } 40 } 41 42 43 if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 44 al_get_mouse_state(&state); 45 mouse_x=al_get_mouse_state_axis(&state,0); 46 mouse_y=al_get_mouse_state_axis(&state,1); 47 if ([...]){ 48 [...] 49 } 50 } 51 52 if(redraw && al_is_event_queue_empty(event_queue)) { 53 redraw = false; 54 55 offset_horizontal = camera_x % TILE; 56 focus_horizontal = camera_x / TILE; 57 58 if(left){ 59 if(player_x>0){ 60 player_x-=8; 61 if(player_x>=SCREEN_W/2) 62 camera_x-=8; 63 } 64 } 65 if(right){ 66 player_x+=8; 67 if(player_x>SCREEN_W/2) 68 camera_x+=8; 69 } 70 71 72 al_clear_to_color(al_map_rgb(0,0,0)); 73 for(int i = 0; i < 10;i++){ 74 for(int j =0; j<8;j++){ 75 switch(map1.Map2D[focus_horizontal+i][focus_vertical+j]){ 76 case 0: 77 break; 78 case 1: 79 al_draw_bitmap(grass, i*TILE-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 80 break; 81 default: 82 break; 83 }; 84 85 } 86 } 87 if(focus_horizontal>0){ 88 for(int j = 0; j < 10;j++){ 89 switch(map1.Map2D[focus_horizontal-1][focus_vertical+j]){ 90 case 0: 91 break; 92 case 1: 93 al_draw_bitmap(grass, -TILE-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 94 break; 95 default: 96 break; 97 }; 98 } 99 } 100 if(focus_horizontal<map1.getWidth()-1-(SCREEN_W/TILE)){ 101 for(int j = 0; j < 10;j++){ 102 switch(map1.Map2D[focus_horizontal+(SCREEN_W/TILE)][focus_vertical+j]){ 103 case 0: 104 break; 105 case 1: 106 al_draw_bitmap(grass, SCREEN_W-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 107 break; 108 default: 109 break; 110 }; 111 } 112 } 113 if(player_x >= SCREEN_W/2){ 114 al_draw_bitmap(chara, SCREEN_W/2, player_y, 0); 115 } 116 else{ 117 al_draw_bitmap(chara, player_x, player_y, 0); 118 } 119 al_flip_display(); 120 } 121 }

I though the logical thing to do was to place the sleep function in an else or else if at the end of the main if, however that did nothing to the processor usage.

Thanks for your time.

Kris Asick

There shouldn't be enormous processor usage at all because you're calling al_flip_display(). So, there can only be one of two explanations:

1: You're not vsyncing. With a 2D application there's no excuse not to and this alone should dramatically cut down CPU usage. Before creating your display object, use al_set_new_display_option() to set ALLEGRO_VSYNC to 1. The default setting of 0 means to let the video drivers decide, and by default, video drivers tend to be set to NOT vsync. :P

2: Some video cards have optimizations in place to spread GPU rendering across CPU core usage in order to maximize performance. As you might expect though, this increases CPU usage, sometimes substantially. I don't know if ATI/AMD cards have an option related to this, but with an nVidia card, try disabling its "Threaded Optimization" setting.

As for calling al_rest() or Sleep(), keep in mind that ANY value higher than 0 could potentially give up as much as 50 ms of processing time, which would cut down CPU usage for sure but could also kill your game's performance. On the flip side, a value of 0 only gives up processing time to other threads of equal priority. This means if no such threads exist, no time is given up. It's meant as a method of playing nice with multitasking so you would put al_rest(0.0) or Sleep(0) typically at the end of your game loop. Mine is right after al_flip_display().

GaryT

Very interesting explanations Kris, particularly for me point number 2.

I didn't know anything about the "threads of equal priority" bit before this.

Just to ask a question off topic: What's the best internet browser to use for this forum. I'm using explorer9 on Vista and explorer10 on Windows7, and I don't seem to be getting this big download button for attachments appearing anywhere. Cheers :)

Edgar Reynaldo

Kill IE with fire. :P

Use Firefox / SeaMonkey / Chrome

GaryT

Cheers Edgar :-*

Xenowar

Hey Kris, thanks for your answer! I didn't have vsynch enabled, however enabling
it didn't fix the problem. It still has a very high processor usage.

{"name":"7lbnvfd7.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/3\/53f68312e053ddcc40cd064f525724a7.png","w":810,"h":190,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/3\/53f68312e053ddcc40cd064f525724a7"}7lbnvfd7.png
As you can see it is much higher than anything else.

I haven't seen the option in ATI and didn't find anything useful by googling, however I will test it later on a PC with an Nvidia card.

GaryT

Hi Xenowar,

In situations like this, I've always found starting with a simple example that doesn't have the problem, and then gradually adding/replacing sections of code until it eventually becomes the program that does have the problem, helps me.

Just out of interest what CPU usage does this small test program show:

#SelectExpand
1#include <allegro5\allegro.h> 2#include <allegro5\allegro_primitives.h> 3 4const int WIDTH = 800; 5const int HEIGHT = 600; 6 7int FPS = 60; 8 9bool done = false; 10 11int main(void) 12{ 13 ALLEGRO_DISPLAY *display = NULL; 14 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 15 ALLEGRO_TIMER *timer = NULL; 16 17 if(!al_init()) 18 return -1; 19 20 display = al_create_display(800, 600); 21 22 if(!display) 23 return - 1; 24 25 al_init_primitives_addon(); 26 al_install_keyboard(); 27 28 event_queue = al_create_event_queue(); 29 timer = al_create_timer(1.0 / FPS); 30 31 al_register_event_source(event_queue, al_get_keyboard_event_source()); 32 al_register_event_source(event_queue, al_get_display_event_source(display)); 33 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 34 35 al_start_timer(timer); 36 37 while(!done) 38 { 39 ALLEGRO_EVENT ev; 40 41 al_wait_for_event(event_queue, &ev); 42 43 if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 44 done = true; 45 46 if(ev.type == ALLEGRO_EVENT_KEY_DOWN) 47 { 48 switch(ev.keyboard.keycode) 49 { 50 case ALLEGRO_KEY_C: 51 al_draw_filled_circle(WIDTH/2, HEIGHT/2, 100, al_map_rgb(50, 200, 50)); 52 break; 53 54 case ALLEGRO_KEY_SPACE: 55 al_draw_filled_circle(WIDTH/2, HEIGHT/2, 100, al_map_rgb(0, 0, 0)); 56 break; 57 } 58 } 59 60 if(ev.type == ALLEGRO_EVENT_TIMER) 61 al_flip_display(); 62 } 63 64 al_destroy_timer(timer); 65 al_destroy_display(display); 66 al_destroy_event_queue(event_queue); 67 68 return 0; 69}

On my 4 year old Vista laptop it shows a varying CPU usage of between 0 to 2 percent. If I change the FPS to 1000 it uses an average of about 50 percent, and an extremely non-linear CPU usage for FPS in-between these values.

I’m off to work now. Cheers :)

Edit: If this test program uses too much CPU, then I guess it's something along the lines of what Kris has suggested.

Xenowar

Hmm, it has a CPU usage of 0. I guess I will add my code bit by bit and see how that
will change. Hopefully what will show what's at the core of it.

Kris Asick

One difference I can see between GaryT's code and your code Xenowar is that you're calling al_clear_to_color(). I'm not sure how much that would affect things though. Clearing the screen does burn some power, but it's odd it would account for 8% of your CPU's maximum capacity. Plus, in my own project, I have to call al_clear_to_color() every frame because of the special effects I'm performing, yet my program uses extremely little CPU power.

Still, 8% is not that bad when you really think about it. Unless you're using 8% of an 8-core, 4GHz CPU. Then... that would be kinda weird. :P

Xenowar

The clear to color is neccessary, and sadly removing it doesn't solve the problem either.

I identified the code which is responsible for the bad performance and unsuprisingly
it is the loops with which the tiles are drawn

#SelectExpand
1//draws all tiles in focus 2 for(int i = 0; i < 10;i++){ 3 for(int j =0; j<8;j++){ 4 switch(map1.Map2D[focus_horizontal+i][focus_vertical+j]){ 5 case 0: 6 7 break; 8 case 1: 9 al_draw_bitmap(grass, i*TILE-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 10 break; 11 default: 12 break; 13 }; 14 15 } 16 } 17 //draws tiles out of the left of the window 18 if(focus_horizontal>0){ 19 for(int j = 0; j < 10;j++){ 20 switch(map1.Map2D[focus_horizontal-1][focus_vertical+j]){ 21 case 0: 22 23 break; 24 case 1: 25 al_draw_bitmap(grass, -TILE-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 26 break; 27 default: 28 break; 29 }; 30 } 31 } 32 //draw the tiles at the right of the screen 33 if(focus_horizontal<map1.getWidth()-1-(SCREEN_W/TILE)){ 34 for(int j = 0; j < 10;j++){ 35 switch(map1.Map2D[focus_horizontal+(SCREEN_W/TILE)][focus_vertical+j]){ 36 case 0: 37 38 break; 39 case 1: 40 al_draw_bitmap(grass, SCREEN_W-offset_horizontal, (SCREEN_H-TILE)-j*TILE, 0); 41 break; 42 default: 43 break; 44 }; 45 } 46 }

This is the first time I used Tilemaps and I kinda made that stuff up myself, so
I don't know if its really efficient code. If anyone has a way to improve the drawing
of the tiles, it would be nice if you could share that knowledge. Meanwhile I will
look at some tilemap tutorials.

Audric

How frequent is your timer "timer" ?

Xenowar

60 times a second.

pkrcel

Strange, how many tiles should you be drawing? (meaning how many should be on the screen)

Out of a total of how many tiles that make up the full map?

Xenowar

The test map is a 30 x 8 two dimenstional array, which will later on differ with
each map. The number of drawn tiles is normally a number of 8x12 (so 96), but since
the tilemap has only some actual content right now it only has to draw about 12
tiles.

Kris Asick

What's the code for map1.getWidth()?

Xenowar

It returns the horizontal length of the 2D array, which in this case would be 30.

Edgar Reynaldo

A few questions :
1) What are focus_horizontal and vertical for?
2) What is offset_horizontal? Is that your camera's x position?
3) Why are you drawing from the bottom up ((SCREEN_H-TILE)-j*TILE) instead of from the top down (j*TILE - camera_y)? You're just making your code harder to read and understand.

Advice :
When you get your tile engine going, you want to draw batches of the same bitmap or sub bitmaps from it at the same time using al_hold_bitmap_drawing before you start, and then again when you're done.

You may want to get into processing arrays by row first instead of by column. It mirrors the arrangement in memory usually (you would have to swap your dimensions and indexing to do so).

And you could probably combine your left and right edge tile drawing into your main tile drawing loop.

taron 

It mirrors the arrangement in memory usually

Which ought to improve caching, so that should increase performance too.

Xenowar

1) What are focus_horizontal and vertical for?
focus_horizontal is the number of the first visible tile (array position) on the left side. focus_vertical is the same for the vertical position.
So if we have this screen:

0000000000
0000000000
0000000000
0000000000

the zero in the bottom left corner defines the focus horizontal and vertical.

2) What is offset_horizontal? Is that your camera's x position?
I always draw one tile outside of the visible range. When I move to the right the offset_horizontal increases and once it reachs the tile width focus_horizontal is
incremented by 1 and the offset is set to 0 again.

3) Why are you drawing from the bottom up ((SCREEN_H-TILE)-j*TILE) instead of from the top down (j*TILE - camera_y)? You're just making your code harder to read and understand.
I don't know, it's been a while since I wrote it and I've been lazy with comments.
But since I did it, it might have a reason, probably will make something easier I
will do in future.

I'll try to do that with al_hold_bitmap_drawing. So if I have a row which looks
like this:

111000011111144444

where each number represents a differen image, should I set al_hold_bitmap_drawing
false and true again with each new row of bitmaps coming or just leave it on true
until the row is drawn?

pkrcel

You shold defer bitmap drawing when drawing:

- the same bitmap over and over
- many sub bitmaps from the same parent (either with al_draw_bitmap_region or when created with al_create_sub_bitmap)

...which by the way seems something common when using tilesets :D

Edgar Reynaldo

It could all be so much simpler though - I wrote out an example for you. Tilemaps should be fairly simple.

Note, I didn't code any of the loading, that's up to you. Included is a simple camera.

#SelectExpand
1 2#include <allegro5/allegro.h> 3#include <vector> 4#include <map> 5 6std::vector< std::vector<int> > tilemap;// 2D array of integers representing tiles 7std::map<int , ALLEGRO_BITMAP*> int_to_image_map;// mapping of integers in tilemap to images 8 9// Basic setup 10int camx = 0; 11int camy = 0; 12const int scrw = 800; 13const int scrh = 600; 14const int tilew = 24; 15const int tileh = 32; 16 17// To be determined during loading of map... 18int mapntileswide = 0; 19int mapntilestall = 0; 20int worldw = 0; 21int worldh = 0; 22 23 24 25// repositions camera, centering on new coords and adjusting for out of bounds 26void SetCamera(int centerx , int centery) { 27 camx = centerx - scrw/2; 28 camy = centery - scrh/2; 29 if (camx < 0) {camx = 0;} 30 if (camy < 0) {camy = 0;} 31 if (camx > worldw - scrw) {camx = worldw - scrw;} 32 if (camy > worldh - scrh) {camy = worldh - scrh;} 33} 34 35 36// draws our map 37void DrawMap() { 38 39 40 int tilexstart = camx/tilew; 41 int tileystart = camy/tileh; 42 int tilexstop = (camx + scrw)/tilew; 43 int tileystop = (camy + scrh)/tileh; 44 45 /// asserts about 'tilemap's size should go here, or verify elsewhere 46 47 // if all our tiles are sub bitmaps of the same parent, we can use al_hold_bitmap_drawing 48 al_hold_bitmap_drawing(true);// cache drawing ops 49 50 // draw visible tiles 51 for (unsigned int row = tileystart ; row <= tileystop ; ++row) { 52 for (unsigned int col = tilexstart ; col <= tilexstop ; ++col) { 53 al_draw_bitmap(int_to_image_map[tilemap[y][x]] , row*tileh - camy , col*tilew - camx , 0); 54 } 55 } 56 57 al_hold_bitmap_drawing(false);// send it off to the gpu 58 59}

Thread #612412. Printed from Allegro.cc