Skip to content

Commit 87d7fee

Browse files
Add AssetBundle Format documentation topic
Add Documentation/assetbundle-format.md, a technical, hands-on companion to the Unity content overview that covers the AssetBundle object (m_Container, m_PreloadTable, m_Dependencies), asset preload, and how scenes are laid out inside a bundle (paired SerializedFiles, PreloadData, and m_SceneHashes for SBP vs the BuildPlayer- naming convention for BuildPipeline.BuildAssetBundles). Linked from unity-content-format.md.
1 parent d46c6af commit 87d7fee

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# AssetBundle Format
2+
3+
This topic digs into the internal layout of AssetBundles, with a focus on the parts that are useful
4+
when inspecting an AssetBundle with UnityDataTool. It complements the higher-level
5+
[Overview of Unity Content](unity-content-format.md), which introduces SerializedFiles and Unity
6+
Archives.
7+
8+
For Unity's official reference on the container format, see
9+
[AssetBundle file format](https://docs.unity3d.com/Manual/assetbundles-file-format.html).
10+
This page builds on that information with more technical information and examples of how
11+
these data structures can be examined.
12+
13+
## AssetBundles are Unity Archives
14+
15+
An AssetBundle is a [Unity Archive](unity-content-format.md#unity-archive) with some conventions for
16+
what lives inside. The [Addressables](https://docs.unity3d.com/Manual/com.unity.addressables.html)
17+
package builds its content as AssetBundles too, so the same layout applies there.
18+
19+
An AssetBundle always contains at least one SerializedFile, and may contain auxiliary files such as
20+
`.resS` (Textures and Meshes) and `.resource` (audio/video).
21+
22+
### SerializedFile names inside a bundle
23+
24+
The names of the SerializedFiles inside a bundle are technical and hash-based. You do not need to
25+
understand them for normal use, but they show up throughout UnityDataTool output:
26+
27+
- **Regular (non-scene) bundles** contain one SerializedFile named `CAB-<hash>`, where the hash is
28+
the **MD4** hash of the AssetBundle name (not the `Hash128` / spooky hash exposed in the C# API).
29+
- **Scene bundles** name their scene files differently depending on the build pipeline:
30+
- `BuildPipeline.BuildAssetBundles` uses `BuildPlayer-<SceneName>`.
31+
- The Scriptable Build Pipeline / Addressables uses `CAB-<hash of the scene path>`.
32+
- The Multi-Process Build Pipeline (2023.1+) uses `CAB-<scene GUID>`.
33+
34+
## Inspecting a bundle with UnityDataTool
35+
36+
The [`archive`](command-archive.md) command lists or extracts the files inside a bundle, and
37+
[`dump`](command-dump.md) / [`serialized-file`](command-serialized-file.md) inspect the
38+
SerializedFiles. A typical workflow is to extract the bundle into a folder and then dump specific
39+
objects:
40+
41+
```
42+
UnityDataTool archive extract mybundle.bundle -o extracted
43+
cd extracted
44+
UnityDataTool sf objectlist CAB-<hash>
45+
UnityDataTool dump --stdout CAB-<hash> --type AssetBundle
46+
```
47+
48+
## The AssetBundle object
49+
50+
Every bundle has one **AssetBundle** object (ClassID 142). It records which assets the bundle
51+
exposes and what each of them needs preloaded. Its important fields are:
52+
53+
- `m_Container` - a map from an asset's address/path to the object it maps to, plus the
54+
`preloadIndex`/`preloadSize` range describing that asset's dependencies (see below).
55+
- `m_PreloadTable` - a flat list of `PPtr`s. Each container entry references a contiguous slice of
56+
this list.
57+
- `m_Dependencies` - the names of other bundles this bundle depends on.
58+
- `m_IsStreamedSceneAssetBundle` - true for scene bundles.
59+
- `m_SceneHashes` - for scene bundles, a map from each scene path to the SerializedFile that holds
60+
it (populated by SBP/Addressables only; see [Scenes in AssetBundles](#scenes-in-assetbundles)).
61+
62+
In a regular bundle the AssetBundle object is typically at local file id (LFID) 1. In a scene bundle
63+
it is at LFID 2, because the PreloadData object takes LFID 1.
64+
65+
### Preload for assets (non-scene bundles)
66+
67+
For a regular asset, `m_Container` names the asset and points at its main object, and
68+
`preloadIndex` + `preloadSize` select the slice of `m_PreloadTable` listing every object that must
69+
be loaded for that asset (including objects found in SerializedFiles in other dependent AssetBundles). This is how Unity knows the full set of objects to load in order to fully load an asset.
70+
71+
For example, a bundle whose single ScriptableObject references an AudioClip stored in another
72+
bundle:
73+
74+
```
75+
ID: 1 (ClassID: 142) AssetBundle
76+
m_Name (string) directaudioclipreference
77+
m_PreloadTable (vector)
78+
Array<PPtr<Object>>[4]
79+
data[0] (PPtr<Object>)
80+
m_FileID (int) 1
81+
m_PathID (SInt64) -895285400835485219
82+
data[1] (PPtr<Object>)
83+
m_FileID (int) 0
84+
m_PathID (SInt64) -602721743313719314
85+
data[2] (PPtr<Object>)
86+
m_FileID (int) 0
87+
m_PathID (SInt64) 8127315791103748070
88+
data[3] (PPtr<Object>)
89+
m_FileID (int) 2
90+
m_PathID (SInt64) -5151917721481642524
91+
m_Container (map)
92+
Array<pair>[1]
93+
data[0] (pair)
94+
first (string) assets/scriptableobjects/directaudioclipreference.asset
95+
second (AssetInfo)
96+
preloadIndex (int) 0
97+
preloadSize (int) 4
98+
asset (PPtr<Object>)
99+
m_FileID (int) 0
100+
m_PathID (SInt64) -602721743313719314
101+
m_Dependencies (vector)
102+
Array<string>[2]
103+
data[0] (string) 6
104+
data[1] (string) a
105+
m_IsStreamedSceneAssetBundle (bool) False
106+
```
107+
108+
Here the single asset `directaudioclipreference.asset` has `preloadIndex 0` and `preloadSize 4`, so
109+
its dependencies are `m_PreloadTable[0..3]`. Those `PPtr`s use `m_FileID` to say which file each
110+
object lives in: `0` is this file, and `1`/`2` are entries in the file's external reference table -
111+
in this case objects in the dependency bundles `6` and `a` listed in `m_Dependencies`.
112+
113+
> [!NOTE]
114+
> `m_Container` only records the assets that were **explicitly** added to the bundle. Assets that
115+
> are pulled in implicitly (because an explicit asset references them) appear in `m_PreloadTable`
116+
> and the dependency bundles, but are not listed as their own container entries.
117+
118+
## Scenes in AssetBundles
119+
120+
Scenes are a special case, where the component hierarchy requires a dedicated serialized file that is entirely
121+
loaded each time a scene is loaded. Unity's build pipeline emits **two SerializedFiles
122+
per scene**:
123+
124+
- `<scene>` - the scene's own contents (the GameObject/Transform hierarchy, RenderSettings,
125+
LightmapSettings, and so on).
126+
- `<scene>.sharedAssets` - the objects referenced by the scene (Materials, Meshes, MonoScripts,
127+
etc.). To avoid duplication, a scene's files may reference objects in *other* scenes' `.sharedAssets`
128+
files, but there is never an external reference **into** a scene's own contents file. Note: in some cases a scene will have no additional external references apart from the references already saved in other .sharedAssets files. So in that case there will be no sharedAssets file for that scene.
129+
130+
Extracting a simple single-scene bundle shows the pair:
131+
132+
```
133+
UnityDataTool archive list scene1.bundle
134+
```
135+
```
136+
BuildPlayer-Scene1.sharedAssets
137+
...
138+
Flags: SerializedFile
139+
140+
BuildPlayer-Scene1
141+
...
142+
Flags: SerializedFile
143+
```
144+
145+
A scene bundle contains exactly **one AssetBundle object**, in one of the `.sharedAssets` files.
146+
Its `m_Container` lists every scene in the bundle by `.unity` path, and each scene's container entry
147+
has a null `asset` PPtr (`m_FileID 0`, `m_PathID 0`) because there is no object to point at:
148+
149+
```
150+
UnityDataTool dump --stdout BuildPlayer-Scene1.sharedAssets --type AssetBundle
151+
```
152+
```
153+
ID: 2 (ClassID: 142) AssetBundle
154+
m_Name (string) scene1.bundle
155+
m_PreloadTable (vector)
156+
Array<PPtr<Object>>[0]
157+
m_Container (map)
158+
Array<pair>[1]
159+
data[0] (pair)
160+
first (string) Assets/AssetDuplication/Scene1.unity
161+
second (AssetInfo)
162+
preloadIndex (int) 0
163+
preloadSize (int) 0
164+
asset (PPtr<Object>)
165+
m_FileID (int) 0
166+
m_PathID (SInt64) 0
167+
m_IsStreamedSceneAssetBundle (bool) True
168+
m_SceneHashes (map)
169+
Array<pair>[0]
170+
```
171+
172+
Note: the layout for scenes inside an AssetBundle is basically the same as how a Player build builds scenes (except different naming convention for the serialized files). In fact BuildPipeline.BuildAssetBundles and BuildPipeline.BuildPlayer() share much of the same code for processing scenes.
173+
174+
### m_SceneHashes: mapping a scene to its SerializedFile
175+
176+
Because SBP/Addressables name their scene files `CAB-<hash>`, the file name alone does not reveal
177+
which scene it holds. The AssetBundle object records the mapping in `m_SceneHashes`, from the scene
178+
path to the scene's SerializedFile name:
179+
180+
```
181+
m_SceneHashes (map)
182+
Array<pair>[145]
183+
data[0] (pair)
184+
first (string) Assets/Scenes/Dungeons/BaneChamber/DUN_BaneChamber_DESIGN.unity
185+
second (string) CAB-256771f7b55e388852b84baa22aeb5b2
186+
```
187+
188+
`BuildPipeline.BuildAssetBundles` leaves `m_SceneHashes` **empty** (as in the single-scene example
189+
above) and instead encodes the scene name directly in the file name (`BuildPlayer-<SceneName>`).
190+
191+
### PreloadData
192+
193+
Each scene's `.sharedAssets` file contains a **PreloadData** object (ClassID 150) at LFID 1. Its
194+
`m_Assets` vector lists the `PPtr`s of everything the scene depends on; at runtime Unity walks this
195+
list to load all required objects before the scene loads.
196+
197+
```
198+
UnityDataTool dump --stdout BuildPlayer-Scene1.sharedAssets --type PreloadData
199+
```
200+
```
201+
ID: 1 (ClassID: 150) PreloadData
202+
m_Name (string)
203+
m_Assets (vector)
204+
Array<PPtr<Object>>[2]
205+
data[0] (PPtr<Object>)
206+
m_FileID (int) 1
207+
m_PathID (SInt64) 10001
208+
data[1] (PPtr<Object>)
209+
m_FileID (int) 2
210+
m_PathID (SInt64) 4900368479417156912
211+
m_Dependencies (vector)
212+
Array<string>[1]
213+
data[0] (string) imagelist.bundle
214+
```
215+
216+
As with the AssetBundle preload table, each `m_Assets` entry's `m_FileID` selects the file: `0` is
217+
this file and `1`, `2`, ... are entries in the file's external reference table (other bundles, or
218+
Unity's built-in resource files). Here `data[1]` points into another bundle (`imagelist.bundle`).
219+
220+
Because PreloadData maps out the whole dependency graph of the objects referenced from a scene, its
221+
size grows with the number of dependencies. Projects with very large or numerous hard references
222+
(for example a MonoBehaviour in a scene directly referencing thousands of prefabs) can end up with
223+
PreloadData tables containing huge numbers of entries, adding significant metadata overhead to the
224+
build. If PreloadData is contributing excessive size or load time, the usual fix is to break up
225+
large dependency graphs by loading some assets on demand (via Addressables, AssetBundles, or
226+
`Resources.Load`) instead of using direct hard references.
227+
228+
## How `analyze` represents this
229+
230+
The [`analyze`](analyzer.md) command turns the above into queryable tables:
231+
232+
- Each explicit `m_Container` asset becomes a row in `assetbundle_assets`, and its preload slice
233+
becomes rows in `preload_dependencies`.
234+
- Because a scene has no Unity object, `analyze` synthesizes a "Scene" object per scene to stand in
235+
for it, so scenes appear in `assetbundle_asset_view` and their PreloadData dependencies appear in
236+
`preload_dependencies_view`.
237+
238+
See [Analyzer](analyzer.md) for the full schema and the exact behaviour of those views.

Documentation/unity-content-format.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ AssetBundles always contain at least one SerializedFile. In the case of an Asse
2626

2727
UnityDataTools supports opening Archive files, so it can analyze AssetBundles.
2828

29+
For a more technical, hands-on look at the internals of AssetBundles - the AssetBundle object, preload tables, and how scenes are laid out inside a bundle - see [AssetBundle Format](assetbundle-format.md).
30+
2931
## Player Builds
3032

3133
A player build produces content as well as compiled code (assemblies, executables) and various configuration files. UnityDataTool only concerns itself with the content portion of that output.

0 commit comments

Comments
 (0)