Game Development Community

lazy loading of bitmap files in GuiBitmapCtrl

by Orion Elenzil · 04/20/2009 (11:37 pm) · 2 comments

This resource implements lazy loading of bitmap files in GuiBitmapCtrl.
Motivation:
this probably won't be of much benefit to most applications,
but for some particular ones it can be fairly nice.

I have a GuiScroll which contains sometimes several hundred GuiBitmapCtrls, only a handful of which are actually within view at a given time.
I create the controls all at once, setting their bitmaps along the way.
In stock TG*, the bitmaps are loaded as soon as the bitmap is set, which causes a "chunk" in execution: the app pauses for say a second.

To alleviate this, this resource implements a new option on GuiBitmapCtrl: "lazyLoad".
If true, a GuiBitmapCtrl will defer actually loading the bitmap until the control is actually rendered.

In my case, creating 2*575 GuiBitmapCtrls was part of a chunk of 2.0 seconds, and turning on lazy bitmap loading changed the chunk to 0.3 seconds. Obviously there's more work to do, for example, not creating 2*575 guiControls all at once, or possibly moving some of it into C, but this definitely helped.

I've been a little agressive and made the default setting for lazyLoad be true.
It's possible this will mess some stuff up. If i discover such, i'll change the resource.

This resource is pretty simplistic, it's quite possible i've overlooked some details, please point them out if you notice them!

This is based on a TGE 1.3.5 codebase, but is likely applicable with minimal modification to all Torque technologies which implement GuiControls in C++.

Familiarity with C++ and Torque tech is assumed.

GuiBitmapCtrl.h
in the declaration, add this public field:
public:
   bool    mLazyLoad;            // don't actually load the bitmap until onRender().

and these protected fields:
protected:
   bool    mNeedLazyLoad;
   bool    mResizeBitmap;        // storage for bool resize in setBitmap.

and add this protected method:
void lazyLoad();

and change getWidth() and getHeight():
note this removes the const aspect of these two methods. Anyone got some insight on the advisability of that ?
S32 getWidth ()       { lazyLoad(); return(mTextureHandle.getWidth ()); }
   S32 getHeight()       { lazyLoad(); return(mTextureHandle.getHeight()); }


GuiBitmapCtrl.cc
in the constructor, initialize those fields!
mLazyLoad            = true;     // for more near-term stability but potentially lower performance you might want to change this to false
   mNeedLazyLoad        = false;
   mResizeBitmap        = false;

in GuiBitmapCtrl::initPersistFields(), expose mLazyLoad to script:
addField("lazyLoad"           , TypeBool    , Offset(mLazyLoad           , GuiBitmapCtrl));

replace GuiBitmapCtrl::setBitmap(const char *name, bool resize) with this:
void GuiBitmapCtrl::setBitmap(const char *name, bool resize)
{
// Con::errorf("%s() - loading bitmap %-50s", __FUNCTION__, name ? name : "(null)");  // optional debug print

   mBitmapName   = StringTable->insert(name);
   mNeedLazyLoad = true;
   mResizeBitmap = resize;

   if (!mLazyLoad)
   {
      lazyLoad();
   }
}

void GuiBitmapCtrl::lazyLoad()
{
   if (!mNeedLazyLoad)
   {
      return;
   }

   mNeedLazyLoad = false;

   if (*mBitmapName)
   {
      setBitmap(TextureHandle(mBitmapName, BitmapTexture, true), mResizeBitmap);
   }
   else
   {
      setBitmap((TextureHandle)NULL);
   }

   setUpdate();
}

in GuiBitmapCtrl::setBitmap(const TextureHandle &handle, bool resize), replace this line:
if (resize) {
with this:
if (resize && handle != NULL) {

at the top of GuiBitmapCtrl::onRender(), insert this:
lazyLoad();

ta dah!

i'll leave it as exercise to extend this to GuiBitmapButtonCtrl.
GuiMLTextCtrl looks a bit tricky.
There's probably no point in adding it to GuiChunkedBitmapCtrl.

#1
04/21/2009 (12:33 am)
I'm realizing that there might be a more powerful form of laziness i could employ here.

My actual example does the following 575 times all at once:
* create a container control
* add & initialize two GuiBitmapCtrls
* add & initialize two GuiTextCtrls
* check the containers nameSpaceList and conditionally bindClassName() on it.

If, instead of doing all that it just did this:
* create a container control
* give the container control an "OnFirstRender" callback

and then the callback did the initialization, that might be a good thing.
#2
04/21/2009 (1:20 am)
re the more powerful form of laziness,
i think it's a good idea and one i may use going forward,
but i think it's something you want to know you're doing when designing the system. ie, with my code as currently designed it would take a lot of work to be lazy.