Reloading assets on resume - android

I'm working in an Android game using libGDX framework while a learn from their wiki and Learning Libgdx Game Development book. The author of the book defines an Assets class to load game assets using an AssetManager. Every time the game is resumed the assets need to be reloaded so he use a call to Assets.intance.init(new AssetManager()) what reload the assets and create new objects to store textures references.
When I do this in my game, after resume, all I have are black boxes where I had nice textures so I suppose that the problem here is that I'm using the old references. I put here part of the code from the book:
AssetManager#init
public void init(AssetManager assetManager) {
/* Dispose previous asset manager */
if (this.assetManager != null) {
this.assetManager.dispose();
}
this.assetManager = assetManager;
assetManager.setErrorListener(this);
assetManager.load(Constants.TEXTURE_ATLAS, TextureAtlas.class);
/* Start loading assets and wait until finished */
assetManager.finishLoading();
Array<String> assetNames = assetManager.getAssetNames();
Gdx.app.debug(TAG, "Assets loaded: " + assetNames.size);
for (String assetName : assetNames) {
Gdx.app.debug(TAG, "Asset: " + assetName);
}
TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS);
/* Create game resource objects. Here I get what I need from the atlas */
player = new AssetPlayer(atlas);
enemy = new AssetEnemy(atlas);
}
When I create my game objets I use Assets.instance.player and Assets.instance.enemy to store a reference to textures as the author of the book does so this could be the problem. The thing is that aftere re-read the book, I don't see how he solve this problem.
I'm pretty sure that I can solve the problem changing references in my game objects but all is becoming so messy. My real question is, how should I manage game assets in my game? I've been searching a lot for game examples but most of them don't use an AssetManager but instead static variables to textures.
Should I keep references of textures in game objects? It is really necessary to reload assets on resume? How could I reload textures in my game objects when all the objects are inside a worldController that don't know when a game is being resumed?

You're doing it correctly, as far as I can tell, at this level. You really do need to reload the textures, as your OpenGL context was lost, and all "pointers" into OpenGL-internal state are stale.
As you point out, because your AssetManager.player property points to a new object, anything that cached an old pointer is stale after a restart, and that is probably the source of your problems. (Though its hard to say for certain.)
If you look at the Libgdx SuperJump demo, they also cache all their asset pointers in static fields, but notice that the render calls effectively look up the texture every call. See WorldRenderer.renderBob(). One alternative would be to do a pass over your objects after (re)-loading assets to have them "refresh" their pointers the assets they use.

Have you tried not to call Assets.intance.init(new AssetManager()) on resume method (and also not to call Assets.intance.dispose() on pause method)?
Please read this link:
https://github.com/libgdx/libgdx/wiki/Managing-your-assets
At the end (Resuming with a Loading Screen) it says:
On Android your app can be paused and resumed. Managed OpenGL resources like Textures
need to be reloaded in that case, which can take a bit of time. If you want to
display a loading screen on resume, you can do the following after you created your
AssetManager.
Texture.setAssetManager(manager);
In your ApplicationListener.resume() method you can then switch to your loading screen and call AssetManager.update() again until everything is back to normal.
If you don't set the AssetManager as shown in the last snippet, the usual managed texture mechanism will kick in, so you don't have to worry about anything.
So, I believe that the author's approach is no longer necessary.
You only need to call Assets.intance.dispose() on dispose method in your "Game" class (I mean, the class that implements ApplicationListener).

Related

Android Rubik's Cube Kociemba optimal solver memory shortage

I'd like to ask for some help about the following problem I have.
I'd like to create an application that solves the Rubik's cube with an optimal solution. I downloaded the following library, whitch supposedly does just that using the Kociemba's Algorithm.
http://kociemba.org/twophase.jar
Apparently it can solve the cube in under 0.5 sec, but in my app it never returned the solution due to memory problems. I know it works, I tested it with wrong inputs and it returns the documented error codes.
I call it in my onCreate method like this:
resultTxt = Search.solution(data, 21, 10, true);
resultTxt is a String variable and it should contain the solution.
It quickly eats up the memory.
I tried it with IntentService without success. By this I mean it didn't really changed anything.
As i didn't find any evidence of anyone using this library in any android application, I thought I would ask someone who is more experienced than me.
Is there any way I could make this work on Android, or is this as impossible as I thought?
It may be a bit late, but I was also facing this issue quite recently when I was working on a Rubik's-Cube-solving-robot using an Android-Smartphone for scanning the cube and computing the solution, so I'll put here what I have found out.
What is the problem?
Let's start off by discussing where the problem causing that performance issue actually is located.
The reason for that being so slow is the class CoordCube, which looks (very simplified) like this:
class CoordCube {
short[] pruneTables;
static {
/* compute and save ~50MB data in `pruneTables` */
}
}
Basically what it does, is to load a pretty big amount of data into lookup-tables which are required for a fast solving procedure. This loading is automatically executed by the JVM once this class is first instantiated. That happens on line 159 in Search.solution():
/* simplified code of solution() */
if (cube.isValid()) {
CoordCube c = new CoordCube(); // pruning tables will be loaded on this line
That is also the reason why this method executes in negligible time as long as the passed cube is invalid, since it never gets to load the tables.
Possible Solutions:
Now that we have identified where the problem is located, let's focus on how to solve it.
I have come up with 3 different approaches, where the first one is probably the easiest (but also the slowest execution wise ...) and is also used in my App. The other two are just ideas on how to improve the performance even more.
Approach 1:
The first and most simple approach is to just manually preload the lookup tables in a kind of LoadingActivity with a ProgressBar showing our current progress. For that we first want to be able to manually control exactly when which tables are loaded (when the class is first instantiated is not precise enough), like this:
loadTable1() {
/* load pruning table number 1 */
}
For that I have written some simple utility here (code is too long to paste). Make sure to check out my instructions there on how to properly import that code in your App.
Also we will probably want to do the loading in the background, namely in an AsyncTask. This is how I have done it in my application (PruneTableLoader is included in the previous link):
private class LoadPruningTablesTask extends AsyncTask<Void, Void, Void> {
private PruneTableLoader tableLoader = new PruneTableLoader();
#Override
protected Void doInBackground(Void... params) {
/* load all tables if they are not already in RAM */
while (!tableLoader.loadingFinished()) { // while tables are left to load
tableLoader.loadNext(); // load next pruning table
publishProgress(); // increment `ProgressBar` by one
}
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
/* increment `ProgressBar` by 1 */
}
}
When using my PruneTableLoader, the loading of all tables needs about 40s on my Samsung Galaxy S3 with 250 MB RAM free. (in contrast it needs well over 2min when loading them automatically and in addition often results in a crash ...)
That may still sound quite slow considering it needs < 1s on PC, but at least you must only do that once, since Android caches the static-variables and you so don't have to load them on every startup of your App.
Approach 2: (untested)
I assume it would be faster to save the pruning tables in a file or a database and load them from there instead of always recomputing them. I have not yet tested that though and it will probably require quite some work getting the saving and loading to work properly. (also maybe it's not even faster because of access times)
Approach 3: (untested)
Well, the hardest and also by decades most work expensive solution would be, to simply rewrite the whole algorithm in C or C++ and invoke it in the App via JNI. (Herbert Kociemba has not published his C-sourcecode yet as far as I know ...)
This is going to be the performance wise fastest solution for sure. (also for the solving procedure itself)
All in all approach 1 is probably the effort/benefit-wise best approach for the beginning (and also was for me), so I would recommend you to go with that, in case the loading time is not such a huge issue for your Application.
I'm not completely satisfied with the performance of that myself though, so I may try out approach 2 and maybe even approach 3 some time in the future. In case I do that, I will update this post with my results then.

Memory Error whilst setting image following screen rotation on Mono for Android

Things were going well until I switched off the screen lock on my device, then things started going wrong intermittently.
I've managed to track the issue down and have some workarounds in mind BUT I would like to know if there is 'best practice' for avoiding or removing the issue.
The issue:
I have an application which changes images based on the application status.
The images are not huge but quite large (231k~) and are stored as resources.
After several screen rotations (I counted 27 with a project using a single ImageView), loading the images fails with Exception of type 'Java.Lang.OutOfMemoryError'
Stripped down to the barest project, the following demonstrates the problem:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
var imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( Android.Graphics.BitmapFactory.DecodeResource( this.Resources, Resource.Drawable.Ready) );
}
The above code is the only method I used to reproduce the issue.
Whilst attempting to resolve, I extended the example so that imageView was released in OnDestry:
protected override void OnDestroy ()
{
base.OnDestroy ();
imageView.SetImageBitmap( null );
imageView.DestroyDrawingCache();
imageView.Dispose();
}
This made no difference unless I added GC.Collect() which I don't want to do.
The best workaround I've currently thought of so far would be to modify the code as follows:
static Bitmap _ready = null;
private Bitmap GetReadyImage {
get {
if (_ready == null) {
_ready = Android.Graphics.BitmapFactory.DecodeResource (this.Resources, Resource.Drawable.Ready);
}
return _ready;
}
}
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( GetReadyImage );
}
This relies upon a static reference to each Bitmap and a property accessor for each.
I could even write a method which stores the images in a static List to save writing property accessors for each different property/variable.
I could perhaps add the flags ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize |ConfigChanges.KeyboardHidden) but this would break the normal Activity lifecycle which I've read isn't best practice?
I find it strange that having scoured the web, I've not yest encountered similar issues or examples. I'm left wondering how most others deal with this?
Any thoughts or comments are much appreciated.
I can only approach this problem from a truly native perspective, as I have not worked directly with the Mono framework.
The described symptoms are 100% indicative of a memory leak in the Activity, but the code shows no real evidence. If you can truly produce the issue with a project containing only one Activity and those four lines of code, it sounds to me like it is perhaps a framework bug that ought to be filed with Xamarin. Have you attempted to create the same simple project in pure Java to see how the results fare on the same device/emulator you are using? It would also be interesting to know if the issue is localized to a specific version of Android. I have never seen this particular behavior before in a native application project.
The awkward part is your statement that forcing a garbage collection makes the problem go away. You're right, you shouldn't have to do that, but a true memory leak (i.e. an unreleased reference) would still persist even if you hit the GC several times. The Android paradigm of destroying and re-creating the Activity on each rotation is such that even if the old Activity lived for awhile, when memory was tight it (and all its references) would quickly be collected to make room for a new instance. If this is not happening, and each Activity is living on past even the system triggered GC passes, perhaps there is a stuck reference in the native code generated by Mono.
Interestingly enough, technically your workaround actually does introduce a true leak, by attaching the Bitmap to a static field that is never cleared. However, I agree that in comparison it seems like a more efficient move. A simpler workaround might also be to code your Activity to manually handle configuration changes (I don't know if Mono is different, but this is accomplished by adding android:configChanges="orientation" to the manifest file). This will keep your Activity from being recreated on each rotation, but it may also require you to reload your view hierarchy if you have different layouts for landscape and portrait. However, even if you have to do this the Acitivity instance will be the same you can safely save the Bitmap without resorting to a static field.
However, if you cannot reproduce the problem with the same project in a native Java project, I would report a Mono bug.
Hard to see without the entire code but it obviously sounds like you have a memory leak. Screen rotation (due to the destroying/creation of the activity) is known to cause these. You might want to have a read at this article by Romain Guy as well as this talk from last year's IO.

Best practice for storing images to use on short term

So, I'm using custom number tiles for images. The images are stored as resources. I've been tracing a memory leak, and have cause to believe the method I am storing these images for use is suspect. Currently, I'm doing this:
private void loadImageList()
{
Log.d(TAG,"Reloading List "+imageList);
if (imageList==null || imageList.size()<10)
{
imageList=new ArrayList<Bitmap>(10);
imageList.add(0,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number0));
imageList.add(1,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number1));
imageList.add(2,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number2));
imageList.add(3,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number3));
imageList.add(4,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number4));
imageList.add(5,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number5));
imageList.add(6,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number6));
imageList.add(7,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number7));
imageList.add(8,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number8));
imageList.add(9,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number9));
}
}
public void onStart()
{
super.onStart();
loadImageList();
}
What will happen is if I open and close this application repeatedly, then the system won't have enough memory to add an image to the ImageList. I'm setting the image of the button like this (But with an object ImageButton, of course). I should add that the application is threaded, and this call resides in a runOnUiThread(Runnable)
ImageButton.setImageBitmap(imageList.get(current_var));
I've tried deleting the images in the onStop() command, but it will sometimes cause a crash when the thread tries to allocate the image stored in memory to the button, due to the threaded nature of the beast.
So, is there a better way that I can load these images that won't cause a memory leak?
I realized something after posting this. I could just make the imageList static. These images aren't going to change from one view to the next. The Bitmaps don't contain a view, so the memory won't lock up. I'm already checking to see if the values exist before I'm using them. And this function is used so frequently that holding on to static memory won't hurt other aspects of the program. Sometimes it just helps to put it into words..

Loading textures at random place

I'm trying to load all the game data at the same time, in my game scene's constructor. But it fails, because texture loading works only in an opengl context, like if load method called from draw frame or surfacechanged. But i think it's ugly to load textures when the drawframe first called or something similar. So is it possible somehow to separate my loading part from opengl functions?
I have exactly the same problem.
My solution is using the proxy textures. It means that when you're creating textures using some data from memory or file you're creating the dummy texture that holds the copy of that memory data or the file path (you can preload the data into memory for faster loading).
After that the next time my renderer calls bind() (which is something like glBindTexture) I check whether there is data to load and if it exists I just create the new texture and load the data.
This approach fits best to me, because in my case textures could be created from any thread and any time.
But if you want to preload all textures you can just do that in onSurfaceCreated or onSurfaceChanged
The same applies to buffers.
Another approach is using the native activity (check the NDK example). In this case you can handle context manually but it requires API level 9.
But i think it's ugly to load textures when the drawframe first called or something similar.
Actually deferred texture loading is the most elegant methods. It's one of the key ingredients for games that allow for traveling the world without interrupting loading screens. Just don't try to load the whole world at once, but load things, as soon they are about to become visible. Use Pixel Buffer Objects to do things asynchronously.

Do I have to delete textures in GLSurfaceView?

After creating textures in Android OpenGL ES, do I have to explicitly delete these textures later using glDeleteTextures()? If so, how would I set up a callback to do this?
Thanks in advance
If you are no longer using a texture, then you should call glDeleteTextures() so that the underlying hardware resources can be freed up.
But as EboMike said, you have to be careful about using a texture that was created from a previous context instance; to expand on that answer, I like to create a texture-wrangling class which wraps the texture's ID and which calls glDeleteTextures() from its finalize() method, but then also have an app-static int named "contextId" which gets incremented every time the GL context is created (i.e. in onSurfaceCreated); the texture wrangler should track the value of contextId when it's instantiated, and if the value changes on it, that means it should reload its textures (keeping in mind that the old texture IDs were already discarded along with the old context).
No, they are automatically deleted once you move away from your app. The greater pitfall actually is to disregard the texture IDs if your app becomes active again - keep that in mind when your onSurfaceChanged is called again.

Categories

Resources