Unity3D: loading external images

So I’ve been doing various tests to see if I can port Harmoniq, a project of mine, to Unity3D for crossplatform reasons. And not only crossplatform, and this is what this post is about.

You see, any heavy image processing operation written in native iOS code consumes shitload of memory and processor time. In fact, so much that I’ve been optimizing them for 3 months and still get user reports about performance and memory problems. Imagine, I had to write about 200 lines of iOS native code to get single pixel from a photo and not crash the app due to memory pressure. Now, imagine a color picker that can be dragged across the photo taking pixel samples – realtime. Unity does this with single GetPixel() call.

So, Unity pixel sampling performance is perfect, as expected, it’s gaming engine after all, it’s supposed to work with textures reeeeeallly fast. What’s unexpected is how Unity loads textures from external resources, be it web or device storage. The most common scenario is this: (code ahead)

IEnumerator LoadImage() {

  WWW loader = new WWW("http://example.com/image.jpg");
  yield return loader;
  renderer.material.mainTexture = new Texture2D(256,256);
  loader.LoadImageIntoTexture(renderer.material.mainTexture);

}

That’s it, texture loaded and displayed on your mesh. This is a most standard coroutine you can find on the web, in the official docs – anywhere. What this piece of code doesn’t have is a single line that prevents memory leaking if you run this operation over and over again. You see, WWW.texture doesn’t get killed along with WWW instance – it persists in memory, taking up space, even if you don’t have references to it anymore. So, after you run this operation twice, you don’t have references to the first loaded texture and no means to unload it. That’s why this coroutine should look like this:

IEnumerator LoadImage() {

  WWW loader = new WWW("http://example.com/image.jpg");
  yield return loader;

  if (renderer.material.mainTexture != null)
    Destroy(renderer.material.mainTexture);

  renderer.material.mainTexture = new Texture2D(256,256);
  loader.LoadImageIntoTexture(renderer.material.mainTexture);

}

Happy now. Although this doesn’t solve huge memory overhead for NPOT textures.

Advertisements

Tags:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: