Search Unity

1.15.1 AssetReference not allow LoadAssetAsync twice

Discussion in 'Addressables' started by JasonAppxplore, Aug 28, 2020.

  1. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    I am disappointed and, frankly, pretty flabbergasted at the continued lack of response here. What the heck is going on over there?! Seriously, one of the very first things I learned when starting iwth Unity >5 years ago was how to load objects/data via Resources.Load -- it just doesn't get much more "core" than that.

    Meanwhile, the current "best practices" is supposedly Addressables; but they're frankly not in a production-ready & stable state (and that's saying nothing of the failures with the architecture, release management/QA, docs, communication, etc).

    Here's a case-in-point example of just how NOT production-ready this is -- specifically due to the issue in this post -- and yet there's still no clear & convincing guidance on the issue, and the communication continues to be one-sided.

    Excerpt from my post in another thread:
    We're basically at the point where we're looking for alternatives because this is really starting to seem like a botched 'experiment' -- and we've been patient, we've asked for improvements, I've written lengthy bug reports and specifics about why/how the documentation falls short, etc... And then we wait, and we continue waiting, for improvements -- over a year now, to no avail. Instead, quite literally, a minor LTS version of the editor ends up causing the Addressables package to be forcibly upgraded, which breaks our production project. At least tell us if this coaster has gone off the rails, or give us some semblance of what the heck is going on!!!

    If Addressables is NOT the way forward, perhaps it's a small "pet project" for a small team or individual, or for whatever reason if the resources aren't going to be put into it -- PLEASE let us know that so we can jump before the ship pulls us underwater too. =|
     
  2. androshchuk-vladyslav

    androshchuk-vladyslav

    Joined:
    Dec 13, 2015
    Posts:
    127
    Nothing strange, almost year we playing with this system. Generally it has good hight level architecture, but so terrible implementation: code is unclear and bad written, with circular dependency, braking base design. We are staying on this because own good solution cost a lot of time in development...
     
    xLeo likes this.
  3. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    700
    Just a suggestion from a UI/UI perspective- Redesign the AssetReference system. Integrate the soft object referencing into the core unity editor so that it behaves just like a normal asset reference. Right now there are too many problems with it-
    • If its a sprite, the thumbnail is too small to make out what it is. Normal asset references render in a bigger icon.
    • In the asset selector dropdown, its the names of the assets can be too long and can be indistinguishable from each other. Why does it behave differently from the hard asset references? Make it bring up the big Object Selection window like you would do in a normal hard reference.
    • Dragging multiple addressable assets into a List of AssetReference doesn't work. With regular asset references, you are able to select and drag and drop multiple assets to populate a List- doesn't work with AssetReferences.
    In short- do a proper editor integration, even if you have to change editor code to do so. Not everything has to be a package.
     
  4. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I agree. Such a core resource system should be integrated in native Unity.
     
    cihad_unity, joe_nk and firstuser like this.
  5. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    517
    I agree. Unity’s asset system is difficult to work with. Addressables was supposed to be a better solution but it has many of the same flaws because it’s built on top of asset bundles. Then they added more complexities to distribute/build these bundles that don’t work when in the editor. We should not need two code paths for loading assets.

    The Unity asset system needs a nested prefab amount of work to reinvent how to do asset management in Unity. AssetRefs need to be a core part of the editor/engine. It should be as simple as adding an attribute to a serialized field. AssetRefs need to ref count assets for us. We should be able to load asset refs sync or async. Not sure why content distribution was a higher priority over having a core asset management system that doesn’t shoot its users in the foot. One of the most popular posts on this forum should not be “An easier alternative to addressables.” I hope you recognize that as the badge of shame it is.
     
    Last edited: Dec 10, 2020
    Vedran_M, andreiagmu, joe_nk and 5 others like this.
  6. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    I'm sorry to those that are feeling frustrated by the package right now. Much of this conversation has drifted into realms for which I don't really have a response, but I'll add a few quick notes to this thread.

    First, to clarify why we added an error on double-load in 1.15.1... We've always only supported one load with AssetReferences, but without validation. That's a very dangerous place to be, so we added the error in 1.15.1 as a quick safety check. Moving forward we are looking at more robust/flexible solutions, but there are quite a few parts of AssetReferences that were built around this non-ideal assumption. So it'll take a bit to figure out if/how to unroll that. I have made a note that we need to update the API docs to be more explicit until/unless we change the functionality.


    Second, as to the 2019 version forcing an upgrade, and refusing to allow a downgrade, that is surprising, and sounds like a bug in package manager. Please file a ticket with Unity for that, though there is one workaround. If you want to force any version and package manager is causing trouble, you can copy any package from Library/PackageCache into Packages directory. While in Packages, I know for sure Unity will leave it alone.


    It's definitely a real package, and will be getting continued support. I'd expect that progress to slow over the holidays, but support will continue next year.

    That being said, I realize it isn't for everyone. We are intentionally not deprecating or hiding the asset bundle API, and we intentionally keep the scriptable build pipeline package separate. I believe addressables is the right solution for most users, but realize it is not right for all.

    -Bill
     
    andreiagmu likes this.
  7. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Bill while you are here, and a bit more on topic... what is the correct way to load things twice and have ref counting work correctly? From what I understand....

    This:
    Code (CSharp):
    1.  var prefabAsset = await prefabReference.LoadAssetAsync().Task;
    2. //..later...:
    3. prefabReference.ReleaseAsset();
    Becomes this:
    Code (CSharp):
    1.  var loadOp = Addressables.LoadAssetAsync<GameObject>(prefabReference);
    2. var prefabAsset = await loadOp.Task;
    3. //...later...:
    4. Addressables.Release(loadOp);
    This works.

    Though annoyingly, C# can't infer the type from the variable, so I need to manually specify that prefabReference is a <Gameobject>, despite the fact that prefabReference is of type AssetReferenceGameObject.

    So now all of the stub classes I've created AssetReferenceXXXX are only useful for the 'rich inspector' they provide, I guess? They serve no purpose in code. I have to manually specify the type on every Addressables.LoadAssetAsync call. (ie, if later I change the prefabReference type, load line of code wont throw a compiler error. <GameObject> will still be valid, even though prefabReference's type might've changed..) This feels like the "wrong" way to do it
     
    andreiagmu and firstuser like this.
  8. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    .....
     
    DTAli and Prodigga like this.
  9. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    700
    The problem with loading an AssetReference through Addressables.LoadAssetAsync is that the Asset is null and accessing OperationHandle.Result throws an exception.

    Code (CSharp):
    1. await Addressables.LoadAssetAsync<Sprite>(m_testReference).Task;
    2. Debug.Log(m_testReference.Asset); //null
    3. Debug.Log(m_testReference.OperationHandle.Result); //exception
    4.  
    Yes, I can get the asset from the return value from AsyncOperationHandle, but I don't want to manage all those. AssetReference.Asset should just do what it says and point to the loaded asset.
     
  10. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Beyond a joke at this point.

    I love this:
    How big're these cracks my guy? You think it has anything to do with only posting 2 replies so far in Dec, 3 times in Nov? 9 times in Sep? ONCE in Aug? We need you here telling us how to use your product. There is no other appropriate communication channel.
     
    Last edited: Dec 18, 2020
  11. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    Seriously, he's 100% right. We need communication on this. We aren't asking for a detailed Q&A session, a voice chat, nor do I think we're even asking "dumb" questions here. This thread isn't even "de-railed" IMO -- we're basically all asking for the absolute minimum to be able to use what is supposed to be the integral way of loading assets in the game engine (per Unity's own documentation).

    Can you please tell us: What the "correct usage" expectation with Addressables -- specifically, are all users basically expected to be rolling their own "AssetLoader" singletons on top of Addressables, to manage/singularize the LoadAsync calls, and to handle ref-counting?

    If that IS the expectation, then what exactly is the purpose of the ref-counting within Addressables, and really the API in general?

    What we want is basically just a relatively simple file/folder-esque ability to async-load assets, in a modular way, and with some level of memory management control.

    Ideally, without having to duplicate [ie everyone having to 'roll their own'] the efforts of managing the entire asset ref/load/unload stack itself -- because once you do that, then what's the point -- that's when the most popular thread in this forum ("Simpler Alternative") becomes far more attractive, since it actually does what you'd expect from an engine's go-to method of asset loading.

    TLDR: Give us an example of how you would use this within a mid-to-large project. Is the answer a singleton "AddressableAssetManager" type class sitting on top of Addressables -- that we all need to write? Or is there something that we're blatantly missing here?

    We literally thought Addressables was that 'singleton'/mgr -- until a [stable] release of the package broke our code, and we're all still unsure what your intention is WRT usage.

    I also have to add, somewhat hesitantly, that despite encountering plenty of other 'rough' areas within Unity, the responsiveness [often bordering on rapid] from Unity employees in other areas of the forum has been quite good in our experience, even in cases where we perhaps were not thrilled with the answers we received. Simply having communication & timely responses at least enabled us to keep moving instead of being unsure and somewhat stuck. The lack of comms here really sets this package back so much. =|
     
    skullthug, andreiagmu, Weiky and 11 others like this.
  12. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    Zamaroht likes this.
  13. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    This is the worst part IMO. Happened to us today and took us 90 minutes to find this thread and refactor. Could have been a LOT worse too if we were deeper into Addressables. No excuse for the auto-upgrade to a newer version of Addressables that breaks things.
     
  14. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    It feels kind of cheap to pile on here, but all the issues brought up in this thread are emblematic of the issues with Unity as a company.

    I too am rethinking the value of the current addressables implementation. Everything was going smoothly until it wasn't which brought me here. Bummer...
     
  15. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    Yeah, I feel like I'm being cheap too, I really hate the status here -- and I get that it's holiday/vacation season right now too, etc...

    I've got 3 genuine & directly-actionable requests.
    1. Can YOU (@unity_bill) please file a bug report re: the Package being forcibly upgraded, and alert the proper team to prioritize it for LTS streams. Minor LTS version should never break a package!
      It's kind of ridiculous to ask us to file a bug report for a known & acknowledged bug, especially when you hear that it's reproducing [trivially] for multiple users [all of whom seem relatively technically inclined], and is of a level I'd call "severe" [hotfix worthy] specifically due to it being introduced in a minor version of LTS stream. [See #3 in my post here, also in Addressables forum, about why you should be filing that bug report, not me/us.

    2. Provide an answer WRT how to use Addressables in a mid-to-large project. Do we each need to write singleton managers, or what!?!
      We're quite literally "stuck" on an old version [both of Addressables & of 2019.4x LTS] because I can't pour dev resources into something, be it a workaround or a guess, if we can't make heads-or-tails of the correct & intended way to use it. It should be <=30 minutes to answer, and it would immediately allow us to move forward & save us >30 minutes [multiples really] by ensuring we don't have to rebuild this bridge multiple times.

    3. Escalate this. Can I get an engineer/engineering manager (ie preferably not sales/support/community etc.) to reach out to me via email as soon as practicable, please: Arthur@LVGameDev.com
      I'm sorry, I'm sure you're doing your best, and you may have 100+ tasks assigned, or personal stuff, vacations, etc -- I'm not trying to get anyone in trouble. I'm not asking for hand holding, either. We really just need stupid-simple answers, and we're OK hearing "bad news"/the answers not being the "ideal" -- but we're a very small team with a live/shipping production project that is being bound up by this specific package, we're not "enterprise" customers but we pay still pay a non-trivial amount of money, and we simply cannot afford to go in circles here. If we need to pay for support on this, so be it. I really, really just need to get to the point where asset loading/unloading/ref-counting [ie Addressables] is no longer a continual issue popping up for my team as a continual thorn in our side. BTW: If there's a better comms channel for this request, I'm all ears, just LMK what that channel is and I'll promptly utilize it.

    Meanwhile, we'll continue to work around issues & release updates to our live/shipping production game nearly every single business day. Throw us a holiday bone, eh. =|
     
    Qriva and travlake like this.
  16. TreyK-47

    TreyK-47

    Unity Technologies

    Joined:
    Oct 22, 2019
    Posts:
    1,820
    Hey everyone. Wanted to let you all know that we hear you, and that you aren't being ignored. I'm raising this with the team, and a more comprehensive response will be provided as soon as possible. It might not be right away, as we're moving into the holidays, but I or a member of the Addressables team will be in touch with a more thorough response.
     
    firstuser likes this.
  17. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    If you need to load it multiple times, then that is a very good model. Essentially you have three choices:
    1. (use helpers) Know your code will load a given reference once. Here you can just use the helpers as is, or do your code above.
    2. (doesn't use helpers) Set up your code to have N loads with a matching N releases. This is the code you wrote.
    3. (uses helpers) Set up your code to do 1 load or reuse the asset, then do 1 release later. What I mean is, during load, check
    prefabReference.IsValid
    and
    prefabReference.IsDone
    to see if the result is ready. If so use it (or wait if the op is valid but not done). Then, at some set point (such as level exit) do a release of all your asset references that have a valid handle.

    Your point about inferring the type is good, and we'll have to explore if there is some magic c# way to handle this. I'm not sure there is given the pieces we already have in place.

    As to the only value of typed references being the 'rich inspector', I suppose that is a benefit, but the real purpose of the typed references is edit-time enforcement of type. So someone can't accidentally assign a material to a reference expecting a prefab.

    As to supporting multiple loads with the asset reference helper methods, we've explored it, but it starts to get fairly complicated from a workflow & safety perspective. Addressables (right or wrong) was built around the idea of one operation handle per ref count. So loading twice gives you two separate handles that each need releasing. Thus the asset reference would need to keep up with both. This isn't the end of the world, but then
    AssetRefernece.OperationHandle
    becomes a list of things, perhaps. Or maybe it's just the first one? And
    AssetRefernece.Asset
    becomes a bit complicated. All handles in the list should have the same asset, so likely it just returns one. But what if one or more of the handles are no longer valid? Should it find the valid one, and ignore the others?
    And what about releasing? Under the normal Addressbles contract, every load you do gives you a handle. Which is what you can keep up with to ensure a proper number of releases. If the reference supports loading multiple times, how does your game code ensure it's released everything the proper number of times? Does
    AssetReference.Release
    decrement by one, or do a full release of all handles. If it does the full release, then we're largely back to option 3 listed above. Perhaps we could have some sort of ref-count exposed, though that starts to get a lot of our internal book-keeping exposed, and the ref count isn't always what you'd expect (such as during a load). And if we did expose ref counts of objects, that gets complicated if you had two asset references pointing to the same object.

    I'm not saying these are insurmountable problems, I'm just saying "let
    AssetRefernece.LoadAssetAsync()
    support multiple load calls" does not have a simple or quick solution.


    I should have been more clear on what bug I was referring to. There are three things going on here, and I believe two are bugs.
    1. Package version updates when unity updates. This is by design for how package manager works.
    2. This upgrade could not be downgraded. (to quote "Worse, we're seemingly unable to downgrade the package") This is a bug, but it wouldn't do much good for me to file the bug as I can't repro it. If I go to into package manager I can downgrade to any older package version. Then, through editor restarts, it persists. So without more information, I don't have much that I myself could file a bug about. So if someone is seeing it, I'd ask them to file the bug, as they'd hopefully have a repro setup. Or at least the specific editor version showing this issue.
    3. This upgrade broke your project. This is also a bug. And, similarly, this is one I can't really deal with barring a lot more detail. We specifically tested upgrading from 1.8.5 to 1.16.x (which was the last "verified" bump for 2019.4). It sounds like we missed a scenario in our testing, and would love to know what scenario that is.

    For some games, a wrapper is advised, and for some not. It depends on your use, and I'm not really sure what you're asking here. Again, as I'd said before, this thread has drifted from "1.15.1 AssetReference not allow LoadAssetAsync twice", so it's a bit hard to follow.
    I will say, the only time I recommend a wrapper is for delayed unloading. I know of several customers that build either a wrapper class, or a helper method, for `Release`. They set it up to delay some amount of time, then call `Addressables.Release`. The purpose of this is to avoid an unload followed immediately by a reload, for example on transitioning between levels. By delaying the release, they give the next level a chance to load or not.

    Beyond that case, most people don't build a wrapper.

    As to the "how to use addressables", I'd say these things:
    * The primary APIs to use are `Addressables.*`
    * The primary purpose of AssetReferences are to replace string addresses with something more stable, and perhaps typed (when setting up in the editor). If your usage of them is simple, they have helpers.
    * You likely don't need a wrapper, though a delayed-release may not be a bad idea.
    * The other case for wrappers is sync APIs, though we are working on getting those out natively soon.
    * For many mid-large projects, I'd expect a custom analyze rule (or perhaps custom build script) to deal with duplicates in a more elegant way than what we've provided. This will likely be game specific in design.
    * Memory management is complicated, and requires a lot of understanding of your data, and how it's built. For basic projects, Addressables automatic behavior should be fine, but for games pushing the edge of memory usage, an understanding of https://docs.unity3d.com/Packages/com.unity.addressables@1.16/manual/MemoryManagement.html is needed, and care needs to be given to how bundle layout is setup.



    Two additional notes:
    1. In the early part of 2021, we are planning to do an overhaul to our docs to make the onboarding process better.
    2. I apologize for those that feel ignored on these forums. We will be basically silent the rest of the year, but starting in January we'll work on ways to get all of you feeling heard.

    Hope this helps,
    Bill
     
  18. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    That was incredibly helpful, thank you. That's the majority of what (I think collectively) we needed. I'm sure it took you some time but, seriously, the context of "how to use it" is so critically important in this case -- the info in your post is incredibly valuable, and much appreciated.

    Re: Bugs, will follow-up separately or start a new thread. Ty.

    On topic, this is one part that I don't really "get" but maybe I'm missing something:
    What I don't follow: As you say, the result is going to be identical (assuming identical [path,type] tuple), so I guess I'm unclear in what case you'd ever want to have >1 OperationHandles? Shouldn't it just always de-dupe -- if there's already a valid handle, use it; if it exists but is invalid, release it & create new? I guess I just can't think of a time when you'd intentionally want to have two given that they necessarily will always resolve to the same underlying asset.

    Nonetheless, the answer for us at least (as the code is now) is going to be a singleton on top, so that we can dedupe the OperationHandles it sounds like. That's totally fine too, I guess I just assumed we were missing something obvious [and maybe we are] since the underlying asset will be the same -- I generally would have expected [or guessed] that the ref-counting was occurring on unique-string [ie address], or unique [type,string] tuple, basis -- not on the OperationHandles. Maybe there's a very good & valid reason for it too, it just wasn't what I expected from basic intuition.

    Again, thank you!! I hope this level of technical communication can continue into the new year & be more timely, too. I really can't emphasize enough how important these comms are, especially in the interim time while the docs are mediocre and/or a work-in-progress AND/OR while breaking / API-level changes are still occurring -- it becomes critically important.

    Happy holidays! :)
     
    andreiagmu likes this.
  19. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    Glad it helped. We'll definitely feed some of this confusion into our doc planning for next year.

    This comes down to the challenge of releasing. If you tell addressables to load a thing twice, the ref count is 2. And you'd then need to release it twice. Or we automatically re-use the asset upon re-load, which would mean only releasing once. Which is counter to the rest of addressables.

    To copy code from earlier in this thread, this works fine:

    Code (CSharp):
    1.  var prefabAsset = await prefabReference.LoadAssetAsync().Task;
    2. //..later...:
    3. prefabReference.ReleaseAsset();
    What doesn't work is if that
    //..later...
    includes another load, like so

    Code (CSharp):
    1.  
    2. //in one part of our code, say LevelOneSetup.cs
    3. var prefabAsset = await prefabReference.LoadAssetAsync().Task;
    4.  
    5. // then, perhaps somewhere else, far far away... OtherWeirdController.cs
    6. var anotherNeedForPrefab = await prefabReference.LoadAssetAsync().Task;
    7.  
    8.  
    9.  
    10. //done with level 1, so we do this in LevelOneSetup.cs
    11. prefabReference.ReleaseAsset();
    12. // what should happen?  do we unload the prefab? or wait for a second release?
    13.  
    14.  
    Generally, with addressables the rule is N loads need N releases. That's why you'd "want to have >1 OperationHandles". To ensure you could release >1 times.

    If you instead want to call Load N times, but Release once at the end (because you somehow know all N uses are done), you can do what I suggested earlier as "option 3" (using IsValid and IsDone to get the result if it's ready).

    Just to be clear, if had some prefab, loaded it via an AssetRef, and through it's string address, it'd have a ref count of 2, but you'd also have 2 AsyncOperationHandles. Similarly loading it three different times via string address (or other means) would give you a ref count of 3 and 3 AsyncOperationHandles.



    Signing off for the year :)
    -Bill
     
  20. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    One more thing, just to be clear.. the topic of this thread is "1.15.1 AssetReference not allow LoadAssetAsync twice". To make sure we're all on the same page, this code will work:

    Code (CSharp):
    1.  var prefabAsset = await prefabReference.LoadAssetAsync().Task;
    2. //..later...:
    3. prefabReference.ReleaseAsset();
    4.  
    5. //then again later
    6. var prefabAsset2 = await prefabReference.LoadAssetAsync().Task;
    7. //..later...:
    8. prefabReference.ReleaseAsset();
    What won't work is:

    Code (CSharp):
    1.  var prefabAsset = await prefabReference.LoadAssetAsync().Task;
    2.  
    3. //loading again before releasing...
    4. var prefabAsset2 = await prefabReference.LoadAssetAsync().Task;
    5. //..later...:
    6. prefabReference.ReleaseAsset();
    7.  
    8. prefabReference.ReleaseAsset();
     
    andreiagmu likes this.
  21. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Thanks for replying. So I just want to be super clear here. You are saying this is only a problem with the helper load/unload functions provided on the AssetReferenceX classes, right?

    I think the crux of the issue is that the API is very misleading. The AssetReferenceX.Load/Unload methods get centre stage, but actually server as nothing more than very simple and very basic examples of addressable usage.

    Realistically (with the exception of VERY simple use cases) you are expected to write your own wrapper on top of everything, not use AssetReferenceX.Load/Unload.

    I think the confusion comes from all the documentation talking about Loading, Unloading, reference counting, and mirroring your loads and unloads. It is unsurprising that we all thought that as long as we mirror our AssetReferenceX.Load/Unload calls then everything will be 'handled for us'.

    So to sum up, my take away is:
    1. AssetReferenceX provides rich inspector support (Disallows drag/drop of incorrect types)
    2. AssetReferenceX provides a basic Load/Unload example helper methods, which is not intended to be the way you actually load/unload assets due to single load/unload restriction.
    3. You are expected to write your own layer on top of this, and use
      Addressables.LoadAssetAsync method and manage your own AsyncOperationHandle's, making sure to unload them when done.
    4. Used in this way, you lose type safety in code-land, since Addressables.LoadAssetAsync is unable to infer the type of asset you are trying to load. Changing the asset type won't result in compile errors. For example, the following does not result in a compiler error, but is clearly wrong:
      Code (CSharp):
      1. Addressables.LoadAssetAsync<Material>(assetReferenceGameObject);
    Edit:

    I just want to add that this might not be a positive sounding post, but I am actually really happy to be getting some concrete answers. So overall I am glad we are talking about all this. :)
     
    Last edited: Dec 22, 2020
    dotsquid likes this.
  22. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    For the most part, this seems correct, though I'm not totally sold on the "You are expected to write your own layer on top of this" part.

    To be clear, I see a handful of use-cases...

    1. A single system has access to a AssetReference. Here, it seems likely to me that in the majority of cases, the system would know if it was in a state that needed thing X loaded, or not. Here, the helpers would be usable, with the addition of perhaps checking IsValid and IsDone on access. Basically, only one controller has access to a reference, and that controller knows "when I leave state Y, I no longer need thing X".

    2. Multiple systems have access to a shared AssetReference, and each system is independent. If each system needs to dynamically load & release at arbitrary times, then you can't use the helpers, but I'm not sure a wrapper is exactly needed either. In my assumption, if each system calls Addressables.Load* N times, and then call Release N times. Not using helpers, but I'm unsure what the wrapper is for. As a quick reminder, this system could either keep up with the N handles, or just pass in the load result N times. The biggest advantages of storing the handles is additional information (status, errors, etc) and you'll know how many releases you need to do.

    3. Multiple systems have access to a shared AssetReference, but loading & releasing may be spread across multiple systems. Here's where the wrapper may come in handy. If system A is loading a thing, then somehow system B is releasing it some time later. There are all sorts of reasons to be doing something like this, and exactly how to set it up best would be very case specific.

    Perhaps I misunderstand what you mean by "wrapper", and the systems I describe in scenario 2 fall into the "wrapper" case for you.


    Thanks, glad we could get to the bottom of it.



    One last comment coming back to the the main topic of the thread... Prior to 1.15.1, if you loaded multiple times via an asset reference, you'd almost certainly be stuck with things in memory, and likely get invalid info from things like AssetRef.IsValid or .IsDone. BUT, obviously your game ran, and you got the asset you wanted from your load calls. If you are in a situation where you don't have time to clean things up, and want to get back to the pre-1.15.1 functionality, I'd recommend copying the package into your Packages dir, and just editing the AssetRefernce.cs. If you comment out the word "else" from
    LoadAssetAsync<TObject>(), what's in the else-block will execute every time, and you'll get the old behavior.

    Now signing off for the holidays, for real.

    -Bill
     
    CDF, Arthur-LVGameDev and TreyK-47 like this.
  23. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    Bill - welcome back from your holiday break, and thanks for providing detailed responses. I'll keep this thread bookmarked for future reference on general Addressables practices.

    Back to the main topic of the thread: here is the issue we had and (I'm guessing) others did as well.

    Anyone in this situation experienced upgrading to a slightly newer Unity (2019.4) and had their project throw a bunch of errors that eventually led to this thread and an urgent re-factor. This is what I meant, and I think others in this thread meant, when we said that the "upgrade broke our project".

    I'm not sure whether "breaking" working projects in this way was a bug someone should report or intentional feature.

    Either way, I don't think it's a good practice to push new versions that break previously working projects without warning, especially when they come automatically with minor Unity updates. I get the argument you make that they were only "working" in the sense that the game was running and assets were being delivered - in the background things weren't going right.

    But I think a better approach here would have been to start with a Warning that said this usage may be causing problems behind the scenes and that in future Addressables versions there will be an Error. This would have given us time to re-factor or avoid upgrading to new Addressables versions altogether.
     
    Last edited: Dec 24, 2020
  24. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    That's fair. We try to avoid anything that would cause an immediate need for refactor, but felt in this case that the current pattern was frighteningly broken. Perhaps we overestimated how bad the state was, and how important it was to push through that fix.

    Agreed. Our intention is that no 1.x release of addressables will break a users project. At some point we'll release a 2.x that does break things, but it should be clear from the version number that this break is happening.

    Thank you for working with us, and providing feedback. The only way we can get better is by listening.
    -Bill
     
    Arthur-LVGameDev likes this.
  25. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    FWIW, the comms here basically solved our issues. 3 things worth noting very briefly:

    1. We fixed this on our project via 'vendoring' the package, directly into our PROJECTDIR/Packages dir [which works well] & then making the 2 small edits to retain the 'original' functionality. This is as per Bill's suggestion, and was definitely the easiest "quick-fix" for us [and especially since we don't do much Release()'ing].

    2. As @travlake mentioned, this really should have been a log warning [or error even] -- but it definitely should have continued to return the results as it did before. Having the return value change is basically API-breaking change, and since the package was force-upgraded via a minor 2019.4x release [IMO a release management woe], there's really no case to be made for changing its function in a force-upgrade version release [but adding deprecation/similar warnings, totally understandable].

    3. One day of prompt & rapid-fire communication allowed 1 member of my team to get us onto latest Unity & Addressables version in right around 2 hours. We had invested >4 hours prior to today, and I've invested about that same amount trying to elicit comms on the topic, etc.

      My point is: accurate technical info is crucially important, especially on core stuff like this; furthermore, it's critically important to have timely communication -- it saves everyone time, and there are no doubt countless numbers of devs who will stumble on this thread over the coming months/years, who will now have answers/understanding, too.

    Thank you @unity_bill -- Happy Holidays, Happy New Year to all! :)

    Edit -- Bill literally responded above while I was typing this post up, hehe. Nonetheless, my points stand; I really appreciate the continued communication very much, too. :)
     
    skullthug, xLeo and magmagma like this.
  26. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    Ah, one last question re: "vendoring" the package so that we can make edits to the two if statements. I had thought it worked, and it did, but I had locally-serialized data that didn't survive a reload.

    Is there a way for me to move the Addressables package from its currently location (ie, latest version of it installed via package manager) to the local 'vendored' directory (PROJECT_ROOT/Packages/) without losing references? It appears that I'm forced to move it from OS-level file browser (since Unity project browser doesn't show anything above the Assets dir), but that causes Unity to lose references to stuff (MonoScripts, configs, etc) -- which obviously causes issues.

    Am wondering if there's a simple way to move it without losing those references?

    Worst case, I figure I could maybe try literally "uninstalling" the package and then drag'n'drop'ing it from within Unity, so that the references get preserved -- though I'm not sure if that'll work either (ie will Unity let me drag'n'drop a Package into a random location within the Assets dir & still retain refs?), heh.

    This one, it might be a dumb question -- but appreciate any thoughts. =D

    Edit ~30 min later -- This is the best I can find so far, documented here under "Embedded Dependencies", requires adding & running some editor/PkgManager API code [complete looking example code provided at that link]. I'm not yet sure if the Unity references/GUIDs will be kept intact or not, but will find out pretty soon. :)

    Edit ~2hrs later -- The above code is AFAICT the only way to do it. Here's a gist that contains working code to "vendor" [aka embed] Addressables package to your local while also keeping all references intact: https://gist.github.com/ArthurD/09d8a174cc036264c83205dde93392e9
     
    Last edited: Jan 5, 2021
    davidla_unity likes this.
  27. JasonAppxplore

    JasonAppxplore

    Joined:
    Jan 14, 2015
    Posts:
    18
    Code (CSharp):
    1. if (assetReference.OperationHandle.IsValid())
    2.         {
    3.             // Do something with assetReference.OperationHandle.Convert<Sprite>().Result
    4.             Addressables.ResourceManager.Acquire(assetReference.OperationHandle);
    5.         }
    6.         else
    7.         {
    8.             assetReference.LoadAssetAsync<Sprite>().Completed += (AsyncOperationHandle<Sprite> handle) =>
    9.             {
    10.                 // Do something with handle.Result
    11.             };
    12.         }
    So is this workaround safe?
     
  28. davidla_unity

    davidla_unity

    Unity Technologies

    Joined:
    Nov 17, 2016
    Posts:
    763
    @JasonAppxplore looking at it for just a second I think that would be fine. You'll just have to be sure to keep that AssetReference around so you can release it as many times as you've called Acquire on it.

    Take that advice with a grain of salt. There are likely cases where that may cause more issues that I'm thinking right now.
     
  29. JasonAppxplore

    JasonAppxplore

    Joined:
    Jan 14, 2015
    Posts:
    18
    I see. Thank you.

    It just felt more intuitive to keep a reference of the AssetReference instead of references to multiple OperationHandles.
     
  30. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    Just came across this thread and thought I'd share my two cents with the team in the hopes that it's helpful.

    My use case:
    I'm trying to serialize portions of a tilemap in a relatively small way, so I have a Palette which is an array of AssetReferences to TileBases. This way I can convert an array of integers into TileBases by loading in the AssetReference associated with that index. So I want to load without instantiating, and I'd like to not have to do my own reference counting (a reference being a single tile using a given asset).

    First attempt:
    I was suffering under the same misunderstanding I think a lot of others were, so I tried using assetRef.LoadAssetAsync() every time I wanted to load, thinking I could a) call that as many times as I want and that it would b) reference count for me. That quickly proved not to be the case and is how I found this thread.

    Next attempt:
    So I figured I could just call Addressables.LoadAssetAsync() each time I needed an Asset, and then later when I'm unloading portions of the tilemap I could use Addressables.Release() and provide the AssetReference as the parameter. That however doesn't work because in this case to work properly, you need to provide the specific AsyncOpHandle from each call of LoadAssetAsync.

    Where I've netted out that seems to work:
    With each tile I call Addressables.LoadAssetAsync() and store the AsyncOpHandle into an array of Stacks (so that each AssetReference has a stack associated with it). Then when clear tiles I call Addressables.Release() with the parameter being an AsyncOpHandle popped from the stack associated with that tile index. (NOTE: I'm using a Stack rather than using the memory stack to just hold onto a variable with the AsyncOpHandle because I'm just naively setting and wiping large rectangles of the tilemap at once).

    So ultimately, though I'm not doing any reference counting, having to hold onto those AsyncOpHandles in a Stack to later release them feels a bit like I might as well be reference counting myself. My hope for this API was that I could do something like:
    1. Call `Addressables.LoadAssetAsync(assetRef)` n times
    2. Then call `Addressables.Release(assetRef)` n times each time I was done with one particular instance of needing that asset (rather than calling it with the AsyncOpHandle as the parameter).
    So ultimately I've got it working for me, but it definitely took a bit of toying around and it never did seem to quite work in any of the ways that I imagined would make sense.
     
    JasonAppxplore and jRocket like this.
  31. MakinStuffLookGood

    MakinStuffLookGood

    Joined:
    Nov 20, 2012
    Posts:
    21
    There's a lot of salt in this thread— the change caught me off guard as well and it took a few minutes to find this thread and figure out what had changed. The way I see this, there were three camps of people this change affected:
    1. People who were using a 1-to-1 pattern as intended. No change to their code was necessary.

    2. People who were using an N-to-1 pattern, wherein the same AssetReference was loaded from multiple times, the returned handle was not cached, and the auto cached handle was being stomped, getting the user into bad states. These users had to change their code to resolve an existing bug and the error log + invalid subsequent handles was likely necessary. A larger restructure of code would have been needed.

    3. People who were using an N-to-N pattern, by caching the returned handles, and calling Addressables.Release(handle) on them later. I think this is the salty group (and it's the group I was in), because our code was working correctly and we were releasing everything properly. Minor code change required, nothing more than an ear flick.
    Using this helper method directly proved problematic and the solution to use Addressables.LoadAssetAsync made sense considering that's how I or anyone else must have been releasing handles before. I took the opportunity to wrap async loads in an extension method, so that future changes to the API won't need fixing at each call site:

    Code (CSharp):
    1. public static AsyncOperationHandle<T> Get<T>(this AssetReference assetReference)
    2. {
    3.     return Addressables.LoadAssetAsync<T>(assetReference.RuntimeKey);
    4. }
    Coupled with a find and replace of all the naked AssetReference.LoadAssetAsync calls to my new Get and given that I was already caching/releasing handles manually, everything worked fine. I disagree with the advice that this should have been made warning level. It was a existing bug for group 2, and a bug waiting to happen for group 1. The people in group 3 found this thread, and had all the information to update to the intended API.

    There is some wild backlash here suggesting that Addressables became unsuable due to this slight API change, but that's just demonstrably false. I appreciate that the find-and-replace extension method approach I used may be more difficult for some projects (I only had about a dozen call sites of the old method across a half dozen files). That being said, this was still at most, a one line change per call to the original method. It didn't necessitate some massive refactor/rearchitecture, unless maybe you were in group 2 and potentially had leaking handles and serious bugs to fix already.

    The only thing I would have asked the Addressables team do differently is reword the error message to something like:

    Attempting to call LoadAssetAsync on an AssetReference which has already been loaded. Access the cached asset via the OperationHandle getter, or use Addressables.LoadAssetAsync to load the same AssetReference multiple times with unique handles.

    Okay that's not perfect and needs some wordsmithing, but I think it has more information and might have actually been enough for me to avoid seeking out this thread and reading all the discourse.

    TL;DR— Quick and easy fix for people who were manually caching/releasing (N-to-N), annoying but necessary for people who were doing it wrong (N-to-1), and no change for people who were using the API as intended (1-to-1). Unity good, angry users bad.
     
    sandolkakos likes this.
  32. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    The unclear API that leads you into these traps, the sparse documentation that doesn't help you figure out how you should actually use the product, the breaking changes with no clear indicator of when and what will break next, and to top it all of a development team that is barely present on the forums to help support those who are trying to make use of their product. That's why people are salty. It wasn't because of this one 'annoying' little problem that has a 'quick and easy fix wow!'. This whole thread is just a perfect example of what happens in so many other threads and the sort of frustrations we deal with so frequently with Addressables - so yeah it triggered people and people started venting.
     
    Last edited: Jul 8, 2021
  33. MakinStuffLookGood

    MakinStuffLookGood

    Joined:
    Nov 20, 2012
    Posts:
    21
    Apologies for being glib at the end of my post. I see that many users in this thread were not using the AssetReference class as intended, perhaps due to sparse documentation as you've noted (it's worth noting that the documentation for v1.15 now calls out this issue clearly). The paradigm used in Addressables of an asset source which you request an asset from— and in return it gives you a handle that you are responsible for— is not uncommon in AAA (I think I even talked to Bill at Unite when I was at NCSOFT, telling him how our team had set up our asset loading in the days before Addressables came along). So I'm at a position of privilege here and more willing to deal with sparse docs or F12'ing into the code to work some nonsense out— it was wrong of me to assume others had that same background and I shouldn't let that affect my tone.

    The pattern suggested by users in this thread is foreign to me— I think it doesn't properly acknowledge that multiple AssetReference instances could refer to the same asset, thus any counting done within the ref would be local to that ref, and releasing the count down to 0 would not necessarily mean that any assets would be unloaded. It's still completely doable though in a short amount of code. It's a more appreciable refactor to get this sort of true auto ref counting (and relies on implicit counting done by equal number of load and release calls, but whatever).

    In the interest of making this thread more useful to future readers, this pattern would look something like this:

    Code (CSharp):
    1. [Serializable]
    2. public class AutoCountedAssetRef
    3. {
    4.     [SerializeField] private AssetReference _assetReference;
    5.  
    6.     private readonly Stack<AsyncOperationHandle> _handles = new Stack<AsyncOperationHandle>();
    7.  
    8.     public int RefCount => _handles.Count;
    9.  
    10.     public Task<T> LoadAsync<T>() where T : Object
    11.     {
    12.         return LoadAsyncInternal<T>().Task.ContinueWith(obj => (T)obj.Result);
    13.     }
    14.  
    15.     public IEnumerator LoadAsyncCoroutine<T>() where T : Object
    16.     {
    17.         return LoadAsyncInternal<T>();
    18.     }
    19.  
    20.     public void Release()
    21.     {
    22.         if (_handles.Count > 0)
    23.         {
    24.             var handle = _handles.Pop();
    25.             Addressables.Release(handle);
    26.         }
    27.         else
    28.         {
    29.             Debug.LogWarning("Attempting to Release AutoCountedAssetRef, but the count is already at 0!");
    30.         }
    31.     }
    32.  
    33.     private AsyncOperationHandle LoadAsyncInternal<T>() where T : Object
    34.     {
    35.         var handle = Addressables.LoadAssetAsync<T>(_assetReference.RuntimeKey);
    36.         _handles.Push(handle);
    37.         return handle;
    38.     }
    39. }
    I exposed awaitable Tasks and yieldable IEnumerator versions of loading, but you could do whatever you want there (including bubbling up the async op, though it sounds like the goal is to completely avoid caching/tracking those manually).

    I still contest that there is a bit of "throw the baby out with the bathwater" mentality in this thread, and people should check themselves before being smarmy/sarcastic to the Unity devs. Having one's work called absurd or impractical over such a slight misgiving is not particularly professional. If I were interviewing someone and they talked about how their solution to a difficult tech problem they solved in the past included "I raged at the engine developers on a public forum because I was frustrated when integrating a change they made", the rest of the interview would not be very positive.
     
    Last edited: Jul 8, 2021
    Dahaka444 and firstuser like this.
  34. firstuser

    firstuser

    Joined:
    May 5, 2016
    Posts:
    147
    I guess there is a bit of a general annoyance with Unity that might be creeping in here which isn't just because of Addressables either. I don't think anyone has any feud with particular Unity staff or teams, but the vibe for a while now from Unity as a company has been kinda chaotic.

    Announcing things and then mismanaging the launch for 5 years will create angry users. Overly optimistic timelines, poor communication, unresponsiveness, bungled releases, lacking docs, etc means some of us probably come in to these threads more than a bit jaded and frustrated when we're just trying to meet deadlines.

    One recent example I'm still personally very salty about is the ignorant castration of the Unity Package Manager (See thread: https://forum.unity.com/threads/updates-to-our-terms-of-service-and-new-package-guidelines.999940/)

    Unity pulled the rug from under us and a huge amount of people/companies of various sizes lost months/years of man hours developing things that were made with very reasonable assumptions around what was supposed to be just a regular ol' helpful package manager.

    So when that's kinda the ongoing state of things, I can see why people can get jumpy and upset. You never know which product is going to sh*t, even when it starts as something that is a good idea which you've been waiting patiently for (like UPM) and creating things around, for free, to help the community.

    Doesn't matter if it's Addressables, UPM, Project Tiny, shader keyword limits, etc — a lot of past and future problems probably could be avoided through thinking things through better, maintaining relevant up-to-date documentation with better code examples, and more regular (and honest, non-corporate speak) communication with the community in general, so we can plan our work realistically and meet deadlines accordingly.

    Right now it's all very hit and miss. Sometimes the difference between getting seriously stuck or finishing your work is a Unity employee kind enough to take time out of their day to help you on the forum.

    And too often the issues are not niche things where premium support can be justified, it's core stuff that can be seriously blocking and it's in the wider interest of the users (and Unity) for it to be handled proactively.

    As a simple example, if you try to find the difference between the "3D" and "Mobile 3D" templates when creating a project in Unity Hub, the docs say nothing. The information "i" button in Unity Hub for the templates has a nearly cruel redundant joke of information rather than any actually helpful information. You need to hunt through the forums and reddit to even begin getting some possible insight on what the difference might be. THIS SHOULD NOT BE STANDARD.

    But that's the kind of energy we're dealing with from Unity regularly.

    Obviously software development is not easy and I don't think any of us are expecting anything close to bug-free, genius tier architected, 100% documented perfection (and I think the Addressables team is eons ahead in terms of responsiveness and accountability compared to Unity as a whole) but maybe this kinda offers some insight into why we act like jaded "angry users" time to time :)
     
    Last edited: Jul 8, 2021
    xLeo, skullthug, andreiagmu and 5 others like this.
  35. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Big +1 from me to firstuser and another big +1 to:
     
    Peter77 and firstuser like this.
  36. Dahaka444

    Dahaka444

    Joined:
    Jun 15, 2021
    Posts:
    13
    Thanks @MakinStuffLookGood, worked perfectly for me. I've modified it a little so we can infer the type :

    Code (CSharp):
    1. public static AsyncOperationHandle<T> Get<T>(this AssetReferenceT<T> assetReference) where T : Object
    2. {
    3.         return Addressables.LoadAssetAsync<T>(assetReference.RuntimeKey);
    4.     }
     
    sandolkakos likes this.
  37. Alxander_Watson

    Alxander_Watson

    Joined:
    Mar 11, 2017
    Posts:
    16
    Hey all.

    My addressable code is working fine on my pc & on my Android device.

    But when I try it out on the iOS simulator in xCode I get this error:

    MissingMethodException: Default constructor not found for type UnityEngine.ResourceManagement.ResourceManager+CompletedOperation`1[[UnityEngine.Sprite, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]

    Anyone have any ideas on a fix?

    Code (CSharp):
    1.  
    2. public static void GetSpriteFromRef(AssetReferenceSprite stickerRef, Image image)
    3.     {
    4.  
    5.         // If asset has already started being loaded into memory.
    6.         if (stickerRef.OperationHandle.IsValid())
    7.         {
    8.             // if loading is not already completed.
    9.             if (!stickerRef.OperationHandle.IsDone)
    10.             {
    11.                 stickerRef.OperationHandle.Completed += delegate (AsyncOperationHandle obj)
    12.                 {
    13.                     if (obj.IsValid() && obj.Result != null)
    14.                     {
    15.                         image.sprite = obj.Convert<Sprite>().Result;
    16.                     }
    17.                 };
    18.             }
    19.             // if loading is completed.
    20.             else
    21.             {
    22.                 image.sprite = stickerRef.OperationHandle.Convert<Sprite>().Result;
    23.             }
    24.         }
    25.         else
    26.         {
    27.             // if loading hasn't started.
    28.             stickerRef.LoadAssetAsync<Sprite>().Completed += delegate (AsyncOperationHandle<Sprite> obj)
    29.             {
    30.                 if (obj.IsValid() && obj.Result != null)
    31.                 {
    32.                     image.sprite = obj.Result;
    33.                 }
    34.             };
    35.         }
    36.     }
     
  38. Alxander_Watson

    Alxander_Watson

    Joined:
    Mar 11, 2017
    Posts:
    16
    Come to think of it. I get that error before the code is even called.

    So, maybe its an issue with how I've setup the addressables.

     
  39. Sylafrs

    Sylafrs

    Joined:
    Jun 25, 2013
    Posts:
    65
    Code (CSharp):
    1. // what should happen?  do we unload the prefab? or wait for a second release?
    Maybe provide an enum to get the developper intent ?
    (decr refs or unload all)
     
  40. malinin_unity

    malinin_unity

    Joined:
    Oct 1, 2021
    Posts:
    3
    AssetReference.Asset is antipattern actually. It is used in sync mode, but there are no guarantees that asset is already loaded. In case of shared usage there will be an refcounted outer cache, and this way is quite fragile. It is better to store handles after async operations manually, rather than rely on this field.
     
  41. Walley7

    Walley7

    Joined:
    Dec 4, 2019
    Posts:
    56
    So... what if you wanted to do the following?
    Texture texture = textureAsset.LoadAssetAsync<Texture>().WaitForCompletion();
    Sprite[] sprites = textureAsset.LoadAssetAsync<Sprite[]>().WaitForCompletion();

    I guess you use sprites[0].texture, but it feels awfully clunky.