Hello everyone!
So every few months or so after I complete some personal game dev milestone (in today’s case, that was a new release of my public framework for DOTS, link in signature), I like to tour around Unity’s experimental tech and see what looks promising and what scares me. The last couple of times, I have left my feedback and thoughts on all the DOTS packages. You can find a link to those here: [12/21/19] My personal feedback of the full DOTS spectrum
This time I am doing the same, but with another few months of development experience. I’m going to keep my harshest criticisms for the things that have left betas or previews and for things that have been marketed the most.
This feedback is addressed specifically at you Unity DOTS Team. I don’t expect each of you to read all of this. Just the parts you are involved with or curious about. Anyways, let’s jump right in.
The Engine
While it is probably the last aspect people think about when it comes to DOTS, the engine has probably seen the most improvements reach production-ready status as of late. And that’s the problem. It is the last aspect people think about because it isn’t being marketed correctly. There are way too many people trying to “Convert their game to DOTS” by rewriting their entire game using ECS and then give up, when there is a perfectly production-ready C# job system and Burst compiler that works perfectly fine with GameObjects.
So first, make some marketing material for DOTS tech that doesn’t involve Entities whatsoever. And second, make a subforum in the Scripting section, titled “DOTS for GameObjects” or something. This should be the dedicated subforum for all the APIs for working with jobs, Burst, and APIs that work with NativeArrays like transforms, graphics, particles, and physics.
Transforms
They work. They need more documentation for unintuitive things like how if you have a job chain where the last job is an IJobParallelForTransform and you schedule it in Update and complete it in LateUpdate, it actually completes very shortly after the rest of the MonoBehaviours finish Update because there’s an internal sync point for all IJobParallelForTransform jobs between the Update and LateUpdate invocations. Instead I have found scheduling all but the IJobParallelForTransform job in Update and then finally scheduling IJobParallelForTransform in LateUpdate gets the results I was looking for.
Also, why is there no IJobTransform that passes in a NativeTransformAccessArray in its Execute method? If this tech was marketed properly, I would expect this to skyrocket to the top of the community-demanded priority list.
Particles
I can’t say much about this as I use VFX graph instead. However, you have JobHandles now, which is awesome. Once again, if you marketed this properly, there would probably a smart AI particle system asset on the Asset Store.
Physics
The limit to one hit per request is annoying. I think you guys would do better with an API that let you lock the Physics world to a custom job and perform raycasts. Granted, I know that team is tiny and is doing some cool stuff with articulations and contact modification which people are pretty hyped about, so I guess that’s just a priority thing.
Graphics
Being able to generate meshes, textures, and compute buffers in Bursted jobs off the main thread and dump them to the GPU is AMAZING! I found it most useful for custom UI. The docs are high-quality here too.
Animation
I don’t use animation jobs right now. That’s more a limitation with the tools I have to author animation in a way that could take advantage of this tech. The lack of feedback here is a “me” problem.
Package Manager
Package Manager is the primary way you deliver packages for DOTS. The workflow for developing packages has gotten a lot better. The workflow for discovering Unity packages has gotten a lot worse. Packages which have dependencies that expose public API don’t show up, so you have to manually add it again if you want to see the samples or docs (or explore the Library folder). In addition, discovering some of these packages requires knowing the magic package names. That means that in order to properly discover all the packages in the Unity registry, I would have to write a dictionary attack and bombard the server with queries. I think the right solution is to have a flag that users can add to the manifest.json to expose all packages in the Unity registry and tag the ones that are normally hidden with a “Hidden” symbol. Right now there is no rhyme or reason to what is visible (are Netcode and Havok Physics really going to leave preview this year?) and what isn’t, and it is disgusting.
The other thing that annoys me with packages is the generated code documentation. So the Unity Engine documentation is amazing! Every class has a page listing the properties and methods in a streamlined format. Then you click on a method and get to a page that has all the overloads and some high-quality descriptions and code examples.
Compare that to the packages documentation where you get one page with all the documentation for everything in that class, after it loads for like 30 seconds in some cases. This means I have to scroll halfway down the page the size of 50 monitors just to get at what I am looking for. Please fix this.
Mathematics
Not a whole lot has changed here, which is fine. It is still my favorite math library. I still think you could do with SoA types and operations for doing vector and matrix mathematics on 4 or 8 elements at a time using SIMD (like compute the cross product of 8 float3s at once). I’ve already started my own implementation.
Burst
This is the tech that lets my performance-sensitive code live on the CPU instead of the GPU using the clean C# language syntax and its tooling. I’m having way too much fun with this tech!
As Burst has evolved and allowed for more in-depth optimizations, you wrote up a bunch of documentation to go with it. Thank you! One of the things I was about to complain about (per-processor compile-time dispatch) is now documented with a simple code example. I’ll be busy! ![]()
One issue I have been having lately is that when looking at the Burst inspector for larger jobs, it is still difficult to separate what is my code and what is code that is part of the framework, like the IJobParallelFor loop or the IJobChunk iteration and fetching of component arrays. While that stuff is annotated, it is annotated everywhere very heavily, making it difficult to find my code. I would love a side-by-side view similar to Compiler Explorer where you can click on the C# code and it highlights the associated assembly instructions and vice-versa. At the very least, an in-window text search would be awesome.
The second thing I struggle with is I never know when specifying a structure to pass by value or pass by “in” will lead to better Burst assembly. Perhaps you could provide some documentation or tooling in this area?
Lastly, hardware prefetching doesn’t usually have a good sense of arrays of indices. There’s been some research articles on embedding software prefetching knowledge into compilers for this and other simple use cases. Have you looked into this at all?
Regardless of those annoyances I have to push absolute performance, I still have mad respect for you! Keep doing what you are doing!
Collections
I thought Collections was going to go out of preview this spring. But then spring happened and you gave a bunch of stuff an overhaul. I can’t complain. There’s a lot of cool tech. NativeHashSet, NativeReference, and FixedString all made it into my projects very quickly. I do hope Collections goes out of preview soon though because it adds so much value to the C# Job System and Burst and I want to see more crazy cool assets on the Asset Store using the C# Job System and Burst.
You have a major documentation problem. I have no idea from looking at the docs which containers are lock-free, or what locking behavior they exhibit. I have no idea which containers support what level of determinism. I have no idea whether the container is fully a reference or if I need to write the struct back to its source to keep things in sync.
Also there’s no UnsafeQueue type.
Jobs
Custom job and custom native container documentation is fine. But only IJobParallelForDefer is properly documented. Why?
That’s really all I have to say.
Entities
I really like this ECS. The boilerplate reduction has been going down quite a bit. Entities.ForEach has really grown on me. SystemBase was a godsend! The ComponentTypeHandle rename was so seemless most people didn’t even notice! Installing systems at custom locations in the player loop works super well.
Also, code documentation related to the runtime is top-notch! Getting that right with codegen was absolutely necessary, and you did that. Thank you!
Normally, I don’t mind breaking changes if what it takes for me to fix them ends up with me having cleaner code. However, if my code breaks without any obvious replacement other than nasty hacks, I get frustrated. That has only ever happened once with Entities, and it is related to ComponentSystemGroups. I need to be able to control the order of my systems and manually tick them. There’s just no way you are going to make ComponentSystemGroup to do all of that for me correctly automagically. I could go into a lot of details why attribute-based system ordering is horrible (couples code together, can’t reference private systems, becomes a trial-and-error guessing game when you have thread-occupancy issues). But I get that some people prefer it. And I really think that Unity needs to have it, but also be able to override it.
The biggest frustration I have had though is with authoring and Game Object Conversion. Don’t get me wrong, I love the concept, and was a strong advocate of it over a pure DOTS editor. Authoring and runtime representations do need to be split. My issue is with some implementation details that are really annoying.
-
You can’t turn off Unity’s built-in conversion systems for specific objects. I believe every GameObjectConversionSystem that Unity provides should come with an associated DisableConversion MonoBehavior so that we can write custom conversions instead. Either that or use WriteGroups. If I have a custom light-management system that makes lights work as pure Entities, I want to be able to disable Hybrid conversion on my lights and run my own conversion. I don’t want to remove the full GameObjectConversionSystem, and I don’t want to undo whatever that conversion system does.
-
Going along those lines, many GameObjectConversionSystems are private, so you cannot order them with attributes. Unlike with ICustomBootstrap, there’s no equivalent for performing GameObjectConversion. I’m stuck with attributes, but cannot even get my systems ordered relative to Unity’s to know that certain parts of the conversion have already taken place or not.
-
Manually versioning of conversion systems is broken. It sounds like a good idea, but in practice, all it takes is for a bugfix to land that modifies the result of a static method used at runtime that also gets used at authoring but the bugfixer never realized that and now everything is out of sync. Maybe there’s a way to make the ConverterVersion attributes apply to the assembly rather than each class so that I can make a tool auto-update it. But until then, it is clunky and not very maintainable.
-
Declaring of dependencies is poorly documented. Some methods do this for you. Some don’t and you have to do it yourself. This is inconsistent and not documented at all. The only way to know is to dig through the code.
-
There’s no way to tell if a BlobAsset belongs to a subscene or not, so at runtime there is no way to know if the BlobAsset is safe to Dispose. Maybe if there was a guaranteed system group in conversion that ran only when baking to a subscene, I could make this work. There probably is, but I have no idea because the process is not correctly documented whatsoever.
-
[GenerateAuthoringComponent] can only appear once in a file, meaning I can’t keep groups of components in one place. This negates nearly all of the rapid prototyping benefits that the attribute offers.
Not allowing struct IComponentData to be IDisposable makes Unsafe collections require indirecting into a NativeList of unsafe collections instead. That’s an unnecessary performance penalty.
The lack of EntityManager.SetSharedComponentData(NativeArray) forces users to do two structural change operations instead of one or do things outside of Burst.
We definitely need API’s for performing structural changes on ArchetypeChunk or NativeArray because allocating NativeArray is unnecessary.
It would be nice if we didn’t have to find all chunks twice when populating NativeArrays inside Entities.ForEach. Right now we have to do it once for EntityQuery.CalculateEntityCount and again for the ForEach. In addition, if the Entities.ForEach uses filters, we have to pre-apply them. Having an Entities.Initialize((int entityCount, int chunkCount)=>{}).ForEach would be awesome.
Can we get more “dynamic” type handle APIs? I’m not talking about DynamicBuffers (although they need this API too), but equivalents for reading and writing data using only ComponentType in EntityManager and ComponentDataFromEntity contexts. This would give us a ton of potential with regards to Entity merging, diffing, and serialization. A lot of us have been reaching at internal methods to do this. It shouldn’t be hard to wrap this up in an API, even if it was in a Unity.Entities.LowLevel.Unsafe namespace.
While we are on the topic of more accessors, GetSharedComponentIndexFromEntity would also be extremely valuable.
There are other things I could ask for, but I think this is a good enough list for now.
No game engine today can do what I want to do. I thought I would have to write my own custom engine. Instead, I am building what I need against Unity DOTS and ECS. Why? Because I believe it is the fastest route to achieve what I want to achieve. I believed that when I started, and I still believe that. That is a testament to the tech.
Transforms
The Transforms code has for the most part remained unchanged. It was great as an initial implementation to test out good ECS API design and all, but it has not aged as well. The biggest issue is that it is trying to be too flexible for something too fundamental. We really need one Translation, one Rotation, one Scale (probably the non-uniform variant), and then the hierarchy. Composites and pivots and all of that should be in a separate package so that people building assets can focus on the pieces that matter.
Besides that, it has two problems. The first is that the Rotation property on LocalToWorld gives incorrect results when there is scaling applied (if you fixed this recently, I missed it). The second problem is that updating the hierarchy is slow. Really slow. I don’t know if part of that is due to the dynamic buffer change filter bug, or if it is just thrashing caches all the time.
Fortunately, because it was designed with WriteGroups in mind, I think I can optimize it without touching the package. But it really needs a usability evaluation.
Scenes
For the most part, I have used subscenes as a way to reduce the number of modifiable Game Objects in the editor, but otherwise I end up having all the subscenes loaded at once in game and have all the entities alive. I do this, because the behavior of subscene streaming otherwise is very undocumented and very confusing. What qualifies whether a subscene is loaded or not? Which entities get destroyed when the subscene unloads? What happens to blobs that might be stored in custom containers when the subscene unloads?
It’s been nearly a year since you prototyped live link and nearly two since the MegaCity demo, and there’s still zero documentation or explanation on this. I commend the few studios that have figured out how to make it work for their projects. It still feels like a game of spin-the-bottle with a fuse-lit firework canister.
Hybrid Renderer
I am a graphics guy. While HR V2 was broken for me for a while, it is working correctly for me in these last few releases with HDRP. There’s a lot of non-obvious ways it can break. I have fortunately been able to figure out why every time I have run into issues so far. The reality is most of the time it is the SRP Team’s fault. Their priorities are confusing, and I feel bad you have to work with them.
Hybrid-instanced properties are absolutely incredible! They just add so much color and personality to the otherwise repetitive worlds.
The one thing that is still broken for me is that the instance data buffer size setting in the editor doesn’t propagate to builds. I have to set it through code in the bootstrap instead.
Something that would be awesome is if we could have a RenderLocalToWorld that if attached to an Entity, is used instead of LocalToWorld. That way I can keep LocalToWorld synced with the simulation state without having to make a backup, and use RenderLocalToWorld for interpolation.
I could go on a rant about the lack of extensibility. But you have already been good about explaining the “early nature” of Hybrid Renderer V2. I’m still trying to figure out when and how I should implement some custom graphics tech that I really want.
Physics
None of the initial issues I pointed out with the design have been addressed. I think this physics works better in the world of MonoBehaviours than Entities. I wonder how many people have memory leaks from runtime blob generation and don’t realize it? There are only a few cases where blobs actually report memory leaks correctly. There are so many side-effects to everything you do, so much digging through messes of events to customize what you need to customize, and so much fragility with system ordering that it just feels like a simulation framework rather than something meant to be used to design gameplay. I’m still watching its development. But I still think it is going to take you a long time to make it “ready for the masses”.
Data Flow Graph
This one is really unfortunate. It is hidden by its dependencies. Most people think it is just some Unity internal solution. People look up the documentation and there is nothing there that makes them believe otherwise. It really suggests it exists for DOTS Animation.
Those people never realize that it has a samples package titled “Guided Tour in Code”. It is one of the best tutorial/documentation works I have ever come across!
Unfortunately, the tour stops short of the ECS integration. From what I can tell, binding entities to it has to be done outside of Burst on the main thread. So it is making it difficult for me to find a good use case for it to stress test it.
But I want to stress test it because the tour is just that hype!
Oh, and IUpdate in simulation nodes is broken, which means a lot of the non-tour examples are broken. You probably want to fix that.
DSP Graph
I have not been able to use it because
-
It doesn’t use Data Flow Graph
-
It doesn’t have very much documentation, so I have no idea how to get data in, when stuff computes, and how to keep things synchronized properly.
-
I haven’t found any examples other than the way-too-advanced-for-me Megacity Demo or DOTS Shooter.
So I am still waiting for DOTS.Audio.
Netcode
Most likely I am being naïve here because I have never gotten a good online multiplayer to work. But I think part of that is because of how I build and reason about my game logic, which never seems to match the models that networking libraries use. Every networking library either provides low-level transport access, or tries to automagically synchronize everything. Really what I am looking for is a library that abstracts client-server connections into a simple API and then lets me dump buffers over those connections of my choosing. And consequently, I can receive those buffers on the other end as well as some metadata about age and whatnot, and then perform my own logic for synchronization.
Am I stupid?
Build
I am currently duplicating a bunch of Build Configuration Settings for Windows, Mac, and Linux. I thought there was supposed to be an override system so that I could share things like the scene list, whether or not Incremental GC was enabled, and all of those other properties. Am I missing something, or is it not working yet?
Tiny
It seems like it is maturing well, but I can’t give much of an opinion on it yet because a lot of my workarounds to Entities ended up using reflection rather than extend the assemblies. Once I fix my framework to work with Tiny, I will probably have a lot more to say about it. WebGL would be a really nice platform to target with my smaller prototypes.
Animation
There’s no samples included with the package, and the GitHub samples are old. I do not feel like reverse-engineering it to figure out how it works. The code is very complex. I am moving on.
Timeline
I don’t think you wanted me to even see this package. The only reason I know about it is because of a GitHub project that referenced it. However, unlike Animation, this one has samples. And they work!
I am going to have to try and use it in a stress-test project before I can really give opinions about it. I suspect that it has some sync point issues that makes it tricky to scale. I’m also not sure how fragile it is. For example, what happens if you destroy an entity through game logic that was instantiated via a track?
One thing that totally surprised me was when I selected certain GameObjects that had Timeline tracks, and suddenly the scene view became a DOTS entities preview that I could scrub through with the Timeline. How are you doing that?!
I must learn this secret power!
Final Thoughts
As I am building up the technology I need to accomplish my goals, one of the issues I run into is prioritizing where to put my effort. The reason this is so difficult is because unless someone drops a hint about “next release” or “soon” on the forums, I have no idea what you are working on.
I’m not asking for videos or roadmaps or posts on the official Unity Blogs. Those all cause problems with expectations. What I am asking is devlogs. Perhaps every two weeks or a month, one team from DOTS writes a little devlog as to what the team is up to. What problems are they running into? What tech are they planning? What are their goals? This would be on rotation, so each time might only provide one to four updates per year, but the community would get a steady stream of new information so that we know DOTS is making progress in many new and exciting areas.
As for all of you who are on these forums reading these posts and answering questions, thank you! You all are awesome. This has been a fun community to be a part of. And I’m excited for what is yet to come!
And lastly, thanks for making to the bottom of this page. Sorry it was so long. I had a lot to say this time.