Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Saving a part of the backbuffer to ALLEGRO_BITMAP

This thread is locked; no one can reply to it. rss feed Print
Saving a part of the backbuffer to ALLEGRO_BITMAP
APrince
Member #12,698
March 2011

Hi, what I need to do is to save the part of the backbuffer (because I need to save complex multisampled primitive that I don't want to draw everytime the screen refreshes again, because it is very slow...). Now I now that HW accelerated multisampling can be used only when drawing to backbuffer, so this is probably what I need to do.

I tried the following...

#SelectExpand
1 2 ALLEGRO_BITMAP* backbufferImage = al_get_backbuffer(display); 3 ALLEGRO_BITMAP* image = al_create_bitmap(region.size.x, region.size.y); 4 ALLEGRO_DISPLAY* currentDisplay = al_get_current_display(); 5 al_set_target_bitmap(image); 6 al_draw_bitmap_region(backbufferImage, region.origin.x, region.origin.y, region.size.x, region.size.y, 0, 0, 0); 7 al_set_target_backbuffer(currentDisplay);

- display is valid and properly initialized variable
- it maybe confusing why there are "2 displays", actually both pointers point to the same object, it's just "forward thinking".
- it always crashed with "access violation" when calling al_draw_bitmap_region.

Am I missing something? I don't insist on doing this this way, I just need to make it as fast as possible...

Thanks a lot.

Trent Gamblin
Member #261
April 2000
avatar

Does the Allegro example 'ex_blit' work for you?

APrince
Member #12,698
March 2011

It does.

Trent Gamblin
Member #261
April 2000
avatar

Hm, maybe you can make a test case? A small program to demonstrate the crash with just the bare necessities?

APrince
Member #12,698
March 2011

OK then, here's the demo:

#SelectExpand
1bool as_al_init(int display_width, int display_height, ALLEGRO_DISPLAY** display, ALLEGRO_EVENT_QUEUE** event_queue){ 2 if (!al_init()){ 3 std::cerr << "Failed to initialize allegro!" << std::endl; 4 return false; 5 } 6 if (!al_install_mouse()){ 7 std::cerr << "Failed to install mouse!" << std::endl; 8 return false; 9 } 10 if (!al_install_keyboard()){ 11 std::cerr << "Failed to install keyboard!" << std::endl; 12 return false; 13 } 14 if (!display) return false; 15 al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST); 16 al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST); 17 if (!(*display = al_create_display(display_width,display_height))){ 18 return false; 19 } 20 21 if (!event_queue) {al_destroy_display(*display); return false;} 22 if (!(*event_queue = al_create_event_queue())){ 23 al_destroy_display(*display); 24 std::cerr << "Failed to create event queue!" << std::endl; 25 return false; 26 } 27 if (!(al_init_image_addon())){ 28 std::cerr << "Failed to initialize image addon." << std::endl; 29 al_destroy_display(*display); 30 return false; 31 } 32 if (!(al_init_primitives_addon())){ 33 std::cerr << "Failed to initialize primitives addon." << std::endl; 34 al_destroy_display(*display); 35 return false; 36 } 37 38 return true; 39} 40 41void as_al_cleanUp(ALLEGRO_DISPLAY* display, ALLEGRO_EVENT_QUEUE* event_queue){ 42 if (event_queue) al_destroy_event_queue(event_queue); 43 if (display) al_destroy_display(display); 44 45 al_shutdown_primitives_addon(); 46 al_shutdown_image_addon(); 47 al_uninstall_keyboard(); 48 al_uninstall_mouse(); 49 al_uninstall_system(); //all allegro shutdowns have to go before this one... 50} 51 52int main(int argc, char* argv[]) 53{ 54 ALLEGRO_DISPLAY* display = NULL; 55 ALLEGRO_EVENT_QUEUE* event_queue = NULL; 56 57 if (!as_al_init(640, 480, &display, &event_queue)) return 1; 58 59 ALLEGRO_BITMAP* bmp = al_load_bitmap("bckg.png"); 60 61 al_clear_to_color(al_map_rgb(0,0,0)); 62 al_draw_bitmap(bmp, 200, 200, 0); 63 64 ALLEGRO_BITMAP* backbufferImage = al_get_backbuffer(display); 65 66 ALLEGRO_BITMAP* image = al_create_bitmap(400,100); 67 ALLEGRO_DISPLAY* currentDisplay = al_get_current_display(); 68 al_set_target_bitmap(image); 69 al_draw_bitmap_region(backbufferImage, 200, 200, 400, 100, 0, 0, 0); 70 al_set_target_backbuffer(currentDisplay); 71 72 al_flip_display(); 73 74 Sleep(3000); 75 76 as_al_cleanUp(display, event_queue); 77 return 0; 78}

//I use 5.0.1 but I tried 5.0.5 and it was the same...

Trent Gamblin
Member #261
April 2000
avatar

Is it supposed to crash? It doesn't here on MacOS X. If you're running Windows, can you try putting a "al_set_new_display_flags(ALLEGRO_OPENGL);" before creating your display? See if that works. Also, are you sure that the png is found and loaded? Try drawing it to the screen to make sure?

APrince
Member #12,698
March 2011

I am sure, because I can draw it and see it. The line where it crashes is still this one:

al_draw_bitmap_region(backbufferImage, 200, 200, 400, 100, 0, 0, 0);

When i insert the line al_set_new_display_flags(ALLEGRO_OPENGL); as you suggested, it works fine. The problem is that I cannot use OpenGL. Not yet. Somehow it takes a long time to load...

Trent Gamblin
Member #261
April 2000
avatar

Ok then it's a bug in the D3D driver. The code for drawing from the backbuffer to other bitmaps I was never really sure of and am still not sure what's the best way to do it. It's always been pretty slow but a crash is not good. Problem is I've got stuff to do. Maybe tonight I can look at it and try to fix it. If you want to look at it the code is in src/win/d3d_bmp.cpp around line 661 and calling into some functions from there.

APrince
Member #12,698
March 2011

Thank's a lot for Your time. Well I could look at the code but I doubt that would be of any good. The thing is that I have a basic knowledge of OpenGL (univerzity course scope), but I know almost nothing about DirectX...

Trent Gamblin
Member #261
April 2000
avatar

Could you please upload the image you're using? I tried your example on Win7 x64 and it doesn't crash with the image I tried. It could also be something fixed in 5.1, so I'm attaching my compiled example which I'd like you to try:

http://www.allegro.cc/files/attachment/605602

If you could also go to http://www.kludx.com and run the binary from there and submit the info and give me the link, or find info on your graphics card that is already on the site, that would be most helpful.

APrince
Member #12,698
March 2011

Now this is interesting... The example you posted works fine (after downloading all the additional dependencies...) - no crash. My system is also Win7 x64.

I ran kluDX but I don't exactly know how am I suppose to send you the link when everything I got from the program is "Data sent to server. Thank You!". And I can't find the Graphics card on the site niether. But it's GTX 460 (1GB version).

I attach the image I tried to render in the simple example I posted. But I don't think that matters, because I can render it with no problems. It's just that I cannot draw from backbuffer...

//edit: I found another interesting thing. When I use Allegro 5.0.5 instead of 5.0.1 in this simple example, then the line:

al_draw_bitmap_region(backbufferImage, 200, 200, 400, 100, 0, 0, 0);

...does not crash but it doesn't do, what it should (meaning it does nothing), which is again fixed when i set OpenGL rendering...

Trent Gamblin
Member #261
April 2000
avatar

How does that line not do what you want? It's the same line as before right? What happens when you do that with 5.0.5? Do you have a screenshot or explanation of what's drawn?

APrince
Member #12,698
March 2011

By "it does nothing", I mean exactly that it does nothing :-) . The bitmap that I draw to stays the same as it was before executing this line. This happens with 5.0.5 with 5.0.1 it crashes with "access violation" as I mentioned before.

Trent Gamblin
Member #261
April 2000
avatar

Well, the only way this is going to get resolved is in the 5.1 branch before it's backported to 5.0.x. So if you want a fix, first try 5.1 (you'll likely have to compile it yourself). If it doesn't work there, then I will try and fix it.

APrince
Member #12,698
March 2011

Ok then, I will. Thanks a lot.

//edit: I'll do it yesterday (too busy now).

//edit2: OK, I've tried it out with 5.1.1 and it behaves exactly the same way as with 5.0.5. The exact code I run is this one:

#SelectExpand
1bool as_al_init(int display_width, int display_height, ALLEGRO_DISPLAY** display, ALLEGRO_EVENT_QUEUE** event_queue){ 2 if (!al_init()){ 3 std::cerr << "Failed to initialize allegro!" << std::endl; 4 return false; 5 } 6 if (!al_install_mouse()){ 7 std::cerr << "Failed to install mouse!" << std::endl; 8 return false; 9 } 10 if (!al_install_keyboard()){ 11 std::cerr << "Failed to install keyboard!" << std::endl; 12 return false; 13 } 14 if (!display) return false; 15 //al_set_new_display_flags(ALLEGRO_OPENGL); 16 al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST); 17 al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST); 18 if (!(*display = al_create_display(display_width,display_height))){ 19 return false; 20 } 21 22 if (!event_queue) {al_destroy_display(*display); return false;} 23 if (!(*event_queue = al_create_event_queue())){ 24 al_destroy_display(*display); 25 std::cerr << "Failed to create event queue!" << std::endl; 26 return false; 27 } 28 if (!(al_init_image_addon())){ 29 std::cerr << "Failed to initialize image addon." << std::endl; 30 al_destroy_display(*display); 31 return false; 32 } 33 if (!(al_init_primitives_addon())){ 34 std::cerr << "Failed to initialize primitives addon." << std::endl; 35 al_destroy_display(*display); 36 return false; 37 } 38 39 return true; 40} 41 42void as_al_cleanUp(ALLEGRO_DISPLAY* display, ALLEGRO_EVENT_QUEUE* event_queue){ 43 if (event_queue) al_destroy_event_queue(event_queue); 44 if (display) al_destroy_display(display); 45 46 al_shutdown_primitives_addon(); 47 al_shutdown_image_addon(); 48 al_uninstall_keyboard(); 49 al_uninstall_mouse(); 50 al_uninstall_system(); //all allegro shutdowns have to go before this one... 51} 52 53int main(int argc, char* argv[]) 54{ 55 ALLEGRO_DISPLAY* display = NULL; 56 ALLEGRO_EVENT_QUEUE* event_queue = NULL; 57 58 if (!as_al_init(640, 480, &display, &event_queue)) return 1; 59 60 ALLEGRO_BITMAP* bmp = al_load_bitmap("bckg.png"); 61 62 al_clear_to_color(al_map_rgb(0,0,0)); 63 al_draw_bitmap(bmp, 200, 200, 0); 64 65 ALLEGRO_BITMAP* backbufferImage = al_get_backbuffer(display); 66 67 ALLEGRO_BITMAP* image = al_create_bitmap(400,100); 68 ALLEGRO_DISPLAY* currentDisplay = al_get_current_display(); 69 al_set_target_bitmap(image); 70 al_draw_bitmap_region(backbufferImage, 200, 200, 400, 100, 0, 0, 0); 71 al_set_target_backbuffer(currentDisplay); 72 73 al_draw_bitmap(image, 0, 0,0); 74 75 al_flip_display(); 76 77 Sleep(3000); 78 79 as_al_cleanUp(display, event_queue); 80 return 0; 81}

The problem is that the line

al_draw_bitmap_region(backbufferImage, 200, 200, 400, 100, 0, 0, 0);

has no effect because the consequent calling of

al_draw_bitmap(image, 0, 0,0);

has no impact on what has already been rendered (image stays blank...).

So I suppose that the problem is initialy in

al_set_target_bitmap(image);

If I uncomment this line:
//al_set_new_display_flags(ALLEGRO_OPENGL);

then it works as expected.

Trent Gamblin
Member #261
April 2000
avatar

Ah... I see the problem. When you use multisampling with D3D, you can't lock the backbuffer. So comment out the two lines after the OPENGL line and it should work.

I'm not sure if there is a way to draw from backbuffer to an image without locking in D3D. If you can find one I'd be interested in implementing it.

APrince
Member #12,698
March 2011

So basically I need to use OpenGL, or I have to drop multisampling... Do I get it? Or what is the problem with "locking" as you mention?

//edit: What am I actually trying to acomplish is, that I need to render a graph, function of which is very difficult (time consuming) to evaluate. This graph, however, does not refresh very often so my idea was to render it to the image which can then be drawn immediately without the need to calculate the function (as long as it is up to date...). But I can't render to an image when using multisampling (actually I can but it would not be multisampled...). So that's why I wanted to copy the content of the backbuffer. Anyway, can you think of some alternate way to do this? (well, I know that I can store the values of the function in some array, but that would have to be dynamically allocated and thus not as fast as saving the whole graph as an image in video memory).

Trent Gamblin
Member #261
April 2000
avatar

You either have to find an alternative to locking that allows drawing parts of the backbuffer to bitmaps when multisampling is on, use OpenGL, or don't use multisampling. But I'm not really sure why you need to draw the backbuffer to a bitmap. Maybe if you tell me your use case I could figure something out.

APrince
Member #12,698
March 2011

Well I just updated the post above before reading your answer, so the reason is there...

Thanks a lot.

//edit: I found this outline on StackOverflow (thread). It seems helpful, but it is of course not clear to me whether that would solve the problem...

  1. rt = get render target surface (this can be surface of the texture, or backbuffer, etc.)

  2. if rt is multisampled (GetDesc, check D3DSURFACE_DESC.MultiSampleType), then: a) create another render target surface of same size, same format but without multisampling; b) StretchRect from rt into this new surface; c) rt = this new surface (i.e. proceed on this new surface).

  3. off = create offscreen plain surface (CreateOffscreenPlainSurface, D3DPOOL_SYSTEMMEM pool)

  4. device->GetRenderTargetData( rt, off )

  5. now off contains render target data. LockRect(), read data, UnlockRect() on it.

  6. cleanup

Trent Gamblin
Member #261
April 2000
avatar

Ok, I see... when I researched this last time I didn't find a way to do it, but I'll make a note to research it again.

EDIT: Can you please try the attached patch and tell me if it works? Seems to work here. Thanks.

http://www.allegro.cc/files/attachment/605622

APrince
Member #12,698
March 2011

Well that means to build Allegro on my own I guess... All right I'll do it when I wake up (it's 2 o'clock am here...).

Thank's.

P.S. I tried doing this with ALLEGRO_OPENGL. But when I render the graph from the image in memory (region that I copied from backbuffer to ALLEGRO_BITMAP), it does not look exactly the same as the original result. It seems to be more aliased even though some multisampling is obviously there... Almost like there was some post-processing when flipping buffers :-) But I will try the DX version before going further.

Thank's again.

//edit: I'm just not able to build it even without doing the change... The CMake configuration and generating works fine as well as doing "BUILD ALL". But INSTALL gives some strange error...

103> CMake Error at cmake_install.cmake:38 (FILE):
103> file INSTALL cannot find
103> "C:/Users/Aros/Libraries/allegro_my_build/lib/RelWithDebInfo/allegro-5.0.dll".

I don't want to bother you with that, because that is of course not even related to the discussed problem. That's just to let you know that I'm not able to test it... It's just that I can't build Allegro.

//edit2: One more question... When I copy anything (using al_draw_bitmap_region(backbufferImage, region.origin.x, region.origin.y, region.size.x, region.size.y, 0, 0, 0);), does this include aplha channel*? If so, can I do it such a way, that the resulting image is 100% opaque? Maybe using this function is not even a good idea and I should use blit or something like that. I just don't really know...

*Normally it would but when the source image is backbuffer I don't really understand what would the alpha channel be good for...

//edit3: Yeah, the image copied from the backbuffer is definitely diferent from the original. I just can't explain that. When I copy the backbuffer to the ALLEGRO_BITMAP and then render it again, it's different. To be more specific, the multisampled line is different. The rest stays the same... (see attachements). This concerns OpenGL case. I was not able to run corrected DX version so far...

Go to: