Search Unity

AssetBundleManager feedback thread

Discussion in 'Asset Bundles' started by strich, Oct 25, 2016.

  1. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    I thought I might kick off a feedback thread here about the AssetBundleManager. We tried using it as part of our next project on the PC platform and it wasn't a good experience at all. In the end we rolled our own with some inspiration from AssetBundleManager - Namely the in-editor simulation.

    Primary issues we had:
    1. The code itself is atrocious - Basic syntax rules aren't followed such as some methods having reversed accessors and return types.
    2. TODOs and workarounds throughout.
    3. An, in our opinion, overly complex inheritance hierarchy of various resolver classes - It is surprisingly hard to reason what such a small amount of code is doing.
    4. The async loading stuff is using a different pattern to that I've seen anywhere else in the Unity codebase.
    I feel like one of the main issues is that ABs are used in such different ways based on the platform and this manager class is trying and failing to perform a one size fits all action on the problem.
     
    ItsLeeOwen likes this.
  2. Reichert

    Reichert

    Unity Technologies

    Joined:
    Jun 20, 2014
    Posts:
    63
    Thanks for the feedback. I think your criticisms of the code are fair. My team has just recently taken the responsibility of moving asset bundle development forward, and we're inheriting this manager code as part of that. From my perspective, the important things it is trying to do are 1) provide the scaffolding for managing bundle downloads, including dependencies. 2) provide a simulation mode so every asset change doesn't require a bundle rebuild. 3) provide a simple local bundle hosting server for release testing in the player and on devices.

    I think it would be fair to say that it does these things but none of them exceptionally well, and I would add that it doesn't go far enough in providing a nice high level API for addressing loadable assets from code (i.e., you still need to track bundle source and asset names independently to know how to load any asset).

    We initially started to try to fix these things within the bundle manager code, but quickly realized that there were more foundational issues that we should address first, that if done properly we would be able to eventually provide a cleaner and more robust high level API. So we're actually starting with the bundle build process itself, and completely refactoring it by pulling more of the process out into C# land, giving you more data and control at every stage of the process. We're then going to provide hooks into the existing loading systems (SceneManager and Resources) to make the utilization of asset bundles seamless from an API perspective. At that point, we have the pieces in place to properly implement a download manager, a simulation mode, a local hosting service, and more.

    Overall the goal is going to be to focus on creating building blocks with good separation of concerns, from low level to high level, with reference implementations for high level APIs that you can modify as necessary.

    -Stephen
     
    Walbert-Schmitz and strich like this.
  3. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    Sounds fantastic. Happy to prototype as beta's become available.
     
  4. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    So, I've been pretty vocal about my distain for the Asset Bundle system; you can hear some of the hell we went through by following Unity's "recommended methods" in my Unite 2015 talk, along with a healthy heaping of race conditions and bugs that caused us no end of issues. I'll do my best to summarize the design issues, as I see them, and what we've done about them for our current project.

    Design goals for a new system:
    - Asset Bundle marking and breakup should be handled automatically without user intervention. The current system suggests that users manually break up bundles by hand by naming bundles with a badly designed UI (we have over 4k asset bundles). Don't break them up enough and you blow out the mono heap, download files you don't need, etc; break them up too much and loading is slow, or in previous versions crash (not enough file handles on iOS, too many web requests, etc). Don't mark something into a bundle? Duplicate a 2k texture in memory multiple times.
    - Dependency loading should be automatic. Really, when do you ever want to load an asset bundle and NOT load its dependencies?
    - No allocations in the loading process except the payload being loaded into game memory. The example AssetBundleManager does tons of string operations, loads via WWW which goes through the mono heap, etc.
    - A "AboutToBeIncludedIntoBundle(AssetImporter ai)" function is needed to preprocess data before it goes into a bundle. The current variant scheme is a disaster, requiring you to have multiple copies of every asset just so you can change some import settings for different variants.
    - No chance of duplicating memory or downloading assets you don't need.
    - More 'just works', less 'do it yourself' - there are only so many ways we actually want to load things, we don't need all of this exposed for customization.
    - Battle tested code. I mean, seriously, 5.0 -> 5.3 was a freaking disaster of bugs, race conditions, and undocumented gotchas.
    - Be aligned with the realities of how this stuff is used, in regards to CDN's and such.

    Note that all but one of these are achieved via the regular build system.

    I have spent more time wrangling Asset Bundle issues than any other part of our last project. It is by far the biggest time suck, and biggest failure point in the build process. To help resolve this, I spent a month rewriting everything, like so:

    - We only mark the things we plan to load from code into bundles.
    - We troll the dependency graph ourselves and figure out a reasonably optimal layout of bundles. All large assets over a certain file size are put into their own bundles, which are automatically named. Of what remains, any unshared asset is allowed to be sucked up into a parent bundle. Shared assets are placed into their own bundles. Finally, the layout of all bundles is examined, and small bundles get combined.
    - We no longer use bundle variants to deliver different versions of each asset. Rather, we produce an entirely new manifest for each variant level. Before each build, we go through all the assets, check them out of P4, adjust their import settings, build bundles, then revert the changes in P4. Unity's P4 integration was so bad at this (checking out files it wasn't supposed to, causing errors on future builds) that we wrote our own. Artists can mark folder or files to override the settings for each variant level.
    - We've written our own Asset Bundle Manager without much of the overhead, allocations, etc. It supports all the same features, plus some new ones, like being able to easily test on device without a server (checks the streaming asset folder first), force variant levels, etc. It has various gates in it to do things like limit the number of files loaded simultaneously, force non-async loading of assets since this crashes Unity on some platforms, track mono-heap high water marks due to large bundles, etc..

    How I think the system should really work:
    No one cares about hard drive space on a CDN. A 'fat' build could be done, which just packages up your whole project and you copy it to the CDN. If I want to load a prefab, I just call load on "Assets/MyPrefabs/thing.prefab" and it loads it- all of it. It finds the dependencies automatically and makes sure they're all in memory, like loading without asset bundles does. The whole thing could exist as a glob, like a torrent stream, where blocks get served based on what you need to load/update/etc. Blocks could reference a hash for comparison (do I need to update this, or is mine fine?). No managing asset bundle names, no worrying about breaking up things in specific ways, duplicating memory. Just a filestream of chunks which are downloaded on demand.
    Alternatively, if you are packaging these up to load from a device where hard drive space does matter, you could go to a system where you only mark the root objects you want to load. This would produce a "thin" build, which only has those objects and dependency data included into the build. Otherwise it would be the same; grab the chunks you need when you need them and load stuff out of them.
     
    StaffanEk and mh114 like this.
  5. Reichert

    Reichert

    Unity Technologies

    Joined:
    Jun 20, 2014
    Posts:
    63
    Agree, and we will have an automatic path, based on "addressable assets" - i.e., things you mark specifically to be loaded by name at runtime (you mention this as well below), and shared dependencies (including a way to calculate shared assets between scenes).

    We're working on a new high level API to replace the ABM. None of that code will likely survive as is. All of your criticisms are valid.

    We're working on a replacement for build/player settings, where you can create any number of build configurations across any platform (i.e., platform becomes a property of a build config). This will also provide per-config importer settings. So basically you would do multiple bundle builds against different configs to achieve the same thing you're talking about (of course we will consider caching of previous built bundles from other configs to optimize this). I think this will solve most but not all use cases of variants; still trying to learn what we're missing though and open to suggestions on additional solutions.

    Are you open to sharing anything you're written with our team, at least for reference? There are precious few developers that have overcome this obstacle at the kind of scale you are working at. We're committed to getting it right for your use case regardless - having a head-start wouldn't hurt. If you didn't see from my other posts, all of the new high level APIs will be published in GitHub as open source. Anyhow, I'll email you directly to discuss.

    We're open to radically different architectures for delivering content. I think we need to get it right with the current system first, as it is entirely achievable (as you've demonstrated). We'll add a few new bits to make it even nicer, including hooks into Resources.Load and SceneManager.LoadScene, ref counting against loaded bundles, and hot-rebuild/reload of bundles for fast content iteration.
     
  6. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    (responded in email, but here also)

    A lot of the issues with the current system can be abstracted away by tooling and all around better APIs. That said, there's a limit to how far you can push the current system in term of usability, especially for smaller developers. As a developer, I think all the requirements are:

    - I want to treat it as an external file system; the API should look just like the regular async loading APIs.
    - I want my files as fast as possible (reduce loading times)
    - I want the minimum download possible (save bandwidth)
    - I never want duplication of memory to be possible
    - I want to be able to update or change any of the data at any time and have it just work
    - I want it to always be data that's designed for my client version (since multiple versions may overlap for some period of time due to stores, etc)
    - I want to be able to deliver different versions of different assets to different users, based on whatever (perf, platform, country, etc)

    Beyond those considerations, I ideally want to be bothered with as little detail about how that happens as possible. Set the server address or pass it into the loading function and load an asset. I suspect you can get pretty close to this with the current system, given the new config system, etc, but it likely leaves the user with the following:

    - Having to mark things into bundles (not so bad if it's just the top level objects)
    - Having to unmark things when you no longer want them bundled (cruft gets left around!).
    - Having essentially two file systems to organize and keep in your head (directories in the editor, bundle names in AssetBundles. As if getting your tree clean isn't hard enough, we now have two!)
    - having to manage client versions -> bundle mappings via CDN paths, etc.
    - having to break up or merge bundles for various reasons (loading size/mem, etc, which makes the first points worse)

    I suspect under the current scheme it's going to be very hard to automatically design bundle layouts that are ideal for download size/count/speed, not downloading data you don't need, not re-downloading data because one thing in a bundle changes, and not having millions of tiny downloads across the massively different projects built on Unity. We've done an ok job at this for our project, but there isn't really a great way to do it even with many of the various bugs fixed in the system that made it impossible before.

    This is one of the advantages of a 'torrent like' chunk stream. Chunks are of a consistent size, so you will see some of these issues (download the whole chunk when only part of it has changed), but you won't have this accidentally happen on a 5 meg download. But you can byte patch the stream if you just want to download a diff and be done, or download chunks which are needed/out of date as needed.

    Also, things like building scenes procedurally need to be able to be done in a way that doesn't cause them to hash differently between builds. For instance, our UI is built from a ton of different scenes so that people don't step on each other when working on it. Before each build, we compile these all into 3 scenes in 3 asset bundles (broken up for size issues), and the game loads these instead of 40 different scenes (for speed). Unfortunately when those scenes are merged together, all the IDs and the scene object order gets scrambled, which means that every build we prop will cause the UI to be re-downloaded even if it hasn't actually been changed.
     
    Freaking-Pingo likes this.
  7. marcus-qiiwi

    marcus-qiiwi

    Joined:
    Jul 29, 2013
    Posts:
    22
    Experience the same issues as posted above. We're also customizing it manually at this time.

    Do you have any ETA on when this "new version" of the Asset Bundle Manager will be ready?
     
  8. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    I would like to have an option of rebuilding just a single selected AssetBundle.
    I have a setup with a main scene and a number of scenes in AssetBundles.
    I do not need to rebuild all the scenes, after having reedited just one, no matter that maybe some material shared in these scene got changed.
    I am missing the previous AssetBundle system, where I could select one or a number of assets in Editor and save them to AssetBundles.
     
  9. Reichert

    Reichert

    Unity Technologies

    Joined:
    Jun 20, 2014
    Posts:
    63
    This will be possible in the new asset bundle build API.
     
  10. Reichert

    Reichert

    Unity Technologies

    Joined:
    Jun 20, 2014
    Posts:
    63
    We're shooting for a version that works against a preview build of Unity Q1 next year, and supported in a full release by next fall/Q3.
     
  11. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    That would be great.
    And - among other basic features - i opt for loading progress, which is missing.
     
  12. Stormy102

    Stormy102

    Joined:
    Jan 17, 2014
    Posts:
    495
    I want to throw my own opinions out here. We are looking into moving into Asset Bundles and the API itself is a minefield. Personally I feel that the entire HLAPI should be the AssetBundleManager, but much more optimised.
    I also am seeing lots of emphasis on mobile downloading. We plan to ship on all standalones with asset bundles; I spent many hours into the night stripping out the downloading code from the AssetBundleManager. I feel that the new API should have automatic duplication checking and downloading. For example, LoadFromFile and LoadFromURL functions that both perform similar functions. I would like there to be a clear divide between downloading via a web request and loading from the local drive. Hope this isn't asking too much but I already like some of the upcoming features mentioned in the Q&A session. Oh and remind me to buy yourself and @hiroki-unity a beer for his excellent work on pushing out AGBT 1.2 :)
     
  13. AFrisby

    AFrisby

    Joined:
    Apr 14, 2010
    Posts:
    223
    I have one big request: we've been using Unity Asset Bundles since 2.X, the 5.0 upgrade removed a key feature we were using --- not having Collect Dependencies forced on.

    It'd be great if we could force that off and only include the assets we specify; there are cases when we do know something is being loaded from another dependency we loaded earlier; and our build process is procedural (i.e. we generate the bundle manifests on-the-fly rather than mark them in the editor; and we've got a good reason™ for doing that.)

    I'd actually love to have a "Func<bool> ShouldIncludeInBundle(Object asset);" parameter on the BuildAssetBundles variant that uses "AssetBundleBuild[]" that would fire a callback on if something should be included in the output; since that is going to be more efficient than AssetDatabase.GetDependencies - which may include unrelated and unwanted items.

    ALSO: I filed a bug on this, but as of Unity 5.4/5.5 (not sure which), building bundles via that same method which don't include a scene now force script recompilation; whereas prior they did not (albeit via the old BuildAssetBundles(Object[] assets method which was depreciated in 5.0)