PDA

View Full Version : Freeing texture memory



gdbfun
12-23-2010, 11:32 AM
Hi,
I think proton is genius, but I'm having difficulty figuring out how to safely free memory from images that are no longer used.
The images are being created with CreateOverlayEntity, loading in from image files.
I believe they are being cached somewhere between proton and GLES.
It's on the native heap somewhere I suspect.
The images are not needed for very long, and need purged at a certain known time.

If this memory is not freed, my app crashes on Android due to low memory.
There are too many images to have in memory all at once on a mobile device.
The major symptom is that after awhile, newly loaded images are just black,
and the emulator/device android logs show android killing background jobs to free up memory, as android would do, and finally complaining about low memory, and then finally crashing due to being out of memory.

I notice there is no documentation for the ResourceManager yet, so I've tried to wing it.

I've tried KillAllResources() and that didn't seem to work right.
I didn't try RemoveTexturesNotInExclusionList because it's using the same mechanism to free things up as KillAllResources, which didn't work.
I've tried KillEntity, I've also tried using the C++ operator delete on the entity.
I've tried entity->RemoveComponentByName("OverlayRender");
I've tried entity->RemoveAllComponents();
I've tried SetSurface(NULL,true) which didn't work because I misread the second argument to SetSurface and the SAFE_DELETE was never called (I can't figure out a case in which it would be called).

Some of those attempts didn't work at all, and some helped a little but soon caused crashing.

At one point I put in some LogMsg code into the SurfaceAnim and Surface destructors, and noticed they were not getting called by what I was doing.

The only thing that seemed to work in freeing memory is to write my own member function to OverlayRenderComponent, which calls SAFE_DELETE on m_pTex (the normal destructor won't do it?). This completely seems to solve the memory problem, as Surface::Kill is finally called, which calls glDeleteTextures.
When this is used, android no longer kills background jobs or complains about low memory, and all images load.... once.
However, I'm not going to post that code because it causes other problems... when the game later returns to the same point and tries to recreate an entity that loads the same image filename again, android crashes with a segfault.

I don't completely understand the image/texture caching mechanism.
If I destroy an old, no longer needed Surface, what else may still be expecting that Surface and still dereferencing a pointer to it later when a new CreateOverlayEntity is created with the same filename as was originally used to load the now-destroyed surface?

Or alternatively, is there a way to turn off the texture caching mechanism?
Or just do a complete purge on an Entity AND it's images?

Thanks

Seth
12-23-2010, 01:08 PM
Sounds like you pretty much have it right, and just need to use the helper function DestroyUnusedTextures() in EntityUtils.cpp.

This runs through the entire entity hierarchy, figures out which textures are being used based on any entity types it recognizes (it knows OverlayRender and ScrollBarRender.. warning, I don't think I added support for the ProgressBarComponent yet, in case you use that) it finds and tells the resource manager to delete them.


So, just to clarify it:

OverlayRenderComponent uses GetResourceManager()->GetSurfaceAnim() to get the SurfaceAnim. (Which is really a subclassed Surface) - ResourceManager caches this, and returns the same instance whenever someone asked for the exact same filename.

At this point it is dangerous to completely clear the ResourceManager because the entity holding the OverlayComponent depending on the surface will crash (if it's still alive), as its surface pointer will be bad.

DestroyUnusedTextures() gets around this by figuring out what textures are still being used, and uses RemoveTexturesNotInExclusionList to only delete SOME of 'em.

If you want to handle memory yourself

Instead of using ResourceManager you can just do SurfaceAnim("filename.bmp"); or SurfaceAnim *pSurf = new SurfaceAnim("filename"); - this WILL actually delete the texture when you expect, and won't cache anything.

OverlayComponent CAN be used this way, but it's a little tricky. You have to init it like this:



Entity *pBG = CreateOverlayEntity(pParent, "MyEnt", "", 0,0);

SurfaceAnim *pSurf = new SurfaceAnim;
pSurf->LoadFile("crap.bmp");

OverlayRenderComponent *pOverlay = (OverlayRenderComponent*) pBG->GetComponentByName("OverlayRender");
pOverlay->SetSurface(pSurf, true);


Now the overlay "owns" the surface, and the true in SetSurface(pSurf, true) is saying "delete the surface yourself when you're killed, or a new surface is set".

So using that method may be what you want if the one above doesn't work. But keep in mind you may end up loading many copies of the same texture as no smart caching is done.

I should probably just add a normal component level parm like "uniqueInstance" and if set, it will just not use the ResourceManager, that would be simpler and not require that ugly cast to OverlayRenderComponent*.