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

Approximating DOTS with GameObjects

Discussion in 'Entity Component System' started by SoftwareGeezers, Dec 5, 2020.

  1. SoftwareGeezers

    SoftwareGeezers

    Joined:
    Jun 22, 2013
    Posts:
    902
    Given the ECS system is a WIP and presently not proven to be usable for a cross-platform mobile+desktop+console multiplayer game in the medium term, what's the closest we can get using the pieces that are already robust?

    The burst compiler and job system are robust, right? During the making of my present game, I cringe at the messy random-access memory - get this info from that object, and that info from this object, and compare them some way. My brain has trouble keeping track of everything and the hardware has trouble accessing everything which is where ECS made so much sense to me.

    Without getting into low-level hacks like writing one's own data representations in binary data heaps, can we manage GameObject structures differently to per-object scripts to reap some of the DOTS rewards? Particularly to batch up memory access and maintain optimised access patterns by processing per workload instead of per object. Do all the 'spawn objects' activity, then all the 'evaluate position' activity, all the 'move object' and then all the 'remove objects' activity?

    For example, how about keeping a list of objects and in a controller script, perform an async job that processes each object to update its position parameters and then in a final step apply these to its transform? Perhaps we'd can use lean structures to represent workloads, so instead of accessing a Transform to update its parameters, we could store a struct for each object

    {
    Vector3 position
    Quaternion rotation
    Vector 3 scale
    }

    ...work on these (obviously not if using physics!), and then batch process all our Transforms in a final job to update each GO with these values?
     
  2. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    158
    Yes, Burst and the new Unity.Mathematics library are both out of preview so they should be safe to use. There was a Unity talk about the copy->process->copy paradigm where you can get quite a speed up just from copying from a managed objects to a burst compatible struct to process and then back again. Even doing extra work, the speed-ups from Burst are substantial enough to make it worth while. That's a first step. And find places you can permanently separate out your data so you have less copying back and forth.

    ECS has GameObject Companion system that does basically what you are describing, and I wouldn't be too afraid to use it. The core ECS is fairly robust, I think, it's the usability and the peripheral systems (Rendering, Audio, Animation, etc.) that need more development. You can add traditional GameObject components to an entity, and they are added to a "shadow" GameObject with a transform update system to keep it in sync with its associated entity. That lets you move certain things into the ECS world while still using the MonoBehaviour components you may be relying on. Moving just a few component types to ECS where you have a lot of data or processing to deal with could be a big win even if everything else is still MonoBehaviour world.

    Both of those are fairly risk-free ways to get the benefit DOTS where you can find a big win while still relying on the relative stability of the MonoBehaviour world.
     
    OldMage and SoftwareGeezers like this.
  3. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    It depends on a lot of factors, but you can indeed improve performance in certain situations by breaking monolithic Update() functions into more focused steps and executing each step on a list of objects at once, even if those objects are not themselves stored in sequential memory. This is specially helpful when you have hundreds of the same type of object running Update() per frame, and makes it possible to do further optimizations like updating only X elements per frame or doing more efficient conditional updates.

    If your functions don't touch Unity's Monobehaviour properties or APIs, you can even execute them in jobs by passing a GCHandle of the object into the job (but it's up to you to ensure thread safety).

    Another technique when you have a large number of objects that need some processing to be done is to assign each object an index into an array of structs which contains only the data needed for that processing. When objects are removed, take the one at the end of the array and give it the removed object's index. It's basically a poor man's targeted ECS.
     
    OldMage likes this.
  4. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,222
    Unless I was getting bit by too many systems eating up the main thread, I would rather use Entities than roll out a custom ECS solution. It is possible to roll out a custom solution without a lot of effort. I have done it, and use it as a live workshop demo to teach the C# job system and Burst. And while it still beats classical GameObjects, it is slower than Entities.

    Anyways, if the real issue is the Hybrid Renderer on Android, then I would rather make my own renderer. The issue with Android devices is that they can have absolutely garbage graphics driver implementations that break batching techniques like the one Hybrid Renderer V2 uses. So probably the most consistent solution would be to create a pool of GameObjects for each presentable type (store the GameObject prefab in a SharedComponent) and then pool these GameObjects, using IJobParallelForTransform to update them after frustum culling. The main downside to this technique is that it can cause motion vector artifacts, but it has the advantage of leveraging the much more stable GameObject rendering. This technique even works with Built-In.

    If your issue is all of the higher-level functionality (animation, physics, audio, rendering, ect), then that's harder to deal with. I haven't been real impressed with them (other than HR V2 on desktop) and have opted to roll out my own solutions where I can easily implement some custom features I have always wanted. I do know that Project Tiny has some simpler (and consequently more sane) implementations of these higher level features.
     
  5. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    My problem with using entities is the wild API changes between Unity versions. If you're doing toy projects or are targeting 2021.1, go for it, but if you're on LTS you will have to tiptoe when looking for documentation and examples.

    Even so, I would use it solely for computational work. Like said before, everything high level like rendering, animation, sound, etc is very experimental and full of holes. The GameObject conversion workflow is, IMO, a gigantic hack I don't even bother with.
     
    Last edited: Dec 6, 2020
  6. SoftwareGeezers

    SoftwareGeezers

    Joined:
    Jun 22, 2013
    Posts:
    902
    Can't believe this, but as part of moving towards a DOTS like system, I've just learnt about the existence of ScriptableObjects from a chance YouTube recommendation! How can I have missed these with years of Unity experience?!

    I've been working on a simple Entity-like system in this little vertical shooter with GO's managed via pools and a manager script. Although not jobified yet as I don't need the performance boost, it looks straight-forward to take these Entity scripts and parrallel-process them in jobs. I'm pretty excited at the possibilities. The only obvious issue is physics-related motion. For what I'm doing, I can disable all the physics and just use manual collider overlap tests, but coordinating transforms via my won pseudo Entities and inbuilt Rigidbodies my be tricky.