[A5] Double buffering
ehguacho

hi everyone! i'm having some issues while double buffering in A5.

this my code:

#SelectExpand
1void CEmulator::DrawFrame() 2{ 3 ALLEGRO_BITMAP *DoubleBuffer = al_create_bitmap(256, 240); 4 al_set_target_bitmap(DoubleBuffer); 5 for(int x = 0; x < 256; x++) 6 { 7 for(int y = 0; y < 240; y++) 8 { 9 ALLEGRO_COLOR Color = al_map_rgb((ScreenBuffer[x][y] & 0xFF0000) >> 16, (ScreenBuffer[x][y] & 0x00FF00) >> 8, ScreenBuffer[x][y] & 0x0000FF); 10 al_put_pixel(x, y, Color); 11 } 12 } 13 al_set_target_bitmap(al_get_backbuffer(Screen)); 14 al_draw_bitmap(DoubleBuffer, 0, 0, 0); 15 al_flip_display(); 16}

...where ScreenBuffer is an array where i'm storing color information encoded in long format (e.g.: 0xFFFFFF is white).
as you can see, what i'm doing is to draw in a buffered bitmap some pixels with a specific color, then set the screen's back buffer as the target bitmap to show those pixels on the screen, and then update the display screen. but the problem is that nothing comes up to the screen.

on the other hand, i can do that like this:

#SelectExpand
1void CEmulator::DrawFrame() 2{ 3 al_set_target_bitmap(al_get_backbuffer(Screen)); 4 for(int x = 0; x < 256; x++) 5 { 6 for(int y = 0; y < 240; y++) 7 { 8 ALLEGRO_COLOR Color = al_map_rgb((ScreenBuffer[x][y] & 0xFF0000) >> 16, (ScreenBuffer[x][y] & 0x00FF00) >> 8, ScreenBuffer[x][y] & 0x0000FF); 9 al_put_pixel(x, y, Color); 10 } 11 } 12 al_flip_display(); 13}

this last piece of code works fine, but the problem is that is very slow, since the display isn't double buffered.

any hints on how can i solve this problem? thank you all in advance!

Matthew Leverton

It already is double buffered.

You just need to lock the bitmap with al_lock_bitmap() to gain speed.

ehguacho

thanks for your fast reply!

i've gained some speed, but still is pretty slow...

#SelectExpand
1void CEmulator::DrawFrame() 2{ 3 ALLEGRO_BITMAP *DoubleBuffer = al_create_bitmap(256, 240); 4 al_set_target_bitmap(DoubleBuffer); 5 al_lock_bitmap(DoubleBuffer, al_get_bitmap_format(al_get_backbuffer(Screen)), ALLEGRO_LOCK_WRITEONLY); 6 for(int x = 0; x < 256; x++) 7 { 8 for(int y = 0; y < 240; y++) 9 { 10 ALLEGRO_COLOR Color = al_map_rgb((ScreenBuffer[x][y] & 0xFF0000) >> 16, (ScreenBuffer[x][y] & 0x00FF00) >> 8, ScreenBuffer[x][y] & 0x0000FF); 11 al_put_pixel(x, y, Color); 12 } 13 } 14 al_unlock_bitmap(DoubleBuffer); 15 al_set_target_bitmap(al_get_backbuffer(Screen)); 16 al_draw_scaled_bitmap(DoubleBuffer, 0, 0, 256, 240, 0, 0, 256 * PixelSize, 240 * PixelSize, 0); 17 al_flip_display(); 18}

am i doing something wrong?

Matthew Leverton

You don't need that DoubleBuffer. You already have a double buffer by virtue of creating a display.

Furthermore, you definitely do not want to be creating a bitmap on every frame.

Edgar Reynaldo

Also, unless your ScreenBuffer array changes, you should draw it into a bitmap once, and then draw that bitmap instead of using al_put_pixel. And even if it does change, you may still want to have the results buffered in a bitmap, and only draw to it the parts that have changed.

ehguacho

tried that already, but it's still VERY slow...

#SelectExpand
1void CEmulator::DrawFrame() 2{ 3 al_set_target_backbuffer(Screen); 4 al_lock_bitmap(al_get_backbuffer(Screen), al_get_bitmap_format(al_get_backbuffer(Screen)), ALLEGRO_LOCK_WRITEONLY); 5 for(int x = 0; x < 256; x++) 6 { 7 for(int y = 0; y < 240; y++) 8 { 9 ALLEGRO_COLOR Color = al_map_rgb((ScreenBuffer[x][y] & 0xFF0000) >> 16, (ScreenBuffer[x][y] & 0x00FF00) >> 8, ScreenBuffer[x][y] & 0x0000FF); 10 al_put_pixel(x, y, Color); 11 } 12 } 13 al_unlock_bitmap(al_get_backbuffer(Screen)); 14 al_flip_display(); 15}

obviously there's something that i'm missing here...

Edgar Reynaldo

See my edit in my previous post.

ehguacho

Also, unless your ScreenBuffer array changes, you should draw it into a bitmap once, and then draw that bitmap instead of using al_put_pixel. And even if it does change, you may still want to have the results buffered in a bitmap, and only draw to it the parts that have changed.

i can't do that, since what i'm developing is a NES emulator, so i can't know when the screen objects changes.

Matthew Leverton

How do you create ScreenBuffer? Is there a reason you cannot use a bitmap directly? (Edit: You probably cannot, depends on what techniques you are using to keep ScreenBuffer updated.)

You can also lock bitmaps this way:

#SelectExpand
1ALLEGRO_BITMAP *bmp = al_get_backbuffer(Screen); 2ALLEGRO_LOCKED_REGION *lock; 3uint8_t *data; 4int row_size; 5 6al_set_target_bitmap(bmp); 7lock = al_lock_bitmap(bmp, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_WRITEONLY); 8data = lock->data; 9row_size = lock->pitch; // or simply row_size = 4 * 256; 10if (row_size < 0) row_size = -row_size; 11 12for (int y = 0; y < 240; ++y, data += lock->pitch) 13{ 14 memcpy(data, &ScreenBuffer[y][0], row_size); 15} 16al_unlock_bitmap(bmp);

Untested.

You are storing your ScreenBuffer as [x][y], but you should store it [y][x] for fastest access.

ehguacho

despite i'm rendering 256*240 = 61440 pixels, which is a lot, i've never had speed issues with A4, but now i need A5 because i've to render 2 separate windows.

ScreenBuffer is just an array of long values:

long ScreenBuffer[256][240];

...wich i'm filling with some of these values:

#SelectExpand
1static const long PALETA[64] = 2{ 3 0x757575, 0x271B8F, 0x0000AB, 0x47009F, 0x8F0077, 0xAB0013, 0xA70000, 0x7F0B00, 4 0x432F00, 0x004700, 0x005100, 0x003F17, 0x1B3F5F, 0x000000, 0x000000, 0x000000, 5 0xBCBCBC, 0x0073EF, 0x233BEF, 0x8300F3, 0xBF00BF, 0xE7005B, 0xDB2B00, 0xCB4F0F, 6 0x8B7300, 0x009700, 0x00AB00, 0x00933B, 0x00838B, 0x000000, 0x000000, 0x000000, 7 0xFFFFFF, 0x3FBFFF, 0x5F97FF, 0xA78BFD, 0xF77BFF, 0xFF77B7, 0xFF7763, 0xFF9B3B, 8 0xF3BF3F, 0x83D313, 0x4FDF4B, 0x58F898, 0x00EBDB, 0x000000, 0x000000, 0x000000, 9 0xFFFFFF, 0xABE7FF, 0xC7D7FF, 0xD7CBFF, 0xFFC7FF, 0xFFC7DB, 0xFFBFB3, 0xFFDBAB, 10 0xFFE7A3, 0xE3FFA3, 0xABF3BF, 0xB3FFCF, 0x9FFFF3, 0x000000, 0x000000, 0x000000 11};

Matthew Leverton
ehguacho said:

ScreenBuffer is just an array of long values:

Right ... switch to be [y][x] (an array of horizontal lines) and try my memcpy code.

(And if you don't want to change your code a lot to test that out, simply pretend the display is 240x256 and draw it sideways.)

ehguacho

i swapped Xs and Ys and got more speed. thanks a lot for that! :)
but your code have a casting error on this line:

data = lock->data;

"invalid conversion from 'void *' to 'uint8_t *'". i've tried to change the line to:

data = (void *)lock->data;

..and to:

data = (uint8_t *)lock->data;

...but then nothing comes up to the screen. any hints?

Matthew Leverton
ehguacho said:

"invalid conversion from 'void *' to 'uint8_t *'". i've tried to change the line to:

With C++ you need to cast it to uint8_t * yourself. Does this make any difference?

memcpy(data, ScreenBuffer + y, row_size);

Hmm, maybe it's picking up 0 for the alpha. Try using these as your colors: 0xFF757575... note the 0xff for alpha.

Or maybe use this format:

ALLEGRO_PIXEL_FORMAT_XRGB_8888

Not sure, but maybe the X means no alpha.

ehguacho

added the "0xFF" prefix to all of the color codes and nothing.
your routine is much more faster than a simple lock/draw/unlock, but i'm still stucked with this speed issue :(

Matthew Leverton

I still think you can do this:

ALLEGRO_BITMAP *ScreenBuffer = al_create_bitmap(256, 240);

And draw to it directly. Then use al_draw_bitmap() once per frame.

You can try out some of the techniques on this thread as opposed to using al_put_pixel() on it.

ehguacho

ok, now i have speed ^^

but now i have another problem (no joke! please don't hit me :S). the image i'm getting is just 256*250 pixels. how can i enlarge it to a 512*480 pixels? i have the same old speed issues while using al_draw_scaled_bitmap(). there's another memcpy() routine out there that allows me to do that?

by the way, ALLEGRO_PIXEL_FORMAT_ARGB_8888 gave me the best performance :)

Matthew Leverton

With Windows (Direct3D) this might do the trick:

d = al_create_display(256, 240);
al_resize_display(d, 512, 480);

If you don't acknowledge the resize event, the backbuffer stays the original size. I'd be interested to know if that works.

Otherwise... what did you end up doing? Did you convert ScreenBuffer to an ALLEGRO_BITMAP? How are you drawing to it?

ehguacho

just build the app as a GUI app. you have to see it! it just flyes :D

thanks for your replies!

Elias

If you don't acknowledge the resize event, the backbuffer stays the original size. I'd be interested to know if that works.

Oh god. What are you teaching people here :P

Thread #607950. Printed from Allegro.cc