I’m working on a small tool on top of the Asset bundle browser to estimate how much size in RAM a bundle will take so I can put a hard limit / validation before a bundle is submitted to version control by artists.
My approach is to:
-
Load a bundle using bundle.LoadAllAssets()
-
Make a serialized object from bundle
-
Access the serialized property “m_PreloadTable”
-
loop through every objectReferenceValue
-
Get it Type then size using Profiler.GetRuntimeMemorySizeLong(object)
-
Adding that to a Dictionnary<Type, float>
Here is the full Editor Class if you want to do Tests:
[CustomEditor(typeof(LoadBundle))]
public class LoadBundleEditor: Editor
{
public string BundleName;
private AssetBundle _bundle;
private GameObject _prefab;
private Dictionary<Type, float> TotalMemory = new Dictionary<Type, float>();
List<string> PreloadedObjects = new List<string>();
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Load Bundle"))
{
LoadBundle loader = (LoadBundle)target;
EditorCoroutineUtility.StartCoroutine(LoadPrefabRequest(loader.BundleName + ".bundle"), this);
}
}
public IEnumerator LoadPrefabRequest(string bundleStr)
{
if (_bundle != null)
{
_bundle.Unload(true);
yield return null;
}
TotalMemory.Clear();
PreloadedObjects.Clear();
AssetBundle bundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, bundleStr));
bundle.LoadAllAssets();
_bundle = bundle;
yield return null;
float total = 0;
SerializedObject sBundle = new SerializedObject(bundle);
SerializedProperty allAssets = sBundle.FindProperty("m_PreloadTable");
foreach (SerializedProperty asset in allAssets)
{
if (asset.objectReferenceValue != null)
{
float size = GetAssetSize(asset, asset.objectReferenceValue.GetType());
if (PreloadedObjects.Contains(asset.objectReferenceValue.name + size))
continue;
if (TotalMemory.ContainsKey(asset.objectReferenceValue.GetType()))
{
TotalMemory[asset.objectReferenceValue.GetType()] += size;
}
else
{
TotalMemory.Add(asset.objectReferenceValue.GetType(), size);
}
PreloadedObjects.Add(asset.objectReferenceValue.name + size);
}
}
foreach (KeyValuePair<Type, float> key in TotalMemory)
{
total += key.Value;
Debug.Log(key.Key + "<color=blue> memory should be: </color>" + key.Value / 1048576f);
}
Debug.Log("<color=green> Total memory should be: </color>" + (total / 1048576f));
}
float GetAssetSize(SerializedProperty s, Type t)
{
float mem = Profiler.GetRuntimeMemorySizeLong(s.objectReferenceValue);
return mem;
}
public float GetTextureSize(SerializedProperty s)
{
float mem = Profiler.GetRuntimeMemorySizeLong((Texture2D)s.objectReferenceValue);
Debug.Log("<color=cyan> Texture size of " + s.objectReferenceValue.name + " is </color>" + mem);
return mem;
}
}
In the end I show the memory of each Type as well as the Total Memory of that bundle. just like in the Profiler if you take a snapshot.
I do a comparison by loading an AssetBundle in an empty APK project in Android and taking a Memory snapshot from the profiler. then I compare that data with my Editor tool by loading the same bundle file (and also checking an editor memory snapshot to see file sizes if they correspond).
And the values are different, In the Editor it feels like if i’m loading a Desktop bundle and not an Android one. Although the asset files are android ones, and the test unity project is set to Android. I tested on some UI bundles that don’t have overrides for android and it works perfectly.
Do you have any idea how Unity behaves under the hood concerning assetbundles loading in the editor ?
Thanks.