Video Bitmap Issue
Acturo

I've only been working with Allegro 5.0.5 for a few weeks now. I a problem that I shouldn't be having. I have an ATI Raedon with 256MB RAM. I'm loading less than 5 MBs of bitmaps. No matter how I set al_set_bitmapflags(flags|ALLEGRO_VIDEO_BITMAP), I can get no more than a handful of MBs loaded into video memory(I assume I don't even need to change the bitmap flags as the default setting are what I need). I've read at least half of the posts that discuss this issue, with no information being present that leads me in a direction to resolve this issue.

I'm loading a modular game suite as a control frame with under 1MB of bitmaps and that works well with abundant FPS. However, when I load a game selected from a list, not one of those bitmaps load into video memory. With 256MB available, I don't understand the problem. I have multiple times verified that all needed addons are loaded successfully and in the correct order. 5 FPS is completely unacceptable. What could be preventing such a small number of bitmaps from loading into video RAM?

jmasterx

Could we see some of the code that might be causing the problem?

Acturo
#SelectExpand
1int main(int argc, char** argv) 2{ 3 int ret = 0; 4 ALLEGRO_DISPLAY *display = NULL; 5 6 if(!al_init()) 7 return -1; 8 9 if(!al_init_primitives_addon()) 10 return -2; 11 12 PHYSFS_init(argv[0]); 13 if(!PHYSFS_addToSearchPath("h:\\CodeBlocks Projects\\Ruth's Solitaire\\Cards.zip", 1)) 14 return -3; 15 al_set_physfs_file_interface(); 16 17 al_set_new_display_flags(ALLEGRO_NOFRAME | ALLEGRO_OPENGL_3_0); 18 display = al_create_display(800, 600); 19 if(!display) 20 return -4; 21 al_init_image_addon(); 22 al_init_font_addon(); 23 al_init_ttf_addon(); 24 al_install_mouse(); 25 26 CSolitaire sol; 27 ret = sol.Init(display, argv[0]); 28 if(!ret) 29 { 30 sol.Play(); 31 if(sol.IsLoaded()) 32 sol.Abort(); 33 } 34 if(sol.IsLoaded()) 35 sol.Abort(); 36 al_destroy_display(display); 37 return ret; 38} 39 40CSolitaire::CSolitaire() 41{ 42 m_x = 0; 43 m_y = 0; 44 m_w = 0; 45 m_h = 0; 46 m_bInitialized = false; 47 m_bmBackground = NULL; 48 m_bmInterface = NULL; 49 m_Display = NULL; 50 m_eq = NULL; 51 m_bnPlay = NULL; 52 m_bnOptions = NULL; 53 m_bnHelp = NULL; 54 m_bnDeal = NULL; 55 m_bnUndo = NULL; 56 m_bnQuit = NULL; 57 m_bnX = NULL; 58 m_txtTitle = NULL; 59 m_txtScoreLabel = NULL; 60 m_txtScore = NULL; 61 m_txtHighScoreLabel = NULL; 62 m_txtHighScore = NULL; 63 m_pGame = NULL; 64 m_pPlayPopup = NULL; 65} 66 67int CSolitaire::Init(ALLEGRO_DISPLAY* display, char* path) 68{ 69 ALLEGRO_BITMAP* bmFrame = NULL; 70 m_Display = display; 71 m_eq = al_create_event_queue(); 72 if(!m_eq) 73 return 1; 74 al_register_event_source(m_eq, al_get_display_event_source(display)); 75 al_register_event_source(m_eq, al_get_mouse_event_source()); 76 m_bmBackground = al_load_bitmap("Cards/Main_bkgnd.png"); 77 if(!m_bmBackground) 78 { 79 al_destroy_event_queue(m_eq); 80 return 2; 81 } 82 int flags = al_get_bitmap_flags(m_bmBackground); 83 al_set_new_bitmap_flags(flags | ALLEGRO_VIDEO_BITMAP); 84// if((flags & ALLEGRO_VIDEO_BITMAP) == ALLEGRO_VIDEO_BITMAP) 85// fprintf(stderr, "PlaceHolder is a video bitmap\n"); 86// else 87// fprintf(stderr, "PlaceHolder is NOT a video bitmap\n"); 88 89 m_bmInterface = al_create_bitmap(800, 600); 90 if(!m_bmInterface) 91 return 3; 92 93 bmFrame = al_load_bitmap("Cards/Frame.png"); 94 if(!bmFrame) 95 return 4; 96 97 // set drawing target to interface bitmap 98 al_set_target_bitmap(m_bmInterface); 99 al_draw_bitmap(bmFrame, 0, 0, 0); 100 al_draw_bitmap(m_bmBackground, 10, 50, 0); 101 al_destroy_bitmap(bmFrame); 102 103 // return drawing target to display 104 al_set_target_bitmap(al_get_backbuffer(display)); 105 106 // Create timers 107// m_Logic = al_create_timer(1000/30); 108// if(!m_Logic) 109// return 5; 110 m_Draw = al_create_timer(1000/60); 111 if(!m_Draw) 112 return 6; 113// al_register_event_source(m_eq, al_get_timer_event_source(m_Logic)); 114 al_register_event_source(m_eq, al_get_timer_event_source(m_Draw)); 115 116 // Seed the random number generator 117 srand((UINT)time(NULL)); 118 119 // Load buttons &settings 120 if(!LoadButtons()) 121 return 5; 122 LoadSettings(path); 123 m_bInitialized = true; 124 al_start_timer(m_Draw); 125// al_start_timer(m_Logic); 126 return 0; 127}

there are 6 buttons loaded with 3 80x20 bitmaps loaded into them. A handful of text based labelbs and outputs, none with the font loaded larger than 20.
This much of my code runs very smoothly.

Loading the next stage is where the problems begin.

#SelectExpand
1bool CGameBase::Init() 2{ 3 // Load images 4 if(!(m_bmCardBack = al_load_bitmap("Cards/Back.png"))) 5 return false; 6 if(!(m_bmBuf = al_create_bitmap(780, 540))) 7 return false; 8// if(!(m_bmBoard = al_create_bitmap(780, 1024))) 9// return false; 10 if(!(m_bmFocus = al_load_bitmap("Cards/Focus.png"))) 11 return false; 12 if(!(m_bmPlaceHolder = al_load_bitmap("Cards/PlaceHolder.png"))) 13 return false; 14 if(!(m_bmLock = al_create_bitmap(71, 96))) 15 return false; 16 17// al_set_target_bitmap(m_bmBoard); 18// al_clear_to_color(al_map_rgb(25, 100, 25)); 19 al_set_target_bitmap(m_bmLock); 20 al_clear_to_color(al_map_rgb(0, 0, 0)); 21 al_set_target_bitmap(al_get_backbuffer(m_Display)); 22 23 // Enable Buttons 24 m_pSol->EnableDealButton(); 25 m_pSol->EnableUndoButton(); 26 m_pSol->EnableQuitButton(); 27 m_pTitle = m_pSol->GetTitle(); 28 29 // Load High Score 30 char buf[MAX_PATH]; 31 GetPrivateProfileString(m_szHelpName, "High Score", "0", buf, MAX_PATH, m_szOptFile); 32 m_HighScore += atol(buf); 33 m_bInitialized = true; 34 return true; 35} 36 37bool CAcesUp::Init() 38{ 39 int i, x, y; 40 41 if(!CGameBase::Init()) 42 return false; 43 44 // Set Talon 45 x = 143; 46 y = 10; 47 if(!(m_Talon = new CTalon(x, y, 71, 96))) 48 return false; 49 if(!m_Talon->Init(m_eq, ID_TALON)) 50 return false; 51 52 // Set Tableau points 53 x = 239; 54 for(i=0;i<4;i++) 55 { 56 if(!(m_Tab[i] = new CTableau(x, y, 71, 96))) 57 return false; 58 if(!m_Tab[i]->Init(m_eq, ID_TABLEAU, i)) 59 return false; 60 x += 81; 61 } 62 63 // Draw Placeholders on Board buffer 64// al_set_target_bitmap(m_bmBoard); 65// DrawPlaceHolders(); 66// al_set_target_bitmap(al_get_backbuffer(m_Display)); 67 68 // Load Deck 69 if(!LoadDeck()) 70 return false; 71 72 // Set Ace Value to 14 73 m_Cards[12].SetVal(14); 74 m_Cards[25].SetVal(14); 75 m_Cards[38].SetVal(14); 76 m_Cards[51].SetVal(14); 77 78 // Init Scores 79 m_Score.Reset(); 80 m_Score.Update(); 81 return true; 82}

Is this enough to see what I'm doing?

jmasterx

A few things I noticed:

Any reason why you need an opengl 3.0 context explicitly? Usually just ALLEGRO_OPENGL is fine. Try that.

Usually you should init your addons before creating the display. That might be causing a problem.

You use 1000/60 to create your timer, you might want to double check that it is indeed 16.666 because neither your numerator or denominator are floats so C++ might do integer division which would give you a weird framerate maybe.

I would also highly recommend that you throw exceptions instead of returning numbers like that. I would much rather see "Allegro Display Failed To Create" in my console than 2 or something.

Also, there's no sense in checking for NULL every time you call new. The only way that will return NULL is if you run out of memory and if that happens the runtime will throw an out of memory exception or something.

You are often changing targets which requires render to texture support. Maybe you do not have that?

Thomas Fjellstrom

Also note that ALLEGRO_OPENGL_3_0 is only a modifier for ALLEGRO_OPENGL, if you don't specify ALLEGRO_OPENGL, Allegro will fall back to its default on a given platform. For windows the default is ALLEGRO_DIRECT3D.

Also ALLEGRO_VIDEO_BITMAP is the default.

Would you be able to post the entire code? Or even better, make a small, simple example that displays the same problems? (I would prefer that over your original code, that way if it is an allegro bug, it would be extremely obvious that it is an allegro bug, and be easier to debug).

Acturo
jmasterx said:

A few things I noticed:Any reason why you need an opengl 3.0 context explicitly? usually just ALLEGRO_OPENGL is fine. Try that.

I've been trying various unnecessary changes to my code attempting to track down the bottle neck. No other reason for for setting the display atm.

Quote:

Usually you should init your addons before creating the display. That might be causing a problem.You use 1000/60 to create your timer, you might want to double check that it is indeed 16.666 because neither your numerator or denominator are floats so C++ might do integer division which would give you a weird framerate maybe.I would also highly recommend that you throw exceptions instead of returning numbers like that. I would must rather see "Allegro Display Failed To Create" in my console than 2 or something.

Ok, I can try changing the order of init'ing the addons, again. I can typecast the timer rate, np. I'm working on a overloaded new operator to use as a mixin class(once I verify Allegro can meet my needs), so had planned to deal with that later.

Quote:

Also, there's no sense in checking for NULL every time you call new. The only way that will return NULL is if you run out of memory and if that happens the runtime will throw an out of memory exception or something.You are often changing targets which requires render to texture support. Maybe you do not have that?

Checking for NULL is good practice no matter how long you've been coding.???
Just how many cycles will it save to not do it?

The target bitmap is changed once per loop, no more than that other than in Init().

Loading and running the first stage(host frame with controls) runs like a charm. I haven't measured FPS, I've merely used fprintf every 100 frames. I see it printed more than twice a second.

Once I've loaded and closed the playlist popup and started the game, every single bitmap is a memeory bitmap, not a video bitmap and everything becomes moot with 5 FPS. If I want a card game where the cards don't move, I could use windoze and be done in a week or probably less.

When I close the game and only the frame is running again, the frame rate returns(all of the first stage of bitmap loading are video bitmaps and accelerated).

-------------------------------------------------------------------------------

Also note that ALLEGRO_OPENGL_3_0 is only a modifier for ALLEGRO_OPENGL, if you don't specify ALLEGRO_OPENGL, Allegro will fall back to its default on a given platform. For windows the default is ALLEGRO_DIRECT3D.Also ALLEGRO_VIDEO_BITMAP is the default.Would you be able to post the entire code? Or even better, make a small, simple example that displays the same problems? (I would prefer that over your original code, that way if it is an allegro bug, it would be extremely obvious that it is an allegro bug, and be easier to debug).

Changing ALLEGRRO_OPENGL_3_0 TO ALLGRO_OPENGL solved the issue.

Thanks all for your input.

jmasterx
Acturo said:

Checking for NULL is good practice no matter how long you've been coding.???
Just how many cycles will it save to not do it?

When it comes to Operator new, I feel it is better that the operator checks for failure and throw an exception. That way you can catch it somewhere else, and keep your code clean and modular. I always check for NULL when using C functions that could fail, but in C++ there are exceptions which IMO result in cleaner modular code.

Thomas Fjellstrom

I find exceptions clutter code more than explicit error checking does. Especially when you have to start creating a boat load of custom exception classes. It gets to be a mess very quickly.

Aaron Bolyard

Unrelated, but exceptions are also very expensive on some embedded hardware, especially game consoles. In some cases, they are unsupported on these systems. So if you develop a very budget game and happen to have a console development kit, keep this in mind...

(Alternatively, you can use homebrew compilers for current consoles and come to the same conclusion, such as on the Wii.)

jmasterx

I only use them for exceptional circumstances that would otherwise result in a crash or a state where the application can no longer continue. This way I can quickly see why it crashed. I agree that if you used them for anything that could return NULL it would get messy. As for expensiveness, since after my exceptions are thrown the application closes, I don't care if it takes a second or two.

In theory, I do not expect my games to throw any exceptions for the end user. If it does, there is a bug.

Thomas Fjellstrom
jmasterx said:

I only use them for exceptional circumstances that would otherwise result in a crash or a state where the application can no longer continue.

I like to use assert/ASSERT for that. Just make it puke and throw a backtrace to the debugger.

Oscar Giner
Acturo said:

Checking for NULL is good practice no matter how long you've been coding.???
Just how many cycles will it save to not do it?

His point is that new will throw an exception if there's any error, so the check for NULL is useless (the code inside the if is just unreachable).

You can use new (nothrow) if you don't want new to throw an exception on failure, and make it return NULL instead.

jmasterx

I like to use assert/ASSERT for that. Just make it puke and throw a backtrace to the debugger.

I thought assertions were skipped in release builds? That is why I use exceptions.

Thomas Fjellstrom
jmasterx said:

I thought assertions were skipped in release builds? That is why I use exceptions.

Yup. I want the app to crash hard if theres a fatal error. Maybe my idea of a fatal error is different from yours. I tend to split errors into "unimportant", "recoverable", "fatal, but needs graceful handling" and "fatal". That last one usually means things are so far beyond anything I can check for that things crash regardless. second last one is where I'd check return values, and bail out of a function, which would bubble back up and somewhere the app would puke. recoverable is stuff like not being able to fetch a file off the internet, the app shouldn't crash, but it might not be as useful, and unimportant is... well I'm not sure, doesn't often come up, more like an error I turn into a warning and paper over completely.

But yes, ASSERT is compiled out in release mode. So the overhead isn't there. And you should have done a bunch of testing in debug mode, so you have a chance to hit those ASSERTs, meaning you get a nice backtrace rather than some nasty chain of exceptions.

weapon_S
jmasterx said:

Usually you should init your addons before creating the display. That might be causing a problem.

Is this true?

Acturo said:

However, when I load a game selected from a list, not one of those bitmaps load into video memory.

What does that mean? What does the code that does work look like? You know your mainline (probably) always calls sol.Abort ? Lines 27 to 35 make no sense to me.

Acturo
weapon_S said:

What does that mean? What does the code that does work look like? You know your mainline (probably) always calls sol.Abort ? Lines 27 to 35 make no sense to me.

Yes, my c++ is weak. I'm aware of this.

The lines you question are there to test for Initialization failure and returns what stage of initialization failed. If 0 is returned, the code enters the play loop of the host frame, load graphics for the Play Popup dialog, then waits for the user. Yes, I know I could have just used the constructor for initialization, but I wanted a return value. So, I chain up to the base class with the calls to Init(), and chain up with the Abort() functions as well. Maybe Abort() was a bad choice for the function name? Regardless, it gives me the opportunity to sort the errors rather than just going straight to crash mode.

Neil Walker
Acturo said:

the lines you question are there for Initialization

I think what he means is you've got

  if(!ret)
  {
    sol.Play();
    if(sol.IsLoaded())
      sol.Abort();
  }
  if(sol.IsLoaded())
    sol.Abort();

so you are calling if(sol.IsLoaded()) and sol.Abort() whether you enter the condition or not, so you only need have it once after the condition.

weapon_S

TBH my internal parser crashes when encountering redundant conditionals. (I really didn't get it ;D ) Also it is confusing that CGamebase returns 0 on failure and you said CGamebase was where the trouble begins. Is CSolitaire derived from CGamebase? If so, I'd exepct a call to CGamebase::init() somewhere.
Is "Abort" the opposite of "init"? (I also struggle to define the opposite of "init" ;) ) Using an init method which returns false on error is ok, if done consistently.
Perhaps you should more clearly define the problem (What class is not functioning. Which bitmap fails to load. Does it fail to load if you don't load others.)

Acturo

I think what he means is you've got
if(!ret)
{
sol.Play();
if(sol.IsLoaded())
sol.Abort();
}
if(sol.IsLoaded())
sol.Abort();

I saw that not long after I posted it and corrected the redundant call.

if(!ret)
sol.Play();
if(sol.IsLoaded())
sol.Abort();

weapon_S said:

TBH my internal parser crashes when encountering redundant conditionals. (I really didn't get it ;D ) Also it is confusing that CGamebase returns 0 on failure and you said CGamebase was where the trouble begins.

CSolitaire::Init() returns int, CGameBase::Init() returns bool so it can be the first check during game instance loading.

Quote:

Is CSolitaire derived from CGamebase? If so, I'd exepct a call to CGamebase::init() somewhere.

CSolitaire serves as little more than a host frame, event router, and play loop. CGameBase won't be initialized until a game is selected from the resulting popup from clicking the "Play" button CSolitaire displays and manages.

Quote:

Is "Abort" the opposite of "init"? (I also struggle to define the opposite of "init" ;) ) Using an init method which returns false on error is ok, if done consistently.

Yes.

Quote:

Perhaps you should more clearly define the problem (What class is not functioning. Which bitmap fails to load. Does it fail to load if you don't load others.)

All classes are functioning. The problem was that I wasn't happy with the default DirectX window and needed to specify OpenGL; which I did incorrectly.

Thread #609640. Printed from Allegro.cc