Allegro.cc - Online Community

Allegro.cc Forums » Off-Topic Ordeals » Allegro, Direct 3D, and OpenGL - A tale of dumb woe

This thread is locked; no one can reply to it. rss feed Print
Allegro, Direct 3D, and OpenGL - A tale of dumb woe
André Silva
Member #11,991
May 2010
avatar

I have a quick story to share, one that will hopefully help any users that had the same problem I did.
Also, do keep in mind that I've been using Allegro for years, but I do not claim to know everything about it. Some of the stuff I say here might be absolute nonsense -- feel free to correct me!

I'm working on a game that had a very peculiar glitch: if you zoomed out, the stage would gradually fade into darkness, but the objects and HUD would remain fine. The big problem was that this only happened under Linux; it was fine under Windows.

For months, I tried to discover the problem. Eventually, I discovered that the problem would not happen if I had mipmapping off. Well, time to submit this to the Allegro forums, I thought to myself. But I decided to take it a step further, and tried to understand why that was.

The map geometry is fixed, so instead of drawing every polygon every frame, I just drew it on image buffers once, and then drew said buffers in-game. Well, if mipmapping was the problem, surely everything would be fine if I made my buffers' dimensions not be in the power of 2. ...Oh, they already aren't (800 by 800).

I eventually checked the Allegro source code. And then I noticed an interesting comment on one of the files... OpenGL, for backwards-compatibility purposes, makes it so that every texture is actually in power-of-two dimensions, by adding padding. This would explain why the problem only happens with mipmaps on, and under Linux! Linux uses OpenGL (Windows uses Direct3D by default), and mipmaps are only valid for bitmaps with powers of two -- which is actually every bitmap in Allegro's OpenGL.

...But why in the world was it fading out? If the problem was figuring out why something is or isn't using mipmapping, that'd be fine, but I still had to find out why everything went black. It was actually something simple, that should've been obvious: I first create the buffer images in their final size. They start off completely transparent. Only then do I draw my polygons on top of them. Turns out the mipmaps are generated when an image is created, so any change that happens from there on out does not get transferred to the mipmap. Because of linear interpolation while drawing, the further I zoomed out (the closer the image buffers were to taking less space on-screen, and hence, the closer the next mipmap level was), the darker everything became!

I fixed it by copying my buffer onto a different bitmap, with al_copy_bitmap, which recreated the image's mipmaps.

So in conclusion, the map was fading because the buffer map images were being mipmapped into nothingness, which was only the case because mipmaps don't update as you update your image, and mipmaps were only valid for those bitmaps in the first place because of OpenGL backwards compatibility.

It was a crazy ride, but for the first time in ages, I was actually pleased about solving that bug. You know those glitches you spend ages finding the solution for, only for it to be a typo? That's so disappointing! For this bug, I actually investigated, read source codes, and experimented, and in the end, I came up with a pretty satisfying explanation.

I hope this helps anyone else that is wondering why mipmaps are going crazy under Linux (or Windows).

Gideon Weems
Member #3,925
October 2003

That was a surprisingly satisfying read. I'm glad it worked out. Do you think mentioning this in the Allegro documentation would be worthwhile and not entirely out of place?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

André Silva
Member #11,991
May 2010
avatar

Ah yes, I knew there was more to my story than just posting it.
More than just warning newcomers that might fall prey to the same mistake, I too agree we should update the documentation, and adding a function like the one Edgar suggested would probably be for the best, too. I'm guessing updating the mipmaps might be done by Direct3D or OpenGL themselves, not necessarily Allegro, so I can't imagine it being any harder than a single function call.

Gideon Weems
Member #3,925
October 2003

Are you able to provide a test program and describe its expected output? We should probably get confirmation for different setups.

André Silva
Member #11,991
May 2010
avatar

Sure thing.

#SelectExpand
1//This program demonstrates a "ghost image" effect problem while scaling with OpenGL and mipmaps. 2#include <allegro5/allegro.h> 3#include <allegro5/allegro_image.h> 4 5int main() { 6 float scale = 1.0; 7 bool scale_going_up = false; 8 9 al_init(); 10 al_init_image_addon(); 11 al_install_keyboard(); 12 13 //We need OpenGL and mipmapping for this. 14 al_set_new_display_flags(ALLEGRO_OPENGL); 15 al_set_new_bitmap_flags(ALLEGRO_MIPMAP); 16 17 ALLEGRO_DISPLAY* display = al_create_display(400, 400); 18 ALLEGRO_TIMER* timer = al_create_timer(1.0 / 60.0); 19 ALLEGRO_EVENT_QUEUE* queue = al_create_event_queue(); 20 al_register_event_source(queue, al_get_timer_event_source(timer)); 21 al_register_event_source(queue, al_get_keyboard_event_source()); 22 23 ALLEGRO_BITMAP* img = al_load_bitmap("bmp1.png"); 24 ALLEGRO_BITMAP* new_img = al_load_bitmap("bmp2.png"); 25 26 //bmp1.png is old. We want bmp2.png on img, now. 27 al_set_target_bitmap(img); { 28 al_draw_bitmap(new_img, 0, 0, 0); 29 } al_set_target_backbuffer(display); 30 31 ALLEGRO_EVENT ev; 32 al_start_timer(timer); 33 34 while(true) { 35 al_wait_for_event(queue, &ev); 36 if(ev.type == ALLEGRO_EVENT_TIMER) { 37 38 ALLEGRO_TRANSFORM t, old; 39 al_identity_transform(&t); 40 al_scale_transform(&t, scale, scale); 41 42 al_copy_transform(&old, al_get_current_transform()); 43 al_use_transform(&t); { 44 45 al_clear_to_color(al_map_rgb(128, 0, 0)); 46 al_draw_bitmap(img, 0, 0, 0); 47 al_flip_display(); 48 49 } al_use_transform(&old); 50 51 //Animate the scaling. 52 scale += (0.40 / 60.0) * (scale_going_up ? 1 : -1); 53 if(scale < 0.4) scale_going_up = true; 54 if(scale > 1.2) scale_going_up = false; 55 56 } else { 57 if(ev.type == ALLEGRO_EVENT_KEY_DOWN) return 0; 58 } 59 } 60 return 0; 61}

To run it, you'll need a bmp1.png and a bmp2.png. They should both have the same dimensions, be distinct enough, and not have power-of-two dimensions, for clarity's sake.

When the program runs, the bitmap img will be drawn on screen, and will oscillate in size with time. When img is created, it has bmp1.png as its contents. The mipmaps are created for bmp1.png. We then draw bmp2.png on img, because, for the sake of the demo, bmp2.png is the image we actually want to show.

If OpenGL and mipmapping are on, as they are in the code, the problem will appear: when the image is shown at less than 1.0 scale, the program will begin using the mipmaps to render. Except because the mipmaps were not updated when we drew the final image (bmp2.png), the mipmaps will contain bmp1.png's visuals. In this example, it becomes very clear. Disabling either mipmapping or enabling Direct3D (not on Linux) will hide this problem, for the reasons I explained before.

The thing is, there's nothing inherently wrong about this behavior. Like I explained before, this happens because mipmaps aren't updated, and mipmaps only work on all bitmaps because of OpenGL compatibility. It's just very misleading, and can cause a few head-scratchers.

Gideon Weems
Member #3,925
October 2003

Thank you for the explanation. I ran the test program and now realize how problematic such behavior can be. This is one of those idiosyncrasies that has the power to make programmers pull their hair out for hours. It was nice of you to file a report.

I've attached the test program you provided, as code boxes have clipboard issues with certain browsers. I have also attached two image files for convenience.

This thread should probably be moved to the development forum.

André Silva
Member #11,991
May 2010
avatar

I'm glad to hear that! I initially thought this was just an episode where I was too oblivious to realize an obvious gimmick in Direct3D and OpenGL differences, but apparently I'm the survivor of a war. I'm all in favor of, at the very least, having a warning set up on the documentation.

SiegeLord
Member #7,827
October 2006
avatar

Mip-maps are sort of... poorly supported, as you've found out. This seems like a black hole of API design, but at least we could add a al_update_mipmaps as Edgar suggested.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Go to: