|
al_draw_bitmap faster on backbuffer than on another bitmap? |
Dash125
Member #13,764
November 2011
|
First of all, hello everyone. When I tried to implement this (I.E. Using a bitmap to store the generated map image and then draw this on the backbuffer on all successive updates), I noticed things worked great as long as I stuck with really small maps, but when I tried larger maps (50x50) everything went to hell. The initial building of the map into the buffer bitmap took around 4 minutes, whereas drawing it on each frame into the backbuffer as done previously took a few seconds (Which was the reason why I tried to improve its performance by only drawing it once). Is it possible that drawing on an ALLEGRO_BITMAP is a LOT slower than drawing on the backbuffer? 1void CTileMap::paint(ALLEGRO_DISPLAY *display)
2{
3 if (!m_bmpMapBuffer) {
4 TileCoord tc;
5 TileGrid *tgTiles = NULL;
6 CLayer *lyrCurrentLayer = NULL;
7 CTileset *tlsCurrentTileset = NULL;
8 CTilesetImg *tliPalette = NULL;
9 ALLEGRO_BITMAP *bmpMesh = NULL;
10 int nTileIndex;
11 // int nTranspColor;
12
13 m_bmpMapBuffer = al_create_bitmap(m_nWidth * m_nTileWidth, m_nHeight * m_nTileHeight);
14 //al_set_target_bitmap(al_get_backbuffer(display));
15 al_set_target_bitmap(m_bmpMapBuffer);
16
17 // Iterate through the layers from bottom to top painting each one
18 for (LayerVector::iterator itLayer = m_vLayers.begin(); itLayer != m_vLayers.end(); ++itLayer) {
19 lyrCurrentLayer = (*itLayer);
20
21 tgTiles = lyrCurrentLayer->getTileGrid();
22
23 // Iterate through the rows of the current layer
24 for (int x = 0; x < lyrCurrentLayer->getWidth(); x++)
25 {
26 tc.x = x;
27
28 // Iterate through the columns of the current row of the layer
29 for (int y = 0; y < lyrCurrentLayer->getHeight(); y++)
30 {
31 tc.y = y;
32
33 // If the coordinate has no Tile element, skip to the next
34 if (tgTiles->find(tc) == tgTiles->end()) {
35 continue;
36 }
37
38 nTileIndex = tgTiles->at(tc)->getTileIndex();
39
40 // Iterate backwards through the available tilesets for the map in order to find the one corresponding to the current GId
41 for (TilesetVector::reverse_iterator itTileset = m_vTilesets.rbegin(); itTileset != m_vTilesets.rend(); ++itTileset)
42 {
43 tlsCurrentTileset = (*itTileset);
44
45 // If the GId is within the current tileset, draw it
46 if (nTileIndex >= tlsCurrentTileset->getFirstGId()) {
47 tliPalette = tlsCurrentTileset->getTilePalette();
48
49 bmpMesh = tliPalette->getImage();
50
51
52 al_draw_bitmap_region(bmpMesh,
53 tlsCurrentTileset->getTileX(nTileIndex),
54 tlsCurrentTileset->getTileY(nTileIndex),
55 tlsCurrentTileset->getTileW(),
56 tlsCurrentTileset->getTileH(),
57 (m_nTileWidth * x), (m_nTileHeight * y), 0);
58
59 // End the loop in order to avoid the tile from being overwritten by an empty image
60 break;
61 }
62 }
63 }
64 }
65 }
66 }
67 al_set_target_bitmap(al_get_backbuffer(display));
68 al_draw_bitmap(m_bmpMapBuffer, 0, 0, 0);
69}
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
Dash125 said: When I tried to implement this (I.E. Using a bitmap to store the generated map image and then draw this on the backbuffer on all successive updates), I noticed things worked great as long as I stuck with really small maps, but when I tried larger maps (50x50) everything went to hell. The initial building of the map into the buffer bitmap took around 4 minutes, whereas drawing it on each frame into the backbuffer as done previously took a few seconds (Which was the reason why I tried to improve its performance by only drawing it once). If your buffer bitmap was 50x50 tiles, then it could easily have exceeded the size limit of video bitmaps on your hardware and become a memory bitmap. If it was, and you then tried to draw your video bitmap tiles onto a memory bitmap it would have been horrendously slow due to reading from video memory and writing back to regular memory. I think your best bet is to a single tile sheet in video memory and create your tiles as sub bitmaps of this sheet. Then when you draw your tile map use al_hold_bitmap_drawing before and after you draw all of your sub bitmap tiles. The main problem is probably due to memory bitmaps being in use somewhere. Edit After looking at your code, you are creating a bitmap (likely a memory bitmap) on every drawing frame and then drawing that to your backbuffer. You don't want to create and redraw the background buffer on every frame. That defeats the purpose of pre drawing it. You're better off drawing directly to the backbuffer every frame, using sub bitmaps instead of drawing regions, and drawing all the sub bitmaps that share a common parent at the same time if you can. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Dash125
Member #13,764
November 2011
|
Edgar Reynaldo said: After looking at your code, you are creating a bitmap (likely a memory bitmap) on every drawing frame and then drawing that to your backbuffer. You don't want to create and redraw the background buffer on every frame. That defeats the purpose of pre drawing it. You're better off drawing directly to the backbuffer every frame, using sub bitmaps instead of drawing regions, and drawing all the sub bitmaps that share a common parent at the same time if you can. Actually, I'm only drawing the memory bitmap only once: m_bmpMapBuffer is a class member, and is initialized as NULL when the object is created, and will be created only once (Hence the null check at the beginning of the function), and will remain alive as long as the object lives. Edgar Reynaldo said: If your buffer bitmap was 50x50 tiles, then it could easily have exceeded the size limit of video bitmaps on your hardware and become a memory bitmap. If it was, and you then tried to draw your video bitmap tiles onto a memory bitmap it would have been horrendously slow due to reading from video memory and writing back to regular memory. I think your best bet is to a single tile sheet in video memory and create your tiles as sub bitmaps of this sheet. Then when you draw your tile map use al_hold_bitmap_drawing before and after you draw all of your sub bitmap tiles. The main problem is probably due to memory bitmaps being in use somewhere.
A few questions on this, since I'm quite a newbie on both Allegro and game development from scratch: Just in case, I will briefly describe the map circuit, since maybe my error is deeper than just the drawing code - The map is parsed and all pertinent objects are generated and initialized. (I know you can deduce most of this from my code, but I guess it can't harm to add all the information I have) edit: I just added the al_hold_bitmap_drawing to the method, and after some reading, set all bitmaps to be video bitmaps by default, and found no changes whatsoever |
Edgar Reynaldo
Major Reynaldo
May 2007
|
Major Edit Dash125 said: Actually, I'm only drawing the memory bitmap only once: m_bmpMapBuffer is a class member, and is initialized as NULL when the object is created, and will be created only once (Hence the null check at the beginning of the function), and will remain alive as long as the object lives. I totally missed the if (!m_bmpMapBuffer) { line.
1paint() {
2 create_buffer()
3 set_target(buffer)
4 for each layer
5 for each column x
6 for each row y
7 if (Tiles->find(Tile(x,y))) {goto next y}
8 reverse for each tileset tls
9 if (tls.contains(tileindex)) {
10 draw_bitmap_region(tileset->get_palette()->get_image(),....);
11 break for each tileset
12 }
13 end for
14 end for
15 end for
16 end for
17 set_target(allegro_backbuffer)
18 draw(buffer)
19}
Quote: - How can I can tell if a bitmap I declared lives in video memory or regular memory? (I thought that was automatically handled by Allegro). Use al_get_bitmap_flags to check what properties the bitmap has. Use al_set_new_bitmap_flags to tell allegro what kind of bitmap to create when you create or clone a new bitmap. I don't know what the default kind of bitmap is anymore, but I thought it would be a 'fail if unsuccessful video bitmap'. Quote: - The buffer bitmap being created on my method is 800x800px (16x16 px per tile), but according to al_get_display_option(display, ALLEGRO_MAX_BITMAP_SIZE), the maximum size for bitmaps is 4096. How is this possible? As long as al_get_new_bitmap_flags() & ALLEGRO_VIDEO_BITMAP is true, it should be making a video bitmap for you, especially if it is only 800x800. Quote:
- When the paint method is called, I check if the buffer bitmap has been created.
Ideally, I would do something like this (Edit - he already is) : 1class TileMap {
2public :
3 void Display() {
4 if (!buffer_drawn) {DrawBuffer();}
5 al_set_target_bitmap(al_get_backbuffer(display));
6 al_draw_bitmap(buffer , -camerax , -cameray , 0);
7 }
8 void DrawBuffer() {
9 if (!buffer) {
10 buffer = MakeBuffer();
11 }
12 // draw tile map onto buffer
13 buffer_drawn = true;
14 }
15};
I am still not sure what you did to make it take 4 minutes, or even several seconds to draw unless memory bitmaps were involved somehow. Edit Quote: edit: I just added the al_hold_bitmap_drawing to the method, and after some reading, set all bitmaps to be video bitmaps by default, and found no changes whatsoever If the tile bitmaps don't share a common parent (like a sub bitmap would, like I recommended earlier), then al_hold_bitmap_drawing probably won't do much for you. What graphics card are you using? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Dash125
Member #13,764
November 2011
|
I found the four minute problem: I had a batch of test map files which I had built using Tiled (The editor I chose for this project), and at some point must have renamed one that should never have made on this test in the first place (It was 50x50, but had an extremely large amount of layers). I replaced it by the one that should have been on the test (four 50x50 layers), and re-tested. The whole thing took around 1:12 minutes to draw the map buffer. (Sorry about the mix-up!) Since the tile sheet is on a single bitmap, and the map buffer is built by painting bits of the tile sheet onto it, I added the al_hold_bitmap_drawing before starting the loop and after finishing it. (Is this what you mean by parent bitmap?)
Edgar Reynaldo said: As long as al_get_new_bitmap_flags() & ALLEGRO_VIDEO_BITMAP is true, it should be making a video bitmap for you, especially if it is only 800x800.
1int main(int argc, char **argv)
2{
3 // Initialize allegro and do stuff
4 // ...
5 // Create display
6 // ...
7 al_set_target_backbuffer(display);
8 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
9
10 // Load a bitmap
11 // This is created as a VIDEO BITMAP
12 ALLEGRO_BITMAP *bitmap = al_load_bitmap("tilesetTest.png");
13
14 // Create an object with a bitmap attribute
15 MyClass mc;
16
17 // Create the same bitmap as before inside the init() method
18 // This is created as a MEMORY BITMAP
19 mc.init("tilesetTest.png");
20}
21
22class MyClass {
23public:
24 MyClass() { bmp = NULL; }
25 ~MyClass() { if (bmp) { al_destroy_bitmap(bmp); } }
26
27 void init(char *bitmapPath) {
28 bmp = al_load_bitmap(bitmapPath);
29 }
30
31 ALLEGRO_BITMAP * getBmp() { return bmp; }
32
33private:
34 ALLEGRO_BITMAP *bmp;
35}
LAST EDIT The only thing I'm still not sure of is this: If I use the tile sheet bitmap to draw the tiles onto the map buffer, is it right to use al_hold_bitmap_drawing? Read my next post! |
Edgar Reynaldo
Major Reynaldo
May 2007
|
Dash125 said: Since the tile sheet is on a single bitmap, and the map buffer is built by painting bits of the tile sheet onto it, I added the al_hold_bitmap_drawing before starting the loop and after finishing it. (Is this what you mean by parent bitmap?) I'm not sure if al_draw_bitmap_region benefits from al_hold_bitmap_drawing the way a sub bitmap would. You'd have to ask the developers. I would think that it does, but I don't know. I don't think al_hold_bitmap_drawing would do anything for drawing memory bitmaps though, and that may be why you didn't see any difference. Dash125 said:
I've just tried creating the bitmap on the main() which is where I create the display, and the result is a video bitmap! Are you calling your init method before the display has been created? You can't make a video bitmap without having a display to attach it to. There was some mention of auto converting memory bitmaps with the ALLEGRO_VIDEO_BITMAP flag to video bitmaps upon creating a display, but I don't know if that is the default or if it is in the current version of Allegro yet. Are you making your objects in a different thread? You can't make video bitmaps in a secondary thread unless it also creates another display. Dash125 said: I'm quite lost here... Shouldn't both bitmaps be created using the same settings? Assuming you create them in the same thread as your display, you create them after your display, and you either don't call al_set_new_bitmap_flags, or you call it with ALLEGRO_VIDEO_BITMAP, then yes they should be. Make sure they are with (al_get_bitmap_flags(bitmap) & ALLEGRO_VIDEO_BITMAP) Which version of A5 are you using? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Dash125
Member #13,764
November 2011
|
It turns out I wrote the conditional to check the bitmap flags wrong ( ). So, to bottom line it: I used al_set_new_bitmap_flags to use all bitmaps as video bitmaps, and re-tested on release mode (I'm using Visual Studio 2010) and everything worked like a charm. I even tested a 14 layers of 50x50 map, and took less than half a second to load and draw! Edgar Reynaldo said: Which version of A5 are you using? I'm currently using Allegro 5.0.4 and 5.0.5 Also, I'd like to thank you for both answering and following up on this, since it has helped me understand a lot better how Allegro works and is supposed to be used! |
|