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:
and these protected fields:
and add this protected method:
and change getWidth() and getHeight():
note this removes the const aspect of these two methods. Anyone got some insight on the advisability of that ?
GuiBitmapCtrl.cc
in the constructor, initialize those fields!
in GuiBitmapCtrl::initPersistFields(), expose mLazyLoad to script:
replace GuiBitmapCtrl::setBitmap(const char *name, bool resize) with this:
in GuiBitmapCtrl::setBitmap(const TextureHandle &handle, bool resize), replace this line:
at the top of GuiBitmapCtrl::onRender(), insert this:
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.
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.
About the author
#2
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.
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.
Associate Orion Elenzil
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.