Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Asmdef with subfolders?

Discussion in 'Scripting' started by SolarFalcon, Jun 13, 2023.

  1. SolarFalcon

    SolarFalcon

    Joined:
    Nov 28, 2015
    Posts:
    170
    There is a thread about this that is from 2021 or something, I am having the same issue. Is this not possible? I want to cut down compile times as I have my own code then a few heavy assets I've added. I need to be able to put an asmdef in the root folder of each asset and have all the scripts therein included. Is this not possible?
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,396
    Might help to link to the original thread?

    Assembly Definitions apply to the scripts of the folder they're in, and any sub-folders. If you make another assembly definition asset in one of these sub folders, scripts in that folder and its sub-folders will be compiled into that new assembly.
     
    Ryiah, Bunny83 and Yuchen_Chang like this.
  3. SolarFalcon

    SolarFalcon

    Joined:
    Nov 28, 2015
    Posts:
    170
    What would cause this to not work? I have scripts in subfolders that throw a ton of errors about namespaces or type names not being found. This doesn't work for any of my folders either, I'm using multiple asmdefs throughout my project.

    The post in question didn't have an answer, hence why I'm asking.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,396
    Probably because you didn't reference the other assembly definitions each assembly requires: https://docs.unity3d.com/Manual/Scr...finitionFiles.html#reference-another-assembly
     
  5. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    535
    An asmdef does not reference other asmdefs by default, and they cannot reference the default predefined CSharp assembly. They will automatically reference precompiled dll assemblies that are marked auto reference, but not another asmdef. If an asmdef is marked auto reference, it is only automatically referenced by the predefined, default CSharp assembly. You must specify if an asmdef should reference another asmdef. (And just for clarity a precompiled dll assembly only references the other assemblies it needed to reference when it was originally compiled, including being pointed to things like UnityEngine.Core.dll, and it assumes that any such dependencies will be available when Unity compiles your project.)
     
    Last edited: Jun 13, 2023
    Ryiah, Bunny83, orionsyndrome and 2 others like this.
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,594
    Ditto on everything Spiney and CodeRonnie said. You need to setup your assembly references if they rely on code found in another assembly.

    Be careful though, you can't have circular references. This is why what CodeRonnie said in regards to how an assembly can't reference the default CSharp assembly... well that's because the default CSharp assembly reference all these asmdef assemblies you've created. And you can't have a circular relationship.

    So say you have asmdef A that is your UI, and you have asmdef B that is your Level Controllers. Welp... you need to pick now. Either A can ref B, or B can ref A. Not both.

    Really you should be thinking of assemblies as a whole new layer of encapsulation (if you're unfamiliar with encapsulation, it's a core principal of oop). Assemblies don't exist specifically to speed up compilation, that's just a benefit. Assemblies exist to organize your code.
     
    SisusCo, Ryiah, CodeRonnie and 2 others like this.
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,396
    Agree with lordofduct above. Namely, your code needs to be organised with assembly definitions in mind from the outset. It's not something you can throw in as a afterthought, as your code structure is likely to not be compatible with the one-way nature of assembly definitions.
     
    SisusCo likes this.
  8. SolarFalcon

    SolarFalcon

    Joined:
    Nov 28, 2015
    Posts:
    170
    Ok thanks for the basic info about how asmdef's work.

    The issue I'm having is that I have a folder, lets call it Main Folder, inside main folder there are other subdirectories. Sub Folder A, Sub Folder B, etc.

    The asmdef is in Main Folder and doesn't require any other asmdefs except stuff like UnityEngine.UI etc. Those are linked as they are supposed to be.

    The problem is Scripts in Sub Folder A and B aren't able to use the stuff under the same namespace in the asmdef that is in Main Folder. From what I understand, sub folders are supposed to be in the main folder asmdef. Is this not the case, and if so what would cause this? Because as I said, the code in sub folders A and B are in the same namespace as other scripts in the Main Folder. Do I have to have an asmdef in EACH subfolder for stuff that is in the same namespace? That doesn't make sense at all if so. This is my project's namespace I'm talking about here, there isn't anything else being used in these sub folders. I moved them out of the main folder to for organization.
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,594
    The subfolders is not your problem, I can assure you of that. I do the subfolder thing all the time. Case in point you can see here:
    upload_2023-6-13_21-15-13.png

    There are various asmdef's all with no source files adjacent to them, only source files in subfolders of them.

    Your problems are going to be rooted in those errors, without knowing what those errors say specifically, I can't tell you EXACTLY what you need to do. But whatever it is, since you say it is "namespace" or "type name"s not being found. It's because your asmdef's aren't referencing the necessary assemblies.

    So just ignore the whole subfolder thing for now.

    Lets say you have this "Main Folder". Do you have any source files that are located NOT as a child (subfolder or not) of "Main Folder"? And if so, are they referenced by the source files inside "Main Folder"? Note that this could include references to types found in 3rd party things like packages you've installed. Think like TextMeshPro or UniTask.

    If you look at the errors in question, they'll say which types and/or namespaces can't be found. The assemblies, 3rd party packages, etc from which those types and/or namespaces are from are the ones that your asmdef don't have references to and need references.

    For example in my above image that 'DataBinding' library has conditional use of TextMeshPro, Addressables, and UniTask and so therefore I had to setup the asmdef like so:
    upload_2023-6-13_21-21-43.png

    Note - the 'version defines' at the end aren't necessary. Just the 'Assembly Definition References'. The 'version defines' are just because this assembly has conditional reliance on tmpro/unitask/addressables. Basically the library will conditionally compile sections of its code if and only if it finds those 3rd party packages installed.
     
    SolarFalcon and CodeRonnie like this.
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,594
    On a tangent... I have to complain about something in regard to assembly definitions and this conversation about them speeding up compile times.

    I just don't agree that it does actually do that!

    OK, OK, OK... I will agree that it can speed up compilation in certain situations, BUT can also slow down compilation in other situations.

    Case in point this project above that I showed images from... that project is slow as all heck to compile any time I change something! And it's because if I edit code in say "com.spacepuppy.core", being the core assembly that many other assemblies rely on, a cascading of compiling seems to occur. Or at least that's what appears to be happening. Cause it takes a while to compile if I edit that. Longer than if I had the entirity of this spacepuppy just flat in a project.

    Of course... the upside is when I have spacepuppy imported into one of my games, I'm not generally editing spacepuppy, so it doesn't impact the compilation times of my game specifically.

    And that's what I mean... it speeds up compilation in a very specific way. It only speeds it up if, and only if, that assembly isn't being touched often and nor is the assemblies it references OR if that assembly isn't referenced by anything else. But if your assembly is both referenced by something (which is likely) and you edited it... it actually slows things down since now it has to individually compile that assembly and every assembly that references it.

    Or again, that's what it appears to be doing from how I've noticed.

    If I edit spacepuppy.core, which has lots of references to it, it's slow.
    If I edit spacepuppy.DataBinder, which has no references to it, it's fast.

    This isn't to say you shouldn't use asmdefs, and it's not to say it can't speed up compilation. It's just that I feel like whenever I see people talk about asmdefs speeding up compilation on the forums it seems like they think of it as some sort of panacea to their slow compilation woes.

    Personally speeding up compilation is NOT why I use asmdefs. As you can see in my pics above... I use them for organizing my packages.
     
  11. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,396
    So do these sub folders define their own assembly definition or not? If not, they're going to be compiled into whatever assembly is present in any of their parent folders, so I'm guessing this 'Main Folder'? If they are, they still need to reference the appropriate assembly definitions.

    You can click on your script assets to see what assembly they're compiled into.

    Yeah pretty much the same here. They reinforce good code architecture, keep my code modular, etc etc.

    The best way to speed up compilation time is just better hardware.
     
    Ryiah and lordofduct like this.
  12. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,214
    Yeah I pretty much agree with the overall sentiment about the compilation speed. I wanted to share my view early on, but lordofduct did it better, and I hoped someone would. It's really just a PR stunt. It was advertised as a holy grail of speed ups. It is not.

    I'm assuming that's like a hot point of comparison people use when comparing Unity with Unreal or Godot. Godot especially is known for very fast compilation times since it's using a custom python dialect and whatnot.

    That doesn't make it necessarily better in every thing it does, but I'm guessing they're fighting over the adopting population, because once you make a decision, it's harder to convert. And Unity PR sometimes falsely repurposes unrelated tech to build an argument. This is one of those cases.

    You know
    FAST COMPILATION TIMES -- CHECK * and then a small asterisk
     
    SisusCo, Nad_B and lordofduct like this.
  13. SolarFalcon

    SolarFalcon

    Joined:
    Nov 28, 2015
    Posts:
    170
    I'm really confused now, because I just knew none of my scripts were referencing anything that wasn't in the asmdef, but something keeps happening when I compile sometimes where there are a bunch of using statements added for crap I have never needed or even know how to use. So apparently there was something being referenced. I must have something else wrong in my project and I'm gonna go over everything again. I started with an asmdef to keep my code organized and reduce compile times. It works for reducing compile times, but organization is a bit annoying when I can't just reference one thing I need from some asset I'm using. Thanks for the help and the patience. Cause I ran out of mine.
     
  14. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    535
    If you're working in Visual Studio, it may automatically add using statements while you're working. If they are greyed out, and the file is complete, you don't actually need them. If you click the clean up code button that looks like a little brush icon at the bottom of the page it will apply styling and remove any unused using statements. Also, just be aware that nesting one asmdef inside another creates a separate assembly that doesn't automatically reference the parent.
     
    SolarFalcon likes this.
  15. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,098
    The situation was a bit different when asmdefs were introduced.

    At that point, Unity was still using an old Mono compiler that was much slower. Compilation was the major factor in iteration time and one big selling point of asmdefs was to help reduce compilation times in large projects. But Unity has switched the Roslyn compiler, which is much faster. Nowadays, compilation time is quick and it's dwarfed by domain reload time, so there's much less performance benefit in using asmdefs.

    This is also reflected in Unity's documentation, compilation time was the leading point when asmdefs were introduced, now it's just one of three points and only mentioned one time.
     
    Ryiah, spiney199 and Lurking-Ninja like this.
  16. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,214
    @Adrian
    I agree with you but we're not talking about the same thing.

    I'm not talking about documentation, documentation was always fine, i.e. it didn't exaggerate. In fact the "small asterisk / print" in my example leads directly to documentation.

    I am talking specifically about PR, which gives people an idea of a massive boost only to be disappointed. The blogs, the talks, the announcements. What would otherwise cause such disappointment when the docs in 2017 clearly state the prerequisites
    Truly they never claimed that the compilation is guaranteed to be accelerated.

    Also the first version of IL2CPP was introduced in 2015
    It wasn't in full swing yet, but predates asmdefs.
     
  17. SolarFalcon

    SolarFalcon

    Joined:
    Nov 28, 2015
    Posts:
    170
    IDK about holy grail of speedups, but it does help to speed up compile time in my project. My compile time on my crappy lappy went from 30+ to around 10-12 seconds.
     
  18. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,214
    You organize your codebase into separate assemblies so that it doesn't have to recompile entirely on every change. Technically that's not a speed up per se, but it was regardless promoted as a long awaited improver of compilation times. I'm trying to show why this particular play on semantics is disappointing to many people.
     
    SolarFalcon likes this.
  19. VRARDAJ

    VRARDAJ

    Joined:
    Jul 25, 2017
    Posts:
    87
    I haven't built any projects that use complex nested trees of asmdefs yet, so I haven't really kicked the performance tires on them, but this thread confuses me. I don't see why asmdefs would be of use for organization. I'd just continue organizing w/ namespaces within the default assembly if I thought there was not much performance value. I suppose if I wanted a white-label plugin capable of running in other .NET platforms outside of Unity, an asmdef might be useful for organization, but for any code known to only run within Unity projects, asmdefs would feel like more subfolders for my subfolders if they don't actually separate compilation the way the documentation suggests.

    For instance, say I only have two assemblies, NamespaceNameRuntime & NamespaceNameEditor, and the Editor asmdef refs the runtime asmdef but not the other way around. It sounds like we're claiming a change in a script in Runtime namespace will force reload on both. Do I have that right? That doesn't make sense to me. Why would asmdefs exist if they can't separate compilation across refs?

    RE: "dwarfed by domain reload," I thought the domain was nothing more that the total collection of assemblies Unity is aware of. If so, then "domain reload" just means all changed assemblies are reloading, yes? How can an assembly reload be dwarfed by domain reload when, AFAIK, domain reload IS assembly reload? Is domain reload doing something in addition to assembly reload that I'm not aware of?
     
    Last edited: May 25, 2024
  20. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    844
    The issue is that Unity today doesn’t have any sense of tiered or incremental domain reloading. One does not simply reload only the changed assemblies. That has the potential to change in the future once they migrate to modern .NET, using tiers of AssemblyLoadContext.
    https://forum.unity.com/threads/domain-reload-in-editor.1403320/#post-8828686
    https://forum.unity.com/threads/unity-future-net-development-status.1092205/page-51#post-9766101
    asmdefs have other uses in the meantime. Source generators / analyzers can be scoped to assemblies that reference another asmdef, entire assemblies can be conditionally included in a project instead of needing per-file conditional preprocessor directives, etc.
     
    CodeRonnie and orionsyndrome like this.
  21. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,396
    Personally I like how it allows me to keep my dependencies tight. I can ensure one system only references as many other assemblies as it needs and no more. I find it more 'physical' than namespaces, if that makes sense.

    It does make sense as since the editor assembly is referencing the runtime one, if you change the runtime one, the editor one needs to recompile to ensure its code is still all valid. Otherwise you could change code in one assembly, and not know that a bunch of code was broken in other assemblies.

    Unity recompiling scripts (based on code in assemblies and assemblies that are dependant on it) is only a tiny portion of the overall domain reload. There's a whole bunch of other stuff Unity does that takes up the majority of the time. So while using assembly definitions may reduce the script recompilation time of domain reload, it's impact is small.

    You can see how much time Unity spends on it in the editor.log. Such as this one I just did:
    Code (csharp):
    1. Asset Pipeline Refresh (id=f29ea27449094d142b10c7fb0c5326f1): Total: 2.897 seconds - Initiated by StopAssetImportingV2(ForceSynchronousImport | ForceDomainReload)
    2.     Summary:
    3.         Imports: total=1 (actual=1, local cache=0, cache server=0)
    4.         Asset DB Process Time: managed=2 ms, native=2231 ms
    5.         Asset DB Callback time: managed=40 ms, native=2 ms
    6.         Scripting: domain reloads=1, domain reload time=618 ms, compile time=1 ms, other=0 ms
    7.         Project Asset Count: scripts=1812, non-scripts=2944
    8.         Asset File Changes: new=0, changed=0, moved=0, deleted=0
    9.         Scan Filter Count: 7
    10.     InvokeCustomDependenciesCallbacks: 0.001ms
    11.     InvokePackagesCallback: 0.574ms
    12.     ApplyChangesToAssetFolders: 0.272ms
    13.     Scan: 9.824ms
    14.     OnSourceAssetsModified: 0.001ms
    15.     CategorizeAssetsWithTransientArtifact: 0.008ms
    16.     ProcessAssetsWithTransientArtifactChanges: 2.715ms
    17.     CategorizeAssets: 23.776ms
    18.     ImportOutOfDateAssets: 2177.026ms (2146.778ms without children)
    19.         ImportManagerImport: 10.840ms (0.825ms without children)
    20.             ImportInProcess: 10.000ms
    21.             UpdateCategorizedAssets: 0.015ms
    22.         CompileScripts: 1.163ms
    23.         CollectScriptTypesHashes: 14.921ms
    24.         ReloadNativeAssets: 1.619ms
    25.         UnloadImportedAssets: 0.101ms
    26.         ReloadImportedAssets: 0.000ms
    27.         EnsureUptoDateAssetsAreRegisteredWithGuidPM: 0.873ms
    28.         InitializingProgressBar: 0.020ms
    29.         PostProcessAllAssetNotificationsAddChangedAssets: 0.000ms
    30.         OnDemandSchedulerStart: 0.710ms
    31.     PostProcessAllAssets: 65.936ms
    32.     Hotreload: 1.943ms
    33.     GatherAllCurrentPrimaryArtifactRevisions: 0.388ms
    34.     UnloadStreamsBegin: 0.042ms
    35.     PersistCurrentRevisions: 0.235ms
    36.     UnloadStreamsEnd: 0.002ms
    37.     GenerateScriptTypeHashes: 4.445ms
    38.     Untracked: 614.427ms
    Note how 'CompileScripts' is a measly 1.163ms.
     
    VRARDAJ, Spy-Master and orionsyndrome like this.
  22. VRARDAJ

    VRARDAJ

    Joined:
    Jul 25, 2017
    Posts:
    87
    Yeah, my bad. I was thinking in terms of 3rd party APIs endpoints and clean deprecation schedules, but you're right. Within a repo, there's no confidence that two assemblies would only be connected with protection from modification. If one assembly invokes a method in the other that no longer exists, for example, the compiler would need to throw.

    Good to know about the log. I guess I'm still unclear though about the definition of the domain. Log says it took 618ms and compilation was only 1.6ms of that, but I don't see anything itemized that accounts for the other 616.4ms. Do we know of any docs that give more detail? Or is it considered Unity secret sauce beyond this LOD?

    Also, I had assumed (falsely it seems) that other items outside of domain reload would be segmented by use of asmdef files too, e.g. I assume 'Asset DB Process' refers to Unity refreshing the junk in the Library folder, which, when involving scripts, could theoretically be skipped if said scripts didn't recompile.
     
  23. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    844
    Domain reload time / compile time don't fully contain one another. Here's an example of changing some code in a shared assembly. IL post processors can take a hell of a long time :p in this case, vastly overshadowing the domain reload time.
    Code (csharp):
    1. *** Tundra build success (29.68 seconds), 84 items updated, 1969 evaluated
    2. Reloading assemblies after forced synchronous recompile.
    3. [Licensing::Client] Successfully resolved entitlements
    4. Begin MonoManager ReloadAssembly
    5. Total cache size 268074442
    6. Total cache size after purge 268074442
    7. - Loaded All Assemblies, in  4.660 seconds
    8. Refreshing native plugins compatible for Editor in 34.19 ms, found 2 plugins.
    9. Native extension for WindowsStandalone target not found
    10. Assembly reference Packages/com.unity.entities/Unity.Scenes.Editor/Internals/Properties/Unity.Properties.Internals.asmref has no target assembly definition
    11. [MODES] ModeService[none].Initialize
    12. [MODES] ModeService[none].LoadModes
    13. TypeManager.Initialize took: 936ms
    14. [PhysX] Initialized MultithreadedTaskDispatcher with 6 workers.
    15. Mono: successfully reloaded assembly
    16. - Finished resetting the current domain, in  6.548 seconds
    17. Domain Reload Profiling: 11190ms
    18.     BeginReloadAssembly (2677ms)
    19.         ExecutionOrderSort (0ms)
    20.         DisableScriptedObjects (732ms)
    21.         BackupInstance (0ms)
    22.         ReleaseScriptingObjects (0ms)
    23.         CreateAndSetChildDomain (958ms)
    24.     RebuildCommonClasses (498ms)
    25.     RebuildNativeTypeToScriptingClass (22ms)
    26.     initialDomainReloadingComplete (248ms)
    27.     LoadAllAssembliesAndSetupDomain (1198ms)
    28.         LoadAssemblies (1125ms)
    29.         RebuildTransferFunctionScriptingTraits (0ms)
    30.         AnalyzeDomain (746ms)
    31.             TypeCache.Refresh (99ms)
    32.                 TypeCache.ScanAssembly (83ms)
    33.             BuildScriptInfoCaches (627ms)
    34.             ResolveRequiredComponents (17ms)
    35.     FinalizeReload (6548ms)
    36.         ReleaseScriptCaches (0ms)
    37.         RebuildScriptCaches (0ms)
    38.         SetupLoadedEditorAssemblies (3378ms)
    39.             LogAssemblyErrors (0ms)
    40.             InitializePlatformSupportModulesInManaged (24ms)
    41.             SetLoadedEditorAssemblies (12ms)
    42.             BeforeProcessingInitializeOnLoad (951ms)
    43.             ProcessInitializeOnLoadAttributes (1475ms)
    44.             ProcessInitializeOnLoadMethodAttributes (897ms)
    45.             AfterProcessingInitializeOnLoad (18ms)
    46.             EditorAssembliesLoaded (1ms)
    47.         ExecutionOrderSort2 (0ms)
    48.         AwakeInstancesAfterBackupRestoration (1742ms)
    49. Refreshing native plugins compatible for Editor in 3.21 ms, found 2 plugins.
    50. Preloading 0 native plugins for Editor in 0.00 ms.
    51. Asset Pipeline Refresh (id=): Total: 43.666 seconds - Initiated by RefreshV2(AllowForceSynchronousImport)
    52.     Summary:
    53.         Imports: total=0 (actual=0, local cache=0, cache server=0)
    54.         Asset DB Process Time: managed=0 ms, native=6801 ms
    55.         Asset DB Callback time: managed=356 ms, native=136 ms
    56.         Scripting: domain reloads=1, domain reload time=4717 ms, compile time=31655 ms, other=0 ms
    57.         Project Asset Count: scripts=2114, non-scripts=3075
    58.         Asset File Changes: new=0, changed=1, moved=0, deleted=0
    59.         Scan Filter Count: 0
    60.     InvokeCustomDependenciesCallbacks: 0.002ms
    61.     InvokePackagesCallback: 143.586ms
    62.     ApplyChangesToAssetFolders: 0.340ms
    63.     Scan: 65.454ms
    64.     OnSourceAssetsModified: 1.040ms
    65.     CategorizeAssetsWithTransientArtifact: 0.337ms
    66.     ProcessAssetsWithTransientArtifactChanges: 7.454ms
    67.     CategorizeAssets: 44.109ms
    68.     ImportOutOfDateAssets: 6597.849ms (-25063.430ms without children)
    69.         CompileScripts: 31654.831ms
    70.         CollectScriptTypesHashes: 2.685ms
    71.         ReloadNativeAssets: 0.949ms
    72.         UnloadImportedAssets: 0.324ms
    73.         EnsureUptoDateAssetsAreRegisteredWithGuidPM: 1.110ms
    74.         InitializingProgressBar: 0.000ms
    75.         PostProcessAllAssetNotificationsAddChangedAssets: 0.000ms
    76.         OnDemandSchedulerStart: 1.378ms
    77.     PostProcessAllAssets: 359.705ms
    78.     Hotreload: 7.832ms
    79.     GatherAllCurrentPrimaryArtifactRevisions: 0.221ms
    80.     UnloadStreamsBegin: 1.408ms
    81.     PersistCurrentRevisions: 0.172ms
    82.     UnloadStreamsEnd: 0.001ms
    83.     GenerateScriptTypeHashes: 6.149ms
    84.     Untracked: 36436.830ms
     
    spiney199 likes this.