Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Feedback [12/21/19] My personal feedback of the full DOTS spectrum

Discussion in 'Entity Component System' started by DreamingImLatios, Sep 29, 2019.

  1. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    12/21/19 update here: My personal feedback of the full DOTS spectrum

    I was hoping that more Unite talks or the new DOTS packages would be out before I wrote this, but unfortunately, they are not. Since I’m not going to have a lot of time the next few weekends, I figured it best to post this now and update it if I learn anything new. I’m trying to keep this post focused on things that have not been frequently discussed or addressed in the Unite videos but directly affect my development and workflows.

    This is a long post so I have broken things up by sections that can mostly be read independently of each other.

    About me and my perspective
    Major disclaimer, I am not a full time game developer working on production projects. I am merely a hardcore hobbyist. I am actually a C++ developer for embedded commercial applications. Before Unity, I used to do all my hobbyist game dev using raw C++ and no engine, so I learned a lot about different game engine architectures. Consequentially, the technical features that DOTS provide makes me feel right at home, and opens up a real potential for dozens of game ideas I had set aside due to technical feasibility. Currently I am participating in weekend game jams using DOTS and building up a framework of utilities until I have enough ideas and technologies in place to commit my free time to one of the dozens of game projects I have in mind. I am still debating whether I want to open-source my framework.

    As an example of the kinds of stupid things I do, here is one of my weekend game jam projects that managed to make it to the internet (most don’t): https://dreaming381.itch.io/sheep-or-fall

    DOTS Documentation
    My general policy is that I don’t expect anything in preview to be fully documented, and the released DOTS packages are actually quite well-documented compared to most things I come across. But one of the things that has kept me using Unity has been the excellent documentation and scripting reference pages. While the pages are finicky on mobile, they are superior in many ways to package manager documentation, and that is something I hope gets addressed since DOTS documentation is package manager documentation.

    First, the scripting reference brings to the forefront what is meant to be the public API, whereas the package manager documentation auto-generates documentation for everything. Second, the scripting reference collapses all overloads into a single page with detailed descriptions and sometimes even a code example to help me gauge if what I am looking at might fit my use case. Meanwhile, the mathematics documentation takes my web browser way too many seconds to load and I have to use my web browser’s search tool to find what I am looking for.

    Mathematics
    First off, Mathematics has become my new favorite math library. Basing it off of HLSL (which I consider superior to GLSL) was a great decision. In fact, I like it so much that for a recent project I made as an example project in classical Unity where I tried to avoid almost everything DOTS, I still ended up using Mathematics. And while I have difficulty with the package manager documentation format, the actual descriptions provided in the documentation have been very helpful.

    As for where I would like to see some improvement, give some conditional logic a little more love! Right now, bitmask outperforms math.any/all(bool4). It would also be nice to have predefined bitmask constants so that I don’t have to define my own. Example: math.bitmaskFFTF. Having bitmask work with bool2 and bool3 would be cool. Also I sometimes run into situations where I need to compare 2v2 floats with greater than and 2v2 floats with less than and I would like to get better vectorization out of those operations. One way to do that is to shuffle the arguments and then decrement two of the floats by a single machine unit. That latter part could be a useful math function. cmin and cmax are awesome, but sometimes I want to get the first index of the min and max value and use that to select from a different variable. And lastly, Physics uses a FourTransformedPoints struct and I made a similar simdFloat3 data type which does similar things and some extras. I made simdFloat3 use a, b, c, and d to index the four float3s while x, y, and z indexes the components of all four float3s. Both have swizzling thanks to T4. While I have gotten really good results with this, I would love to see an official version in Mathematics with intrinsics.

    Burst
    Burst is probably the most original technology in the DOTS tech stack, and it solves what was Unity’s biggest weakness and made it Unity’s biggest strength. For 99% of the use cases, it is as simple as adding [BurstCompile]. Most questions are answered in the solo documentation page. Simplicity at its finest! The docs are missing function pointers and shared statics, though I haven’t found a real use case for either of those yet.

    I have three major pain points with Burst right now.

    First, generic jobs are not supported in AOT. Normally the solution for this is to use a custom job type instead, but that doesn’t work for my use case (at least to my level of understanding). My use case is that I have several complex acceleration structures that I want to iterate over and process the results of the iteration with custom logic. That custom logic comes packaged in a generic struct with NativeContainers and ComponentDataFromEntity and the struct implements an interface. Now in order to iterate over all the generated results, I need multiple phases where the results within a phase can be dispatched in a thread-safe manner but results of one phase are not thread-safe with the results of another phase. (If you need a more specific example, I can provide one.) Anyways, my solution to this is to have a generic method that takes the custom logic struct and then schedules the jobs in a chain. When testing in the editor, this works amazingly and I am really happy with the performance of my clean and shiny code. But then I can’t ship it. I’m hoping that with the new 2019.3 compiler hooks that this is an easy problem to solve. Ideally, it either just works or I can make it work with some [BurstCompileForeach<ICustomLogicProcessor>] or something.

    Second, Burst is not supported for WebGL yet. I don’t need hand-optimized math intrinsics. Just having LLVM go aggro on my code will probably give me enough performance for my game jam games.

    Third, I’m in play mode and a suddenly see a weird behavior that I can reproduce pretty easily. I pause and then want to set a breakpoint in one particular job I suspect to be the culprit. The job is like many of my other jobs in that I am using Burst without debug = true because otherwise my CPU would cry. But in order to temporarily turn off Burst for that one job to set a breakpoint, I need to stop the game, edit the attribute, recompile the code, then enter playmode again.

    Besides my request that the pains go away, I wish you the best of luck with supporting more platforms and more compilation modes!

    Collections
    Collections are great! But writing custom containers is a pain when trying to get the safety sentinels correct. Most of the time, I need a custom data structure composed of several already existing NativeContainers. So instead of writing a custom container, I just make a struct with those other NativeContainers as members. The problem I run into is when trying to add safety attributes. Sometimes, the attributes work when put on the struct instance in a job. Sometimes I have to put the attributes inside the struct definition instead. It’s confusing.

    Being able to specify in the struct definition [AlwaysReadOnly], [AlwaysDisableParallelForRestriction], [NeverDisableParallelForRestriction], and [InheritFromStruct] or similar attributes could solve this problem.

    Jobs
    I have very little negative to say about the job system. You guys did an awesome job with this! I don’t really use the extra job types other than IJobParallelForDefer. Only pain points with multithreading that isn’t Burst-related are how annoying it is to return a single result when I Run() an IJobForEach and the lack of an out-of-the-box non-deterministic multithreaded RNG for presentation and logic where I don’t care about determinism. These issues are relatively minor for me as I can easily create a workaround solution.

    Entities
    I love this ECS! It has reached the point where it gives me everything I had with every other C++ ECS I have worked with except in a way cleaner format! If I want to use it to batch-reason about my MonoBehaviours, I can do that. If I want to run all my systems totally serial but still have the algorithms go wide, I can do that. If I want to use fewer fat components, I can do that. If I want a full reactive system that only processes a change per entity, I can do that (requires storing the version number per component for anyone wondering). If I want to treat my logic more like an ES (one giant component per entity) and still get Burst and threading, I can do that. If I want to have a binary tree per entity, I can do that. Blobs make awesome static asset tables, and dynamic asset tables can be implemented with entity references. My code is cleaner than ever and more reusable. I have more control over execution order and have less code paths because of it. It was way easier for me to come up with a naming convention scheme that made sense. And I now build games faster with DOTS than without, even though I often cheat and use some MonoBehaviours for certain things, although that’s becoming less frequent with every project.

    But this solution is not perfect, and there’s some room for improvement that can help people like me out.

    I know there are some changes to how Worlds work in that next release, so I am going to spare my complaints about having to duplicate code to add systems to ComponentSystemGroups. I will just mention that being able to subclass Worlds to give them more globalized data is super useful for framework writers like me.

    My biggest complaint of Entities is the performance of systems. Now I know that this has been discussed and optimizations are coming. But I haven’t seen any discussions about what are the real roots of this problem, and one root in particular I believe is a design flaw that I hope can be fixed before Entities leaves preview. The issue I have is with the check to run or not run a system. Every system checks a bunch of EntityQueries for entity count and does some and|or combinational processing to determine if the system should run. If the result differs from the previous frame, the system also has OnStart/StopRunning called. While I agree that the default version of this method is a good idea and should be optimized, the truth is that this code is making decisions when I as the gameplay developer can make better ones. Whether a system needs to run might be dependent on only one EntityQuery, but also be dependent on a value of a singleton and what scene is currently active. I may also not want OnStartRunning and OnStopRunning to be called based on my criteria, but only when the Enabled property changes. And I want all of this visible to the EntityDebugger. So what is my proposed solution to this? Make ShouldRunSystem virtual and return 2 bools, the second bool being for calling OnStart/StopRunning.

    In my custom framework, I have two dictionaries of typed dictionaries<Entity, T> where I store structs that implement either an IComponent or an ICollectionComponent. IComponent can store ref types. ICollectionComponent is just like IComponent except that it can also store NativeContainers, the dictionary keeps track of Reader/Writer JobHandles, and it has special hooks into my abstract JobComponentSystem class to automatically update the handles if they aren’t updated explicitly. I don’t use these very often, but they are nice for some specific game logic. My annoyance is that I can’t make these part of the archetype and get them to work with Entities.Foreach.

    Transforms
    For the most part Transforms just works. The code is pretty optimized. My only complaint is that the transform conversion system automatically assumes that anything with a non-unit scale is a NonUniformScale when it might actually be a uniform Scale. I have to add the scale components back anyways for static entities for physics so it is not a huge deal for me. It’s just a little annoying.

    Scenes
    I haven’t messed around much with scenes as I am not really much of a level designer and end up procedurally generating everything. But for a recent game jam where I was collaborating with someone, we tried it out and I ran into this weird issue where the subscene icon was wrong and then eventually the scene cache got corrupted. I deleted the cache files and reloaded Unity and I haven’t reproduced the issue since. I am greatly looking forward to the new live link features in 2019.3! I am very curious, are those features going to work over internet and/or allow collaborative editing sessions?

    Hybrid Renderer
    0.1.1 broke my HDRP test project, so I use 0.1.0 sometimes. Besides that, most of my qualms with graphics and rendering are with the SRP and Shader Graph team, so I won’t discuss that here.

    Physics
    Unity made this rather interesting decision to use Blobs for colliders, essentially making colliders immutable. I immediately questioned this decision as I knew it was going to cause a lot of problems with procedural generation, scaling, morphing, and query logic. I decided to write my own solution borrowing pieces from Unity but using a union of collider types as an IComponentData in which some collider types could have a BlobAssetRef and be partially immutable while other collider types can have Entity refs instead for mutable meshes. This fundamental change has helped me avoid a lot of issues at the cost of eating up a lot of my free time. I have no idea how the physics team is going to make skinned meshes and cloth work, but that team is smart and will figure something out. I just expect it is going to take them a while.

    As for simulation, that part seems like an improvement over PhysX. I don’t really know. I don’t do a whole lot of realistic physics in games. I usually just want triggers and bump-and-slides and queries.

    I did discover one stupid trick that I think Unity could easily copy. Change the namespace from Unity.Physics to something like Unity.PhysicsEngine. This lets you use “Physics” as a name of a static class to hold all your query API. It gets rid of the need to do all the ICollidable nonsense and makes the API much more intuitive.

    Audio
    I haven’t used this and do not plan on using this until some out-of-the-box ECS API comes along. I may update this section once that happens.

    Animation
    TBD

    Netcode
    Also TBD, though I do have some general comments about my view of this. In the past, I have always avoided making networked games. There is just too much up-front investment. I have to write all the game code with networking in mind. Then I need a networking infrastructure and likely choose my hosting solution before I can even verify if my game is still fun over a network. If the new Netcode allows me to set up a server and client on my local machine and have a remote buddy connect as a client over the internet and we can test the fun-factor of the game before ever choosing my hosting solution, that would be pretty huge for me!

    DOTS in General
    This isn’t a problem at the moment, but I suspect it to become a problem as more things transition to DOTS. People build lots of unique and original games in Unity. There is no way every DOTS package Unity releases is going to meet everyone’s needs, and people are going to want to rip stuff out and replace it with custom solutions. I ask that this be kept in mind when making packages, so that someone like me can replace the physics package with my own, and be able to make it play nice with Animation and Cinemachine without having to modify those packages. This doesn’t have to be easy. It just has to be doable.

    Thanks for reading!
    Hopefully you found this helpful and insightful!
     
    Last edited: Dec 21, 2019
    alexchesser, XELF, ekakiya and 24 others like this.
  2. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    416
    Thanks a lot for this feedback, that's very helpful!

    About the issues related to burst:

    Indeed, they are all on our radar. We can't give any ETA but we hopefully expect that these issues will be solved during the upcoming year.

    Issues for Unity.Mathematics are noted as well, we will see what we can do.
     
  3. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    406
    YES YES YES BURST FOR WEBGL PLEASE ;w;
     
    andrzej_cadp likes this.
  4. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    I did some more investigation into this and what I found is both fascinating and frustrating at the same time. Perhaps someone can think of a way to unblock me?

    So I ran a test where I ran a complex algorithm using several different techniques. The algorithm sees a massive speedup with Burst so by simply profiling the time the job takes I can see which jobs ran with Burst and which ones did not.

    First, I created a job with an explicit type and duplicated the code so that only one had Burst. It called a static generic method designed for the use case when the entire algorithm needs to be launched from within a single-threaded job.

    Second, I created a two pairs of generic jobs, one pair with Burst. I created instances of these jobs with a concrete type and ran them all.

    Third, I created a generic scheduling function which scheduled the generic jobs. I then called the scheduling function with a concrete type.

    I ran each of these 3 times with a Stopwatch and output elapsedTicks and a member of the concrete type which I used as a simple validity check.

    Here's where things got interesting, the first and second approaches had drastically faster Burst running times while the third one didn't see any performance difference. This means that Burst does not only compile the definitions of jobs, but also declared instances of generic jobs from anywhere in code. So that got me wondering if adding AggressiveInlining to my schedulers could fix the issue, but unfortunately that did not change the result.

    It is worth noting that these results are in a built player using a Mono backend for Windows desktop. In the Editor, all three techniques see a significant performance boost with Burst.
     
  5. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    416
    > Third, I created a generic scheduling function which scheduled the generic jobs. I then called the scheduling function with a concrete type.

    This is precisely the case not supported by burst today. We can't detect these jobs as we are currently looking for explicit Job instantiation in the code, but if the job is generic and is created by a generic method, the editor will still be able to compile it at schedule time, but we won't be able to compile it at AOT/Player build time.

    In order to support this scenario, we will have to process all the methods, including the one not related to burst to find out all generic instances. Until this year, there was no infrastructure in Unity that could allow this efficiently, but we are starting to have an integration into the C# Roslyn compiler that can do that, so we might be able to rely on that. We will hopefully tackle this next year.
     
  6. gebbiz

    gebbiz

    Joined:
    May 23, 2018
    Posts:
    14
    I understand the difficulty of automatically detecting all the used types but would it be possible to have an editor script callback where we could manually list them for the compiler?
     
    JesOb likes this.
  7. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    416
    You can do that already today and you don't need any editor callbacks.

    Suppose that you have a generic job `MyJobWithGeneric<T>` which is scheduled by a generic method:

    Code (CSharp):
    1. public static void ScheduleMyJobWithGeneric<T>(T data) where T : IXXX
    2. {
    3.        var job = new MyJobWithGeneric<T>();
    4.        job.Input = data;
    5.        job.Schedule();
    6. }
    and that you are using this method somewhere in your code like that:

    Code (CSharp):
    1. ScheduleMyJobWithGeneric<MyStruct>(new MyStruct() { ... });
    2. ScheduleMyJobWithGeneric<MyStruct1>(new MyStruct1() { ... });
    3. ScheduleMyJobWithGeneric<MyStruct2>(new MyStruct2() { ... });
    Then to help the burst compiler today, you can manually and explicitly instantiate the jobs in a private static method, that method being here just to help the compiler to list the instantiated generics, but that method would not be called by any code:

    Code (CSharp):
    1. private static void InstantiateAllMyJobsExplicitly()
    2. {
    3.      new MyJobWithGeneric<MyStruct>();
    4.      new MyJobWithGeneric<MyStruct1>();
    5.      new MyJobWithGeneric<MyStruct2>();
    6.      ...
    7. }
     
    Radu392 likes this.
  8. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    @xoofx why does the compiler detect the generic instantiation in that case but not when in the first? wouldn't it be eventually generating the concrete structs in both cases?
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    It will eventually generate the concrete structs. But it's not explicit, so you have to run some code that figures that out. That code exists - it's a part of the compiler. Which is why their strategy is to hook into that:

    It would be pretty easy to write a static analyzer that would handle the example case above, but not one that handles arbitrary cases.
     
  10. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    btw IL2CPP can generate the structs AOT in both cases and more...
     
  11. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    This unfortunately is not trivial even with code gen as the structs implementing the interface live in a different assembly as the jobs, and the jobs should be internal. Ideally I could use an attribute on the interface similar to how the UNITY_AVOID_REFLECTION codepath works in IJobParallelForFilter works, since that is extremely similar to my use case. Unfortunately, mimicking that pattern using a dummy static execute function did not work either. Making an actual custom job is also not an option due to the number of variants of the jobs the scheduler can produce.

    Just tried it. I got the same output as Mono except the non-burst code ran 2x faster. Still not the 50x speedup Burst gives though. :(
     
  12. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    I meant, IL2CPP is full AOT like Burst, and it can handle that case.
    I was curious about what is the difference, e.g. how does Burst collect which generic jobs to compile.

    shouldn't
    Code (CSharp):
    1. void Foo() {new GenericJob<int>().Schedule();}
    and
    Code (CSharp):
    1. void Foo() {Bar<int>();}
    2. void Bar<T>() {new GenericJob<T>().Schedule();}
    generate the same metadata in the dll?
    what about
    Code (CSharp):
    1. void Bar<T>() {new GenericJob<int>().Schedule();}
    with explicit type but in a generic method?
     
    Orimay likes this.
  13. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    I will upload my test project so you and others can investigate the different methodologies after LD45. The test project also showcases a performance bug where Burst in the editor outperforms Burst in a build.
     
  14. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    416
    Because in that case, there is directly a table in an assembly that you can inspect without looking at all the methods or trying to instantiate generics to find usage. It's just a lookup which is very fast, that's why we are able to do it via this method.

    Finding job instantiation requires to inspect all call sites of all methods to find out where they could be used, to actually create generic instance of methods...etc. Suppose you have a generic method D<T>, which is actually instantiating the Job, we need to know 1) that this method is actually scheduling a job, then 2) we need to transitively find back all possible path of calling this method where we could resolve an actual type, including generic instances. It is of course possible (that's how a full AOT solution like IL2CPP is working), but for burst it is very costly to do that (because we would have to scan almost everything)

    So we will have to do it, but we will have to use a faster path than the one we have today to detect these kind of generic instances
     
    GilCat, JoNax97, learc83 and 3 others like this.
  15. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    406
    Hey, @xoofx can I pay you to get WebGL-fixes in earlier? What about donation drives towards certain parts of Unity-development to make development better/quicker? I know the future is web-based, apps are dead, and that whoever gets in on that first to be the industry standard will survive in the long run.

    Please, please? :3c
     
  16. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    That's a really good explanation of the problem. I can see how a generic scheduler created by a generic system instantiated by a generic ComponentSystemGroup could be ridiculously ugly. But perhaps my use case might be more quickly solvable if we look at the problem from a different perspective. What I really have is a complicated job type which can be decomposed into simpler job types. The complicated job has approximately 20 different Schedule overloads and I really don't want to write all the unsafe code for each overload or try do merge them into some branchy mess. I think there needs to be a higher-level scaffold for custom job types that don't operate at the reflection level. Perhaps an API like this?
    Code (CSharp):
    1. [JobProducerScheduler(typeof(GridInternal.ProcessEdgesInColumnsJob<>), typeof(GridInternalProcessEdgesInRowsJob<>))]
    2. public interface IJobProcessEdges
    3. {
    4.     void ProcessEdge(ref Node a, ref Node b, ref EdgeData edgeData);
    5. }
    6.  
    7. public static class IJobProcessEdgesExtensions
    8. {
    9.     public static JobHandle Schedule<T>(this T jobInterface, Grid grid, JobHandle inputDeps) where T: struct, IJobProcessEdges
    10.     {
    11.         inputDeps = new GridInternal.ProcessEdgesInColumnsJob<T> { grid = grid, processor = jobInterface }.Schedule(grid.columns, 4, inputDeps);
    12.         inputDeps = new GridInternal.ProcessEdgesInRowsJob<T> { grid = grid, processor = jobInterface }.Schedule(grid.rows, 4, inputDeps);
    13.         return inputDeps;
    14.     }
    15.  
    16.     // ... Many more overloads of schedule, scheduleSingle, and run
    17. }
    18.  
    If that isn't a more achievable near-term goal, then hopefully the code-gen API will be flexible in the way assemblies get defined so that I can have this kind of API.

    I greatly appreciate the time you put into this technology as well as the time you've taken to provide incredibly professional answers to our questions!
     
  17. Deleted User

    Deleted User

    Guest

    Good day,

    This method works only for Mono builds but not IL2CPP. If I try to do the following for IL2CPP build an exception will be thrown.
     
  18. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    That's probably an IL2CPP code stripping issue.
     
    Deleted User likes this.
  19. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    Well, I have finally gotten around to getting my framework up to the latest DOTS again (0.4.0), so I suppose it is as good of time as any to re-evaluate this like I promised a couple months ago.

    I will be referencing my framework for a lot of these discussions mostly because it makes it easier to present my points precisely. You can find it here: https://github.com/Dreaming381/Latios-Framework

    DOTS Documentation

    I didn’t think the Unity Engine documentation could get any better. It got better. Mobile is an enjoyable experience. Unfortunately, packages haven’t evolved in the same light and the Mathematics APIs still take eons to load.

    As for the content in the preview packages, some of them are progressing quite well!

    Mathematics

    Nothing changed. Docs take forever to load but are useful once they do. I’m still using my own simdFloat3. I still screw up the order of the bits in bitmask from time to time. And this is still my favorite math library by a long shot. I’m guessing this package will get some love again with Burst 1.3?

    Burst

    Burst is an incredible piece of technology. It made CPU programming fun again. I can go full technical on whatever I care about and not have to worry about the stuff I don’t care much about because it is still Unity and Unity makes a lot of that trivial.

    But where I really want to give praise is to the team behind the technology. Everyone on the team who participates on the forums has been helpful, kind, patient, and professional. You guys make some of the other teams at Unity look like a bunch of aimless monkeys. I have mad respect for you guys.

    And while I have a ton of things that I would love from Burst, I know you guys have your plates pretty full.

    The only nitpick I want to bring is that you removed showing the C# code in the Burst inspector. And now it is way harder for me to find a particular loop in a long and complex job I want to optimize. Can I have that back?

    Otherwise, I can’t wait for Burst 1.3!

    Collections

    Most of my feedback on containers is the same as before. Those things haven’t changed nor have they been better documented. However, I really do like the new features that have been arriving. Words and more unsafe structures are really solving some of the more awkward use cases I run into.

    The container I am missing the most is a dictionary implementation. I know there’s NativeHashMap, but sometimes I need a map to be able to grow. I’ve seen some clever solutions where people make a tree with each parent having 32 children.

    Jobs

    I have very little to say about the Job system. Engine-side is awesome and profiling performance with it seems to have improved for some odd reason. But is the package going to get any new development? Or is it “good enough” for a good while yet?

    Entities

    A lot has happened here, and I have a lot to talk about.

    This is still the coolest ECS I have ever worked with in terms of data organization. I can build games in so many different styles of ECS for any timeline and performance budget.

    I can tell you listened regarding World instances and ICustomBootstrap. The API is so much cleaner and more flexible. In BootstrapTools.cs in my framework, I still have to copy code from the Entities package and use reflection. But I did find myself removing hacks and adding new features when I updated the framework. Nice job! I hope you continue to improve it so that I don’t have to copy code or use reflection to access internal types.

    However, nothing has been done regarding being able to copy a component from one entity to another based on a ComponentType. I wrote an EntityDataCopyKit.cs file which uses reflection to handle that, as I depend on it for my entity merging system. But this is another hack I don’t want to keep.

    In my last post, I asked for ShouldRunSystem to be virtual. While I think it is a good thing by default that Unity make decisions for me, sometimes I want to make better decisions that Unity will never know about. I am currently getting around this in my framework by overriding OnUpdate of ComponentSystemGroup. (SuperSystem.cs) While my solution works, I don’t think what I am doing is the intended solution, so I would like to know what is the intended way to control whether or not a system runs based on data other than EntityQueries?

    Bursted ECBs are nice. I need to spend a lot more time understanding their performance characteristics for different scenarios. It is really nice in search algorithms where you have a low number of outputs but a high amount of data to crunch to get those outputs. No complaints there.

    The biggest and most exciting innovation in the Entities package is also the most frustrating for me. That’s code gen. [GenerateAuthoringComponent] is awesome for prototyping, except rarely in prototyping do I want to make a file for each component. So that one is counter-intuitive. Entities.ForEach is missing ScheduleSingle. But even more so, I can’t add my own types. If you look at the FindPairs API I have in my physics package, you’ll see I define as the last argument a generic IFindPairsProcessor value. This provides a callback for the jobs that ultimately get scheduled allowing user code to decide what to do with those results. It is similar to Unity.Physics ITriggerEventsJob except this one actually runs in multiple jobs and doesn’t use a custom job type. Anyways, if it looks like that IFindPairsProcessor argument could be replaced with a lambda, that’s because that’s exactly what I want to do! But the code gen code as it exists today is completely internal and not extensible. I would have to copy huge amounts of code to do something similar. Is this what I should do, or is there a better way.

    And lastly, Code gen with the lambdas has interfered with another problem I have been trying to solve. I don’t really care if Unity solves the lambda conflicts or the root of the problem itself. But I really want a solution to this.

    Currently, if you need an Entity to reference a managed object, you need to use a Class IComponentData. When you use a class IComponentData, whenever you instantiate such an Entity, you generate garbage. I want a component type that can be instantiated and destroyed without generating new garbage endlessly and also be a struct type so that copy by value rules apply. Effectively, I want something in between struct IComponentData and class IComponentData. In my framework, I came up with a solution using IComponent. But really it is just a custom interface not tied to Entities in any way and the only way I make it act as part of the archetype is by using reactive tag components. Of course, this conversion from IComponent to IManagedComponentTag<> for specifying EntityQueries totally breaks with code gen Entities.ForEach.

    Ok. So the use cases for my IComponent are small enough that I could deal with not having it as inconvenient as it is. What I can’t live without is my ICollectionComponent. When a NativeContainer needs to be accessed from multiple Systems, the correct home for such data is to be attached to an Entity. Forcing NativeContainers to live in systems is just asking for unnecessary code coupling and not being able to efficiently deal with a dynamic number of NativeContainers. The Overwatch team has been bitten by this. I personally have been bitten by this. And a few people on these forums have been bitten by this. My ICollectionComponent solves that issue and even has this really nice feature of automatically tracking and updating its JobHandles when a user requests it in a JobComponentSystem. But ICollectionComponent is not a true ComponentType either and also relies on reactive tags to affect an Entity’s archetype. It is totally broken with Entities.ForEach.

    I don’t care is Unity unlocks code gen, unlocks an API for creating custom ComponentTypes, or borrows my ICollectionComponent implementation and makes it a first-class citizen. This is a blocker for me. I need to be unblocked if I want to push my tech to the next level.

    Heck, making Entities.ForEach have an option to use an EntityQuery passed in would get me far enough along that it wouldn’t be painful.

    But hey! I would not have built my framework with an Entities dependency if I did not believe that Entities was a solid foundation on which to build. It totally is, and it is so close to being absolutely perfect. I just need a little more extensibility support.

    Transforms

    Nothing new here. It works. But the conversion system isn’t smart enough to use Scale over NonUniformScale. I wrote a system that corrects it. I’m happy.

    Scenes

    Please fix the SubScene components and API to use “SubScene” instead of “Scene” in their type, variable, and method names. They get really confusing when working with actual scenes. Otherwise the Scene of SubScenes paradigm is crazy powerful and solves one of classical Unity’s biggest problems in the most elegant way possible.

    Hybrid Renderer

    HDRP is fixed!

    My only complaint now is please at least partially fix this or at least explain what is going on here:

    Turning off culling of RenderMeshSystemV2

    Otherwise I really like the new instance property system’s engine-side integration. It is fast and clean!

    Physics

    I love the algorithms. I hate the architecture. If you want the full rant, you can go here: https://github.com/Dreaming381/Latios-Framework/blob/master/Documentation/Physics/README.md

    Feel free to borrow anything you like. It’s technical yours anyways.

    Audio

    I haven’t used this and do not plan on using this until some out-of-the-box ECS API comes along. I may update this section once that happens. Tiny has one, but my framework doesn’t work with Tiny yet because of the many previously discussed issues.

    Animation

    I still haven’t figured out what is totally going on here yet and whether or not I like it. I think the biggest question I ran into is: What exactly is a ComputeBuffer in Unity? Does it do things on devices that don’t have Compute Shader support? I think that’s what Animation is using to upload the bones, right?

    I need a lot more time with this one.

    Netcode

    My general comments from my first post still apply here. It also isn’t intuitive to me how I would rely on determinism to simulate thousands of entities with rollback without synchronizing all their positions. The simulation is cheap. And I don’t mind doing something custom. I just need some direction here.

    Build

    I like it. I think assembly code gen integration would go a long ways. I would love to be able to customize the arguments Unity feeds into the compiler for each assembly, as well as intuitively generate and inject new assemblies after the authored assemblies have been built. I’m probably going to try it anyways for my next version of my Burst Generics Patcher. But getting some more official support for such pipelines would be awesome!

    DOTS Runtime

    I like it. I can’t use it yet because my framework doesn’t support it because it needs reflection to get access to stuff in Entities. Also, what kind of Burst support is there for WebGL? I haven’t been able to figure out what “partially supported” means.

    DOTS in General

    Again, I really like what’s there (except for Physics). I’m just having trouble extending it with features that I am not aware to be on any roadmap. Extending and replacing modules is going to be critical for this new ecosystem. You have an incredible design that really opens things up. Its immersive. Keep working at fixing those ugly parts and one day the only negative thing I will be able to say will be prefaced by a “Nit:”.

    Thanks for reading!

    Hopefully you found the helpful and insightful.
     
    alexchesser, 5argon, sngdan and 4 others like this.
  20. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Thanks for the feedback on the 0.4

    Most of it agreed and things we will get to. One specific one to follow up:

    Our plan is to stick with:
    class = managed, main thread, full flexibility
    struct = blittable / optimal / jobified / burstable

    I do think that adding an inbetween adds too much complexity for learning, we already have a lot of different concepts and types to learn.

    I think you should find ways to reduce managed references if you really need instantiation to be faster & GC Free. Alternatively enabling Incremental GC is well supported in Unity now.

    We are also looking at adding support for IDisposable on managed IComponentData.
    This would make it straightforward to manage any native collection on a class IComponentData.

    Supporting EntityQuery as an input to Entities.ForEach will be added.
     
    alexchesser likes this.
  21. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    Trust me. I try. There's not a lot I can do though about ComputeBuffers and friends, and I would prefer not having an additional layer of remapping and indirection if I can help it. One of my actual use cases is I have thousands of entities simulated, but only a small number are visible. The ones that are visible rely on a pool of ComputeBuffers, and ideally, each entity updates the same ComputeBuffer each frame so that it only has to perform incremental updates. Maintaining that link using a reference in a struct is just a lot cleaner.

    Existence-based processing on NativeContainers with semi-automatic dependency management is a crazy powerful luxury. And for me, that means instantiating and destroying a small number of entities every frame.

    I completely understand this viewpoint. So if I get to be the one who provides that functionality for those who want it, even if it means using smoke and mirrors, I am okay with that. EntityQuery as input and being able to modify and extend the code gen process will be all I need. Thanks for replying!
     
  22. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Regarding ComputeBuffers... Generally speaking you want to batch things. To me this implies that this state belongs in a system. For example in the animation system. We generate one huge ComputeBuffers. All bone matrices are extracted from all character rigs (DynamicBuffer) and blitted into one big compute buffer every frame.

    Why is this important:
    1. GPU's like large amounts of data, small compute buffers have large overhead in memory management in the driver etc
    2. Once the data is in one big ComputeBuffer, you can do instancing at render time. Eg. Each character just has a base index into the big ComputeBuffer (Simply using the new IComponentData material property's). Thus if the characters happen to share material they can use instancing codepath & easily render massive amounts.


    This is probablywhy we have never run into the problem you are running into.
    I am quiite sure that in terms of actual overhead, class IComponentData with a reference to a compute buffer won't be a performance bottleneck, it will be the managing of many ComputeBuffers itself on the Unity side and driver...
     
  23. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Note that the FPS Netcode package is not intending to solve that. The FPS netcode package is all about running the simulation on one machine and efficiently sending delta compressed packets.

    Our intention is to have multiple different netcode architecture packages for specific uses.

    Entities is a very good foundation for rollback & deterministic simulation. But we currently don't have a high level package that takes care of the highlevel netcode for it. We did a prototype over a hackweek trying out GGPO and it was straightforward ~ 2000 lines of code rigging up the surroundings and worked very well.
     
    DreamingImLatios likes this.
  24. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,113
    Which netcode architecture should I use for mobile MOBA game?
     
  25. filod

    filod

    Joined:
    Oct 3, 2015
    Posts:
    223
    it's would be kind if you can share more about latio's rant against unity.physics, i ran into some of them, for example Imutable Collider, i can't tell whether it's a choice with serious consideration that applies well on most cases, or it's a design tailor for specific situation (like in networked FPS genre).
     
    Last edited: Dec 22, 2019
  26. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    Same for me, I was a bit confused by this change :)
     
    DreamingImLatios likes this.
  27. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    I totally get where you are coming from, but you made some assumptions about my problem that are not quite true.

    I have a crowd simulation with three LOD levels. At the lowest level, hair and cloth are simple meshes. At the medium level, I do a course simulation over dynamic buffers and pack the results into a GPU buffer (still experimenting with mesh vs compute buffer). But at the highest LOD, I am running a full GPU simulation on hair and cloth. The logic for adding and removing things from the highest LOD group is complicated because adding a group is really expensive. But once I add it, I can do incremental updates to maintain it. It's that complicated load balancing logic in the soft transition area that makes references on structs a lot more convenient than an intermediate remap structure.

    Thanks. That's what I thought. DOTS makes Netcode actually seem viable for the types of things I typically do, but that also means I am really new and I am not sure how I would go about implementing a predicted rollback solution integrated with the solution of the FPS sample (I need a mix of server authoritative and GGPO). I guess my point is that I just don't understand enough about the new networking tech to be able to give a proper review of its intuitiveness.
     
  28. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I'm not sure apps will die before decentralised processing, which is already here, becomes far widespread. Basically, apps have to die before 5G gains widespread adoption. It seems to me that the efforts are in streaming but this is an off-topic discussion.
     
  29. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    406
    Have you developed for iOS & 'Droid? I'd rather not, and I have been doing this for 7 years now. The moment I touched WebXR or anything that takes HTML5 + .js and has less than a second of compile time for a full 3D app experience has become a huge "yes please" from me.
     
  30. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    My biggest comment on DOTS generally is we saved ourselves a good amount of pain by not adopting some of the high level api's early. Core ECS/jobs/burst has for the most part been pretty stable. Things like the conversion pipeline and sub scenes, we looked at the core of how they worked went with that but stuck with simple versions of our own. If/when we do eventually convert to official api's, I think it will be relatively painless.

    High level flows is really where I see things not thought through as well especially early on in the feature. Physics has numerous scalability issues in that area, but the core of it is great.

    Hybrid renderer is sort of an odd duck. It doesn't really solve enough hard problems to be worth much pain. We ended up ditching it last month, just wasn't worth the hassle for the little things it didn't do or didn't do well.