Search Unity

Texture Memory is not being freed?

Discussion in 'General Graphics' started by DonKanallie, Nov 26, 2014.

  1. DonKanallie

    DonKanallie

    Joined:
    Jul 17, 2013
    Posts:
    10
    (This is a cross post from Unity Answers, I'm sorry if this is not appropriate, but I hope someone from Unity could reply)

    Unity seems to leave RGB data from each active texture in RAM, which IMHO should be unnecessary.
    An example: Consider an photo gallery application that displays a couple of pictures in a scrollable grid.
    Here some example code for Unity:

    Code (CSharp):
    1. var images = Resources.LoadAll<TextAsset>(path: "Images");
    2. foreach (var image in images) {
    3.      var texture = new Texture2D(width: 0, height: 0, format: TextureFormat.ARGB32, mipmap: false);
    4.      texture.LoadImage(image.bytes);
    5.      texture.Compress(highQuality: true);
    6.      texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
    7.      var sprite = Sprite.Create(texture: texture,
    8.                                 rect: new Rect(left: 0f, top: 0f, width: texture.width, height: texture.height),
    9.                                 pivot: new Vector2(0.5f, 0.5f));
    10.      var imageObject = new GameObject(name: image.name, components: typeof(Image));
    11.      var imageRenderer = imageObject.GetComponent<Image>();
    12.      imageRenderer.sprite = sprite;
    13.      imageObject.transform.parent = transform;
    14.      imageObject.transform.position = Vector3.zero;
    15.      Resources.UnloadAsset(image);
    16. }
    For my gallery sample app I created an Resources folder with about 50MB worth of PNGs. (Please note that the images use a .bytes extension and not .png, which means there are no asset importer settings for these images, everything is done during runtime)

    When I profile this (using Instruments on OS X) I see that the Resources.LoadAll call allocates about 50MB (which corresponds to the size of all images, to that is fine. Would be nice if a TextAsset would just be a file handle instead of immediately loading everything though).

    Next comes a loop which creates an Unity UI (The 4.6 UI) Image from the PNG. After all images are processed and displayed, the app takes about 400MB RAM.... which is shockingly large.

    As far I know from my own OpenGL experiments, creating a texture goes as follows:
    1. Load the PNG into RAM (Takes a few KB in memory)
    2. Decompress the PNG to RGB(A) (Takes a couple of MB in memory)
    3. Create a new texture in OpenGL and upload RGB data to VRAM
    4. Delete PNG and RGB data from main memory, only the OpenGL texture handle remains in memory as the texture data is now in VRAM on the GPU
    Which means that it looks like that Unity just leaves the RGB data in VRAM and the the main RAM. As a small check I wrote the same gallery app in Cocoa (The default Mac OS Application framework). It does somewhat the same as unity, find all images in a resources folder, load them, display them in a scrollable grid.

    Code (ObjC):
    1. NSArray *images = [[NSBundle mainBundle] pathsForResourcesOfType:@"bytes" inDirectory:@"Images"];
    2. for (NSString *imagePath in images) {
    3.      NSImageView *view = [[NSImageView alloc] initWithFrame:frame];
    4.      NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
    5.      view.image = image;
    6.      [imageGrid addSubview:view];
    7. }
    8.  
    And when profiling my app in Instruments I see that it only uses about 100 MB... for the exact same output.



    I also recorded some memory consumption metrics:

    • Empty Unity app: 63.52 MB
    • Empty Cocoa app: 56.12 MB
    • Unity without texture.Apply(false, true): 609.16 MB
    • Unity with texture.Apply(false, true): 403.93 MB
    • Cocoa app: 98.57 MB
    I also want to point out that the cocoa app launches nearly instantly while Unity takes about 3 seconds to process the textures.

    Am I missing something? This can't possible the be the best one can achieve in Unity! What am I missing here? Why is the memory consumption so crazy high?
     
    BrandyStarbrite likes this.
  2. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,655
    You don't appear to be calling Destroy() for the texture you created?
     
  3. DonKanallie

    DonKanallie

    Joined:
    Jul 17, 2013
    Posts:
    10
    You mean the Texture2D object? Because as soon as I Destroy() that object it won't be rendered the UI.Image anymore, which is not really helpful.

    Code (CSharp):
    1. var images = Resources.LoadAll<TextAsset>(path: "Images");
    2. foreach (var image in images) {
    3.      var texture = new Texture2D(width: 0, height: 0, format: TextureFormat.ARGB32, mipmap: false);
    4.      texture.LoadImage(image.bytes);
    5.      texture.Compress(highQuality: true);
    6.      texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
    7.      var sprite = Sprite.Create(texture: texture,
    8.                                 rect: new Rect(left: 0f, top: 0f, width: texture.width, height: texture.height),
    9.                                 pivot: new Vector2(0.5f, 0.5f));
    10.      var imageObject = new GameObject(name: image.name, components: typeof(Image));
    11.      var imageRenderer = imageObject.GetComponent<Image>();
    12.      imageRenderer.sprite = sprite;
    13.      imageObject.transform.parent = transform;
    14.      imageObject.transform.position = Vector3.zero;
    15.      Resources.UnloadAsset(image);
    16.      [B]Destroy(texture);[/B]
    17. }
    Did I misunderstood you?
     
  4. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,655
    Oh, I misunderstood - I thought you were talking about memory usage after finishing using the texture.

    My suspicion - without having a full repro project to dig into - is that this is a consequence of using the Resources bundle. You could try calling Resources.UnloadUnusedAssets() after the loop to see if that helps; but if not, it might be worth using AssetBundle instead of Resources as I think you get a bit more control over object lifetime that way.
     
  5. DonKanallie

    DonKanallie

    Joined:
    Jul 17, 2013
    Posts:
    10