MEMORY OPTIMIZATION FOR IOS APPS DEVELOPED IN UNITY

这个必须转。希望大家都看看啊。。


This technical entry will discuss how we optimize the memory footprint of our Unity-based Augmented Reality apps for iOS. We frequently use large models (500k+ vertices) exported from CAD programs like SolidWorks, and it can be tricky to avoid memory related crashes on the older iOS platforms. This is becoming less of an issue with more recent hardware, but we're still supporting the iPhone 4 and iPad 2, which both only have 512 mb of memory.

MEASURING MEMORY USE IN IOS AND XCODE

One of the hardest parts of starting to debug memory related crashes in iOS is figuring out where to look to measure them. We typically use Instruments from XCode (XCode top menu --> Open Developer Tool --> Instruments) to monitor a real hardware device running in debug mode. But within Instruments, there are a million different measures of memory usage, and every one of them seems to differ from all the others. You've got real memory, virtual memory, physical memory active, physical memory free, physical memory wired, physical memory used, leaks, allocations and back in XCode 5 itself, there's a tab called "Debug Naviator" that shows memory, CPU and FPS.

Running one of our apps, here's a collection of measurements:
Picture
Running Instruments "Allocations"
Live bytes: 201mb
Overal bytes: 547mb

Picture
Picture
Running Instruments "Activity Monitor"
from the trace highlights pie graph,  Real memory: 240 mb (and after clicking on the "Activity Monitor" row and sorting by real memory)

Picture
Picture
virtual memory: 254 mb

If you click on the timeline, it tells you details on the 4 physical memories:
Physical Memory Free: 35mb
Physical Memory Active: 183mb
Physical Memory Wired: 250mb
Physical Memory Used: 538mb
Picture
And finally, from the "Debug Navigator in XCode 5"
"Memory Utilized": 216mb
Picture
XCode Debug Navigator shows Memory and FPS
Overall, we have 9 different measures of memory usage. Which one matters? According to the  Instruments documentation, the physical memories are all non-process specific, relating to the OS and other apps. Even the free memory available is not a good indicator; in the chart above, it says 35 mb are free, but our app has been hundreds of mb larger and has still run fine. 

In the end, we settled on using the "Real Memory" measure from the Instruments Activity Monitor for the specific process. You might also be able to use the memory gauge from the Debug Navigator within XCode, but this always reported lower memory use. I believe they key is watching one of them and seeing when you get memory warnings.

HOW MUCH MEMORY IS AVAILABLE ON IOS DEVICES?

According to the  Session 242 - iOS App Performance: Memory provided by Apple, the new iPad has a hard limit of 650 mb, but for the iPad 2 and iPhone 4, memory warnings start popping up around 170 mb (real memory in Instruments Activity Monitor for our specific process). We've been able to successfully run apps in the 220 mb range, but there's no hard limit. In the above Apple video, they say that allocating memory too quickly can cause a crash since it doesn't give the OS time to free memory from other apps, so memory availability is not only a function of raw numbers, but also how you ask for memory. We had crashes happening while trying to unload and then immediately load a new model. By putting a delay between the two events, the crashes disappeared.

In the Unity forums when people have complained about crashes, I've seen Unity ask for example projects that run at under 200 mb. The real answer is that you have to test on real devices to be sure.

FINDING MEMORY USE CULPRITS IN UNITY

Unfortunately, there's no way that we know of to see in Unity how much any given object or chunk of code will use. We typically would do A-B trials with and without objects to see what impact they have on the Real memory use. Here's a list of what we've learned:

  • All objects in the View Hierarchy use memory
  • Objects in the Project don't use memory unless they're in the Hierarchy
  • Multiple objects referencing the same initial mesh don't duplicate memory use, they just slow down the frame rate. This has nothing to do with prefabs, only the source meshes.
  • Textures are extraordinarily expensive, especially full screen images

Turning an object in the view hierarchy inactive with SetActive(false) does not free that memory. I've noticed that sometimes an object that's inactive at the start of the app will only consume memory when it becomes active, but once that memory gets used, it won't go away just by setting it inactive again.

One tool we typically use to start hunting down large memory objects is the editor log after doing a build. To open it, right click on the console tab and select "Open Editor Log."
Picture
After a build, search through the log for the size-sorted list of "Used Assets" (use Ctrl-F to find it, the log is huge). You'll see the assets that actually get compiled into the build sorted by size. Note this is not memory use, but rather the size of the object in the binary. But it's a good place to find memory hogs that you didn't even know were in your scene.
Picture
Unity editor log after build, Used Assets listed by size
In my log you can see that I have some fairly large fbx models that are taking up most of the space, but right after those are several images. A single full screen RGBA (rgb+alpha) png that covers a retina iPad display will use 32 megs. Just a few of those could easily kill your available memory if you're not freeing the space (more on that later).

COMPRESSING MODELS REDUCES BINARY SIZE (NOT IN-GAME MEMORY!)

One thing you may have noticed in the Inspector when you click on an imported model is an option labeled "Mesh Compression." 
Picture
Mesh compression in imported models in Unity
This has zero impact on the in-game memory, but it can significantly reduce the the binary size. In our very limited testing with engineering style models, all the compression levels above "off" look the same. We either have this off or at high. If the artifacts were acceptable, we could set it to high, but if they were not, we set it to off. We never noticed differences between low and high compression.

IMAGE COMPRESSION (IOS ONLY SUPPORT PVRCT, NO EFFECTIVE PVRTC COMPRESSION WITHIN UNITY)

If you select a png texture in the project view while your platform is set for iOS, the preview of the image shows some text below with the compression and size. In our experience, this text is not correct. Sometimes it shows the wrong compression or one that's not actually being used, and usually a size that's not right. Bear in mind that iOS only supports PVR compression, so any sizes corresponding to other compression methods are not relevant.
Picture
While working on an app that displayed catalog pages like a book, we needed to use large full-screen textures. The internal PVR compression never seemed to work or it looked horrible, so we ended up using a tool called  PVRTexToolGUI. It seems not all PVR compression tools are created equal, as most comments on forums recommend avoiding PVRCT compression on GUI graphics, but this tool worked ok for us. We were able to compress large 2048x2048 pixel images with text and graphics into 2 mb images (binary and in-game memory use) that looked good on retina displays. Be aware that there are many options in the PVRTexToolGUI program, and  only the following worked for us.

  1. Make sure the image is square and a power of 2 in both dimensions (512, 1024, 2048, etc).
  2. Open the image in PVRTexToolGUI and select the encode button.
Picture
PVRCT Compression using PVRTextTool
3. Select the PVRTC 4bpp (4 bits per pixel) from OpenGL ES 2, very high for quality, and vertical flip at the bottom. No other options worked for us.
Picture
PVRCT compression settings, PVRCT 4bpp, OpenGL ES 2
4. Inspect the resulting image for defects.
5. Important: to save the PVR, select File --> Save as Legacy. Saving any other way didn't work with Unity for us.

DYNAMICALLY LOADING AND UNLOADING ASSETS IN UNITY

By putting assets in the Assets/Resources folder in the project hierarchy, you can dynamically load and unload them from memory. Unfortunately, this completely freezes the app during loading, so we display a loading graphic before initiating the load. There are also a few gotchas to successfully get this to work. Make sure to watch the real memory in Instruments to make sure memory is really being freed.

To load an object: (c#) (plumbing is a prefab in the Resources folder).

PlumbingModel = (GameObject)Instantiate(Resources.Load("plumbing", typeof(GameObject)));

To then free the object:

GameObject.Destroy(PlumbingModel);

PlumbingModel = null; // not sure if this is necessary, but we check elsewhere in the code for null so we wanted to make sure.

Resources.UnloadUnusedAssets(); // IMPORTANT: memory will not be freed without this line.

When you load models using this method, they show up at the top of the hierarchy. If you need to make one of the loaded models a child of something else, do:

MyLoadedObject.transform.parent = MyParentObject.transform;


Be aware that this could change the position and scale. When first creating the prefab, be sure to drag the object out to the highest level of the hierarchy to make sure its position remains where you want it when the model gets loaded.

You may want to use UnitySendMessage to free up memory when iOS sends a memory warning to your App via applicationDidReceiveMemoryWarning as indicated  here.

TIPS

Being forced to run tests on real iOS hardware dramatically slows down the iteration process. We needed to test on android and iOS and rapidly make changes, and switching platforms was a horrible drag on the process, taking over 10 minutes every time we swtiched to iOS. I highly recommend  Fast Platform Switch, which makes a cache of the platform files so switching takes less than a minute.

Use Unity's Profiler (Window menu --> Profiler) to tackle memory and performance issues with large models. Unity recommends keeping the vertex count below a few hundred k on mobile platforms, and several million on desktops.
Picture
Unity Profiler showing number of vertices
We typically use several cameras and lights, which can easily double the vert count if you forget to turn the cameras and lights off when you don't really need them. This helped us go from 9 FPS to 20 FPS!

Unity has a lot of  CPU and graphics optimization tips on their website.

The "Debug Navigator" in XCode is a good place to watch the FPS, memory and CPU, although I have no idea what that memory gauge is showing, as it's always 30% less than the real memory reported in Instruments.
Picture
Xcode Debug Navigator
name="f32911414" width="90px" height="1000px" frameborder="0" allowtransparency="true" scrolling="no" title="fb:like Facebook Social Plugin" src="http://www.facebook.com/plugins/like.php?action=like&app_id=190291501407&channel=http%3A%2F%2Fstatic.ak.facebook.com%2Fconnect%2Fxd_arbiter%2FDU1Ia251o0y.js%3Fversion%3D41%23cb%3Df335d8edbc%26domain%3Dwww.equipcodes.com%26origin%3Dhttp%253A%252F%252Fwww.equipcodes.com%252Ff13525e9a8%26relation%3Dparent.parent&href=http%3A%2F%2Fwww.equipcodes.com%2F1%2Fpost%2F2014%2F02%2Fmemory-optimization-for-ios-apps-developed-in-unity.html&layout=button_count&locale=en_US&sdk=joey&share=false&show_faces=false&width=90" style="position: absolute; border-style: none; visibility: visible; width: 76px; height: 20px;">
id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" src="http://platform.twitter.com/widgets/tweet_button.b68aed79dd9ad79554bcd8c9141c94c8.en.html#_=1422484292009&count=horizontal&dnt=false&id=twitter-widget-0&lang=en&original_referer=http%3A%2F%2Fwww.equipcodes.com%2Fblog%2Fmemory-optimization-for-ios-apps-developed-in-unity&size=m&text=Memory%20Optimization%20for%20iOS%20Apps%20developed%20in%20Unity%20-%20EquipCodes&url=http%3A%2F%2Fwww.equipcodes.com%2F1%2Fpost%2F2014%2F02%2Fmemory-optimization-for-ios-apps-developed-in-unity.html" class="twitter-share-button twitter-tweet-button twitter-share-button twitter-count-horizontal" title="Twitter Tweet Button" data-twttr-rendered="true" style="width: 107px; height: 20px;">
 



Your comment will be posted after it is approved.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值