Reliability of asset bundle hashes in AssetBundleManifest

I’m updating my project’s asset bundle automation pipeline, and am currently running into some issues with how Unity generates hashes for asset bundles. Specifically I’m using AssetBundleManifest.GetAssetBundleHash() and getting inconsistent hashes for the same asset bundle.

I ran some tests using one asset bundle at a specific commit of my project. What I found was:

  • Building the same asset bundle on different machines would result in a different hash reported from Unity. What’s more, running an md5 checksum on the generated files showed that the generated files were different.
  • Building the same asset bundle with a different active build target (i.e. I built an asset bundle targeted for Android once with Android as the active build target in the editor and once with Windows as the active build target) resulting in a different hash from Unity, though the md5 checkshum of both bundles was the same.

That second point (changing the active build target changes the hash, regardless of what build target the bundle uses) is proving to be especially troublesome for me. I have been using the DryRunBuild option in order to generate hashes for all of my bundles on all supported platforms in one go, which is very useful when integrating the build process into a CI server. However, I noticed today that the hashes generated by the dry run don’t actually match the hashes on the built bundles (except for whichever built target happens to be active when doing the dry run). For reference, the code I’m using for this process can be found here.

While looking into this more, I found an official Unity tutorial stating that the AssetBundleManifest hash shouldn’t even be used for versioning in the first place:

This is surprising to me, as we’ve been using this hash for a while on existing projects and had assumed everything was working fine (at least we hadn’t yet found any major issues that we traced back to the asset hashes). This also makes me question if we should even be trying to use the hash returned by GetAssetBundleHash(), or if we should be using some other method of generating a hash, such as taking the md5 checksum of the generated file.

So, to summarize:

  • Is AssetBundleManifest.GetAssetBundleHash() the “right way”, or even a reliable way, to get a hash to use for versioning/caching an asset bundle?

  • If GetAssetBundleHash() isn’t the right way to version an asset bundle, what would the recommended way be?

  • Is it intended/expected that DryRunBuild not produce correct hashes for asset bundles?

  • Is it intended/expected that the active build target would have an impact on the generated hash for an asset bundle, regardless of that build target is actually used for the bundle?

  • If it’s not expected that DryRunBuild or changing the active build target would have an impact on the hash, what other reasons should I investigate to explain why the hashes are inconsistent?

Some additional details that may be relevant:

  • The project is on Unity 2018.4.11f1.
  • Our machine that builds our game (including asset bundles) is a macOS machine.
  • We use SVN for version control (probably not relevant, but it has come up in other forum threads on the subject).

I appreciate any help anyone can offer!

1 Like

Also, is this something that is addressed by the Scriptable Build Pipeline? I know the SBP is supposed to fix various issues with asset bundle build stability, would this be one of them?

I realized that there was a chance that at least part of the inconsistency that I’m seeing could have to do with something specific to the project I’m working on, and could potentially be fixable. To provide a more thorough test, I setup some tests in a separate Unity project. The tests were run in Unity 2019.2.4f1 to also try to determine if any issues were older bugs that might have been fixed since Unity 2018.4. The source for the tests can be found on GitHub.

In order to test the build pipeline, I did the following:

  • Tested building asset bundles for two platforms: Android and Standalone Windows.
  • Tested running the build with two different active build targets: Android and iOS.
  • Tested both dry run builds and full bundle builds for each of the above.
  • Tested both the built in build pipeline and the Scriptable Build Pipeline (version 1.5.4) for each of the above.
  • Tested with three bundles: One with a prefab and custom scripts in it, one with a texture, and one with a material using a custom shader.

I’ve attached a file with the test outputs, but to summarize:

  • The hashes from dry run builds are inconsistent for asset bundles built for targets not matching the current build target.
  • Hashes from dry run builds for the current build target seem to be consistent with the hashes produced by the full build.
  • Hashes from full bundles builds seem to be consistent regardless of the active build target. This differs from my original tests, so it may be something that was fixed in Unity 2019.
  • The Scriptable Build Pipeline seems to produce consistent hashes for both dry runs and full builds, regardless of the active build target.

That last point (that the SBP produces consistent hashes) is exciting, but comes with a major caveat: When building for a non-active build target with the SBP, it will automatically change the specified build target to be active. This happens even for dry run builds. Unfortunately, this defeats the purpose of dry run builds for me, since I was using them as a way to generate the hashes for all bundles quickly, without having to switch platforms and wait for assets to import (which can be very slow in our project). It also means I can’t use that functionality from a batchmode build, since the underling functionality isn’t available in batch mode.

For my purposes, this effectively tells me that the only way to reliably get the hash for an asset bundle is to fully build it. Slightly inconvenient for my CI setup, but not unreasonable.

This does leave me with an additional question, though: When does it make sense to do a dry run build? I have only ever used it to get the resulting AssetBundleManifest, and the only information in there that can’t be gotten through AssetDatabase is the hash (as far as I can tell).

5151503–510374–bundle-hash-results.txt (2.77 KB)

Ah, I just noticed that SBP doesn’t actually support DryRunBuild. That at least partially explains the results I was seeing, and backs up my assessment that the dry run build functionality didn’t have a clear use case.

“Unfortunately, this defeats the purpose of dry run builds for me, since I was using them as a way to generate the hashes for all bundles quickly, without having to switch platforms and wait for assets to import (which can be very slow in our project).”

You could try using Unity Cache Server to speed this up. Also apparently there’s an upgraded version called Unity Accelerator now too: https://blogs.unity3d.com/2019/09/11/speed-up-your-team-with-the-unity-accelerator/

In our CI setup we simply keep a separate checkout for each to avoid these issues and preserve each platform library folder between runs. We’ll have to to see how 2019.3 fast platform switcher factors into this when we start using it in our projects.

It may be related, but when I was testing Addressables, it seems like when ever I delete the Library folder, the asset hash changed (for assets that are never changed…).
I was doing that test because we don’t commit Library folder to our git, having consistent hash was something we expected.
I read somewhere in the forum that this was bug and was fixed something like 2018.4.12? fairly recently like last December. I’ve updated my editor to 2018.4.15f1 and seems like hashes are matching up now.

FYI: The assetbundle build is different each time if I remove the Library folder

2018.4.20 building the same commit from Git gives different hashes. The project is built from scratch (without the Library folder) on the BM

A large portion of Unity’s platform-specific asset serialization is not deterministic. Building bundles from scratch will result in slightly different files every time. The only thing that can prevent this is keeping the previous run bundle, their manifests, and the library folder around, with their original timestamps too (because Unity will rebuild a bundle if any of the source assets are newer than the bundle file). It’s a pain.

1 Like