[a5] Memory Management -Bitmaps part 2
Phrasz

So I thought I was correctly freeing bitmaps ....but:

{"name":"check.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/c\/2c0587a4cf1c1d6a979121d0c1e7eb3c.png","w":400,"h":127,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/c\/2c0587a4cf1c1d6a979121d0c1e7eb3c"}check.png

The example below is a "book reader" for my kids. The idea is it loads the images and eventually the audio streams for each page. I know the resolution is a bit big (1442, 1024), but I thought it would be fine with reusing the bitmap, freeing the bitmap once done, and then recycling the bitmap for the next image.

Everytime I change the page 20-30 MB of RAM is used...

#SelectExpand
1#include <stdio.h> 2#include <stdlib.h> 3#include "allegro5/allegro.h" 4#include "allegro5/allegro_image.h" 5#include "allegro5/allegro_audio.h" 6#include "allegro5/allegro_acodec.h" 7#include "allegro5/allegro_font.h" 8#include <allegro5/allegro_ttf.h> 9 10//#include "common.c" 11 12int main(int argc, const char *argv[]){ 13//------------------------------------------// 14// VARIABLES 15 ALLEGRO_DISPLAY *display; 16 ALLEGRO_BITMAP *membitmap, *bg;//, *bitmap; 17 18 ALLEGRO_TIMER *timer; 19 ALLEGRO_EVENT_QUEUE *queue; 20 bool redraw = true; 21 bool runthisgame=1; //keeps the while loop alive 22 int counter_sixtith_sec=0; 23 int page_num=1; 24 bool page_printed=0; 25 bool song_started=0; 26//------------------------------------------// 27 28 29 30//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 31// Allegro Bootup 32//Return if allegro cannot start 33 if (!al_init()) { 34 printf("\nCould not init Allegro.\n"); 35 // abort_example("Could not init Allegro.\n"); 36 } 37//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 38 39 40//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 41// I/O Devices Installation 42 al_install_mouse(); 43 al_install_keyboard(); 44 al_init_image_addon(); 45 al_init_font_addon(); 46 al_init_ttf_addon(); 47//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 48 49 50//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 51// Display Creation 52 53 display = al_create_display(1442, 1024); 54 if (!display) { 55 printf("Error creating display\n"); 56 //abort_example("Error creating display\n"); 57 } 58 59 al_set_window_title(display, "How the Gecko Lost His Tail"); 60//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 61 62 63//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 64// Audio Function Variables 65ALLEGRO_SAMPLE *sample; 66ALLEGRO_SAMPLE_INSTANCE *sample_inst; 67 68 al_init_acodec_addon(); 69 70 if (!al_install_audio()) { 71 //abort_example("Could not init sound!\n"); 72 return 1; 73 } 74 75 if (!al_reserve_samples(1)) { 76 //abort_example("Could not set up voice and mixer.\n"); 77 return 1; 78 } 79 80 sample = al_load_sample("music/01_loadsong.ogg"); 81 if (!sample) { 82 //abort_example("Could not load sample from '%s'!\n", argv[1]); 83 } 84 85 /* Loop the sample. */ 86 //sample_inst = al_create_sample_instance(sample); 87 sample_inst = al_create_sample_instance(sample); 88 al_set_sample_instance_playmode(sample_inst, ALLEGRO_PLAYMODE_LOOP); 89 al_attach_sample_instance_to_mixer(sample_inst, al_get_default_mixer()); 90 91//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 92 93 94//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 95// Timer Function Variables 96 timer = al_create_timer(1.0 / 60); //60 FPS 97 queue = al_create_event_queue(); 98 al_register_event_source(queue, al_get_keyboard_event_source()); 99 al_register_event_source(queue, al_get_display_event_source(display)); 100 al_register_event_source(queue, al_get_timer_event_source(timer)); 101 al_start_timer(timer); 102//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 103 104//-------------------------------------------------------------// 105// First BITMAP 106 107 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP); 108 membitmap = al_load_bitmap("images/01.jpg"); 109 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP); 110 bg =al_clone_bitmap(membitmap); 111 al_destroy_bitmap(membitmap); 112 al_destroy_bitmap(bg); 113//-------------------------------------------------------------// 114 115///-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~/// 116/// THE MAGIC WHILE LOOP 117/// Here's the start of the execution of the game 118 while (runthisgame==1){//runthisgame) { 119 ALLEGRO_EVENT event; 120 al_wait_for_event(queue, &event); 121 122 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE){ //User closes the window 123 runthisgame=0; 124 } 125 if (event.type == ALLEGRO_EVENT_KEY_CHAR) { 126 if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE){ 127 runthisgame=0; 128 } 129 if (event.keyboard.keycode == ALLEGRO_KEY_LEFT){//Previous Page 130 if(page_num>1){ 131 page_num--;page_printed=0;song_started=0; 132 //al_destroy_sample_instance(sample_inst); 133 //al_destroy_sample(sample); 134 135 } 136 } 137 if (event.keyboard.keycode == ALLEGRO_KEY_RIGHT||event.keyboard.keycode == ALLEGRO_KEY_SPACE){//Next Page 138 if(page_num<18){ 139 page_num++;page_printed=0;song_started=0; 140 al_destroy_sample_instance(sample_inst); 141 //al_destroy_sample(sample); 142 } 143 144 } 145 } 146 // 147 if (event.type == ALLEGRO_EVENT_TIMER) 148 redraw = true; 149 150 if (redraw && al_is_event_queue_empty(queue)) { 151 redraw = false; 152 ///---MORE MAGIC---/// 153 switch(page_num){ 154 case 1: 155 if(!page_printed){ 156 ///IMAGE 157 membitmap = al_load_bitmap("images/01.jpg"); 158 // //al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP); 159 bg =al_clone_bitmap(membitmap); 160 al_destroy_bitmap(membitmap); 161 al_draw_bitmap(bg,0,0,0); 162 al_destroy_bitmap(bg); 163 al_flip_display(); 164 ///SOUND 165 if(!song_started){ 166 //al_stop_sample_instance(sample_inst); 167 al_set_sample(sample_inst, al_load_sample("music/01_TitleSong.ogg")); 168 al_play_sample_instance(sample_inst); 169 song_started=1; 170 } 171 page_printed=1; 172 } 173 if(counter_sixtith_sec==60*228){ //counter*60== seconds comapre to 228 -->title song is 228 secs (3mins 48sec) long 174 page_num++; //auto go to next page 175 } 176 break; 177 178 . 179 . 180 . 181 182 break; 183 case 18: 184 if(!page_printed){ 185 ///IMAGE 186 membitmap = al_load_bitmap("images/18.jpg"); 187 //al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP); 188 bg =al_clone_bitmap(membitmap); 189 al_destroy_bitmap(membitmap); 190 al_draw_bitmap(bg,0,0,0); 191 al_destroy_bitmap(bg); 192 al_flip_display(); 193 ///SOUND 194 if(!song_started){ 195 al_stop_sample_instance(sample_inst); 196 al_set_sample(sample_inst, al_load_sample("music/01_TitleSong.ogg")); 197 al_play_sample_instance(sample_inst); 198 song_started=1; 199 } 200 page_printed=1; 201 } 202 if(counter_sixtith_sec==60*228){ //counter*60== seconds comapre to 228 -->title song is 228 secs (3mins 48sec) long 203 page_num++; //auto go to next page 204 } 205 break; 206 207 default: 208 printf("\nDANGER WILL ROBINSON!!!!"); 209 } 210 printf("\nCounter(%i) Page_num(%i), Page_Printed(%i)",counter_sixtith_sec,page_num,page_printed); 211 counter_sixtith_sec++; 212 213 } 214 215 ///----------------/// 216 217 } 218///-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~/// 219 220 221 //-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 222 // DATA DESTRUCTION 223 //BITMAP *black, *powered, *PPlogo, *gimp, *presents, *menuscreen; 224 al_destroy_bitmap(bg); 225 226 al_destroy_sample_instance(sample_inst); 227 al_destroy_sample(sample); 228 al_uninstall_audio(); 229 //-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 230 231 232 233 234 return 0; //Program Completed Successfully 235 236}

The kicker: The largest picture is only 414 KB...any thoughts to the memory issue?

torhu

You probably have to free the samples too, even if the docs don't say so.

Phrasz

I was thinking that too since I haven't tested "just" the bitmaps.

I'll try commenting out the audio, check the results, and then play with the audio after.

UPDATE:
It is the audio. The game without the music will run ~23-24 MBs, and I see that the memory is freed.

Next question: How do I properly free audio? (looking at this music post)

UPDATE 2:
Maybe This will work

torhu

I suppose that would be al_destroy_sample.

Matthew Leverton
Phrasz said:

al_set_sample(sample_inst, al_load_sample("music/01_TitleSong.ogg"));

That's not good.

Loading a sample uncompresses it into raw data. A 1 MB ogg file could be a 10MB sample when loaded. Furthermore, you are then attaching it to an instance and forgetting about it. So every time that line runs, you lose X MB... where X is the uncompressed size.

You don't need to even use sample instances if you simply want to play a clip:

al_reserve_samples(16); // sets up a default mixer, and creates 16 sample instances that Allegro manages for you

s = al_load_sample("music/01_TitleSong.ogg")
al_play_sample(s, ... );
// when finished:
al_destroy_sample(s);

All that said, if you want to conserve space, just stream the ogg music with al_load_audio_stream(). That way it doesn't need to uncompress the entire thing at once. This works great for background music.

Samples should be used for playing sound effects.

Phrasz

Thanks for the response!

I've been trying to get the files to stream, but do you know how I would stream multiple files at once?

Matthew Leverton

Call the function on two different files...

Phrasz

Gracias... I also looked at the ex_stream_file.c code

Now with the streams when I am done I use the al_destroy_audio_stream().
1)Would this need to be called before loading a new stream file for that same variable?
2)When it's called it will first call detach audio stream if the stream is in use...I assume this would stop the playing too?

Matthew Leverton
  1. Yes.

  2. Yes.

Phrasz

Thanks guys!

I now am able to keep the memory below 28 MBs!

As far as multiple streams (this is for a future reader) I just used two samples, and then am able to tie the streams to the mixer and detach them / destroy them as needed.

IE: Creation Functions

#SelectExpand
1//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~// 2// Audio Function Variables 3 4ALLEGRO_VOICE* voice; 5ALLEGRO_MIXER* mixer; 6 7 al_init_acodec_addon(); 8 9 if (!al_install_audio()) { 10 //abort_example("Could not init sound!\n"); 11 return 1; 12 } 13 14 if (!al_reserve_samples(1)) { 15 //abort_example("Could not set up voice and mixer.\n"); 16 return 1; 17 } 18 19 voice = al_create_voice(44100, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2); 20 if (!voice) { fprintf(stderr, "Could not create ALLEGRO_VOICE.\n"); return 1; } 21 22 #ifndef BYPASS_MIXER 23 mixer = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); 24 if (!mixer) { 25 fprintf(stderr, "Could not create ALLEGRO_MIXER.\n"); 26 return 1; 27 } 28 fprintf(stderr, "Mixer created.\n"); 29 if (!al_attach_mixer_to_voice(mixer, voice)){ 30 fprintf(stderr, "al_attach_mixer_to_voice failed.\n"); 31 return 1; 32 } 33 #endif 34 ALLEGRO_AUDIO_STREAM *stream, *stream2; 35 bool playing = true; 36 37 stream = al_load_audio_stream("music/01_TitleSong.ogg", 4, 2048); 38 al_attach_audio_stream_to_mixer(stream, mixer); 39 stream2 = al_load_audio_stream("music/Page_null.ogg", 4, 2048); 40 al_attach_audio_stream_to_mixer(stream2, mixer); 41 42 43//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~//

This is to stop a previous stream and load a new one:

#SelectExpand
1al_destroy_audio_stream(stream); 2stream = al_load_audio_stream("music/01_TitleSong.ogg", 4, 2048); 3al_attach_audio_stream_to_mixer(stream, mixer);

Matthew Leverton

The code is still a bit confusing.

If you are going to use your own mixer, you should create it before al_reserve_samples() and call al_set_default_mixer() on it before reserving the samples. Otherwise, al_reserve_samples() will create its own mixer and voice.

Or, you can do this:

and then you don't have to create your own mixer or voice.

Note that if you never call al_play_sample() then you don't even need to call al_reserve_samples().

Thread #607248. Printed from Allegro.cc