Unity remove sprite from memory and replace in next frame.

Started by
7 comments, last by Scouting Ninja 6 years, 8 months ago

So I am working on a mobile game.

It uses slides for a story, the slides are very large. Each slide is almost 2048*2048; the max texture loading size I am using for the game.

 

My problem is that Unity keeps each slide in the memory after it's loaded, even when it will show only once per game. This leads to the game crashing on older mobiles.

My idea was to destroy each object after it was shown using a coroutine, so it deletes the past slide and loads the next slide. This worked because instead of crashing on 23 slides it crashed on 48 slides.

After some profiling I realized that destroy() isn't clearing all the memory that a slide used.

 

What I want to do now is assign a limited amount of memory as a slide slot. Then I need some way to unload the slide from the slot, freeing the slot for the next slide; without Unity storing the slides in the memory.

Any ideas on how I would do this? 

Advertisement

First thought is to use a better texture compression algorithm.  The DXT# formats (often called dds format or S3 algorithms) give a 6:1 compression ratio.  PVRTC2 gives 8:1 or even 16:1 compression depending on which sub-format you use.  Others like ETC and ASTC also have great compression rates but varying hardware support.  Make sure you have set Unity to compress those textures.

After that, make sure you are actually destroying the textures. It will have some memory that won't instantly vanish but will be cleaned up, but the bulk of the memory should be immediately released.

You should be able to load them and replace them all as you go. The resources will be released as you move along, and assuming you've released all the references, the other allocations will be picked up by the garbage collector in due time.  (Note: Don't force the garbage collector to run. It is designed by default to run at lower priority during idle cycles, forcing it to run usually means interrupting time-sensitive work.)

33 minutes ago, frob said:

 Others like ETC

I am using ETC1 with split alpha. It's the one that I find works on most mobiles, even if it has bad mips for iOS games.

I have designed all the art to take advantage of this and yes it reduces the 10mb textures to 2mb. Yet the problems are still there.

 

The worst part is I still have to add in sound.

38 minutes ago, frob said:

other allocations will be picked up by the garbage collector in due time.

It's the time that is a problem. The slides show one after the other, even with the bulk unloaded with destroy() it still builds to the crashing point before the slide show ends.

 

I feel like there should be a way just to load each slide over the last slide. So that the space used by it is now used by the new slide.

What methods are you using to load and unload the slides?

19 hours ago, ferrous said:

What methods are you using to load and unload the slides?

None they are loaded by Unity once they are needed. The are just marked not to load on start up or they would prevent the game from even working on old mobiles.

 

And just for clarification, exactly how old are these old phones you are talking about?

The official end of support for Android devices is 3 years after product launch or 18 months after first sale, which ever is longer. After that you're entirely on your own.

Apple devices are similar, 3 years of support.  The are called "vintage" for a few more years, then they're obsolete.

 

Everyone knows it is short, even us as consumers. However, as customers it is clear the devices are no longer supported, and as developers it is unrealistic to expect years-old devices to conform to this year's needs.

6 hours ago, frob said:

The official end of support for Android devices is 3 years after product launch or 18 months after first sale, which ever is longer. After that you're entirely on your own.

I am testing on as many as I can find. With a samsung galaxy 2.7 made in 2012 I think, as my oldest; android.

Although I am not aiming to get it working on all of them, just on most off them.

 

The problem isn't the devices it's my images. At 6mb in memory and 60 of them I would use 360mb normally. Except Unity isn't normal, it uses 720mb of memory to have them all loaded.

Even on a new device that is scary, especially because it kept building without cleaning the memory.

I found out why. Unity loads the texture to the scene and to the UI image, making two instances. The level instance was also why I could not clear the memory of them once I was done with them.

The pointer the scene kept was preventing the garbage disposal from collecting it. There also appears to be no way of removing that scene pointer.

To fix this I am using a separate level that is streamed over the other(additive scene). The level holds 5 slides, once I am done I destroy() the level and make a new one holding the next 5. This works for most mobiles and as a IEnumerator I can load it without the player noticing.

 

So it's kind of fixed. Except than now I can't resist finding a way to make it work on the old galaxy. I also think I know how:

In my search for clearing the memory I learned Unity has a resource loading function. This could be used at the start of the game to choose between two slide packs. One with 6mb images and the other with say around 500kb images. The APK will be larger yet if t works I will be able to make a game that almost anyone could play.

The only problem I can think with this is detecting the players device. I think that the DPI check can be used then if the player has <200 DPI the smaller texture is loaded. In the options I will allow the player to also set the texture pack if they know there mobile can use it.

For those wondering if it worked.

I made a global image array to hold the slides.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameGlobals : MonoBehaviour {

//Just so I can call it when needed
public static Sprite[] StoredSlides;

}

Then before I load the level I load the sprites based on DPI


using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameLoader : MonoBehaviour {

...
...
	private AsyncOperation LoadSync = null; //for loading the level
	private ResourceRequest request = null; //for laoding the slides
...
...
	private IEnumerator LoadLevel()
	{
		LoadSync = SceneManager.LoadSceneAsync("MainScene");
		yield return LoadSync;
	}

	private IEnumerator LoadLargeImages (string NameOfAnimation, int MaxFrames){
		for (int i = MaxFrames; i >= 1; i -= 1) {
			SpriteCount += 1;
			request = Resources.LoadAsync <Sprite>(NameOfAnimation+ SpriteCount.ToString());// The images follow MyName1 - MyName59
			GameGlobals.StoredSlides.SetValue (request.asset as Sprite, SpriteCount-1);
			yield return request; // return the sprite for use
		}
	}
  ...
  ...
    // Use this for initialization
	void Start () {
    	//Create a arry so we can use it
		GameGlobals.StoredSlides = new Sprite[60]; //Set size of array
		if (Screen.dpi >= 300 && OverSetting == false) { 
          // The OverSetting is to allow larger mobiles to set to low
			StartCoroutine (LoadLargeImages ("Large", 60));
		} else {
			StartCoroutine (LoadLargeImages ("Small", 60));
		}
	}
  ...
  ...
}

This works.

The Unity ResourceRequest keeps things in the APK and only loads the textures you need.

The 300 dpi works best, because all the mobiles that have a 1GB or more memory uses a larger dpi. Works on all the old mobiles I have gathered with no problems.

In the end it made the file 3mb larger, a extremely small sacrifice for compatibility.

This topic is closed to new replies.

Advertisement