Addressables GetDownloadSizeAsync returning wrong size

I am migrating my project from Unity 2022.3.28f1 to Unity 6.0.36f1.
migrating Addressable 1.22.2 to 2.3.16

A wrong calculation started to occur in the function Addressables.GetDownloadSizeAsync

I use this function to get the download size of label “DownloadContent”

Using Old Unity and Old Addressables

  • Addressables.GetDownloadSizeAsync("DownloadContent"): this return 96586979 bytes
  • Addressables.DownloadDependenciesAsync(key): this return 96586979 bytes
    • var download = mDownloadHandle.GetDownloadStatus();
    • download.TotalBytes == 96586979 bytes

Using New Unity and New Addressables

  • Addressables.GetDownloadSizeAsync("DownloadContent"): this return 399943181 bytes
  • Addressables.DownloadDependenciesAsync(key): this return 96586979 bytes
    • var download = mDownloadHandle.GetDownloadStatus();
    • download.TotalBytes == 96586979 bytes

The new return from GetDownloadSizeAsync is 399943181 bytes instead of 96586979 bytes

My code in two versions is thus:

public async Task<DownloadSizeResponse> GetDownloadSize(string key)
{
    mDownloadSizeHandle = Addressables.GetDownloadSizeAsync(key);
    
    await mDownloadSizeHandle.Task;

    return new DownloadSizeResponse()
    {
        Size = mDownloadSizeHandle.Result,
        Success = mDownloadSizeHandle.Status == AsyncOperationStatus.Succeeded
    };
}

public async Task<bool> DownloadAssetAsync(string key, System.Action<float, long, long> progressCallBack)
{
    ReleaseDownloadHandle();

    mDownloadHandle = Addressables.DownloadDependenciesAsync(key);

    int step = 0;
    int stepLimit = 2;
    long oldDownloadBytes = 0;

    while (mDownloadHandle.Status == AsyncOperationStatus.None && mDownloadHandle.IsValid())
    {
        if (!mIsAlive)
            break;

        var download = mDownloadHandle.GetDownloadStatus();

        step++;

        if (step > stepLimit && download.DownloadedBytes != oldDownloadBytes)
        {
            progressCallBack.Invoke(download.Percent, download.DownloadedBytes, download.TotalBytes);
            step = 0;
            oldDownloadBytes = download.DownloadedBytes;
        }

        await Task.Yield();
    }

    var result = mDownloadHandle.Status == AsyncOperationStatus.Succeeded;

    ReleaseDownloadHandle();

    return result;
}

Maybe the size returned by GetDownloadSizeAsync is returning the uncompressed size of bundles.

similar issue here: Unity Issue Tracker - Addressables return incorrect Download Size when using GetDownloadSizeAsync()

A find a fix solution. The bug is in AsyncOperationHandle GetDownloadSizeAsync(IEnumerable keys) function inside AddressablesImpl.cs. To test this solution, I copy the package addressables inside Library\PackageCache and put it in folder packages to create my local package. And remove the package addressables linked via Package Manager.

At allLocations.Distinct is not remove similar entries so the same entry is computed many times.

// old code
long size = 0;
foreach (IResourceLocation location in allLocations.Distinct())
{
    var sizeData = location.Data as ILocationSizeData;
    if (sizeData != null)
        size += sizeData.ComputeSize(location, ResourceManager);
}

I try to use a for to fill a HashSet, but works in similar way of allLocations.Distinct()

The solution was to use a Dictionary using a key.

// new code
Dictionary<string, IResourceLocation> distinticSet = new Dictionary<string, IResourceLocation>();
foreach (IResourceLocation location in allLocations)
{
    if (!distinticSet.ContainsKey(location.PrimaryKey))
        distinticSet.Add(location.PrimaryKey, location);
}

long size = 0;
foreach (var entry in distinticSet)
{
    var location = entry.Value;
    var sizeData = location.Data as ILocationSizeData;
    if (sizeData != null)
        size += sizeData.ComputeSize(location, ResourceManager);
}
1 Like

How can I submit the fix to the Unity development team?

Distinct should work, but it’s possible that the elements in allLocations are each separate instances. So the “normal” comparison would fail. Though the Distinct method can actually take a IEqualityComparer which controls how the elements are compared. Since you want to compare the "PrimaryKey"s you can use a class like this:

        public class IResourceLocationComparer : IEqualityComparer<IResourceLocation>
        {
            public static readonly IResourceLocationComparer inst = new IResourceLocationComparer();
            public bool Equals(IResourceLocation x, IResourceLocation y)
            {
                return x.PrimaryKey == y.PrimaryKey;
            }

            public int GetHashCode(IResourceLocation obj)
            {
                return obj.PrimaryKey.GetHashCode();
            }
        }

and it could be used like this:

foreach (IResourceLocation location in allLocations.Distinct(IResourceLocationComparer.inst))
{
// ...

Though that’s just an assumption. I haven’t looked into the concrete implementations of the addressables.

Well, there is no direct way to suggest code changes. The best bet is to file a bug report in Unity (Help → Report a Bug). There you can explain the issue and attach the current project for them to have a look at. It’s best when you provide a minimal project that reproduces the issue. You may also add a link to this thread in the report.

1 Like

Thanks by reply. A living soul in this forum!

Your solution also fix the bug.

long size = 0;
foreach (IResourceLocation location in allLocations.Distinct(IResourceLocationComparer.inst))
{
    var sizeData = location.Data as ILocationSizeData;
    if (sizeData != null)
        size += sizeData.ComputeSize(location, ResourceManager);
}

But we have to remove the async before Equals:

public class IResourceLocationComparer : IEqualityComparer<IResourceLocation>
{
    public static readonly IResourceLocationComparer inst = new IResourceLocationComparer();
    public bool Equals(IResourceLocation x, IResourceLocation y)
    {
        return x.PrimaryKey == y.PrimaryKey;
    }

    public int GetHashCode(IResourceLocation obj)
    {
        return obj.PrimaryKey.GetHashCode();
    }
}
1 Like

Actually, I don’t quite understand where the async came from ^^. I thought I let VisualStudio auto implement the interface… strange. I’ll fix my post :slight_smile:

1 Like