Code generation

I think one direction can go to improve current ECS is code generation. What I mean by that is when u create a ComponentData and code generator generates highly optimized code. Then u can have even simplier API and you can eliminate the needs of Dependency Injection. It will need a standalone code generator that able to call it from IDE. You can get something like this with code generation:

public class AddViewSystem : ReactiveSystem<GameEntity>
{
readonly Transform _viewContainer = new GameObject("Game Views").transform;
readonly GameContext _context;

public AddViewSystem(Contexts contexts) : base(contexts.game)
{
_context = contexts.game;
}

protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
{
return context.CreateCollector(GameMatcher.Sprite);
}

protected override bool Filter(GameEntity entity)
{
return entity.hasSprite && !entity.hasView;
}

protected override void Execute(List<GameEntity> entities)
{
foreach (GameEntity e in entities)
{
GameObject go = new GameObject("Game View");
go.transform.SetParent(_viewContainer, false);
e.AddView(go);
go.Link(e, _context);
}
}
}

Code generation for sure can make everything simpler to write, but then you also don’t know anymore what is going on. (I like the injection but already here I am not sure exactly what’s under the hood and if it will work on mobile, etc) and there is an extra step always…

I think a good api and documentation would be great and keep it in c# land

But on the subject of code generation, I Could actually see a visual system, where only the execute block stays in a referenced c# script

Just my 2 cents

Well, with code generation at least you’re able to see what kind of code it generates and have a better insight on what’s going on.

But regarding what @optimise said IMHO its code doesn’t seem simpler, to be honest, it looks like a different approach and for me, the Dependency Injection is already pretty clear. Of course, as @sngdan said, would be good to know more how this Injection is done under the hood.

Entitas is a third party c# ECS framework (the one whose generated code you have quoted)
It chooses to generates code primarily to make the logic API simpler while still having the performance gain from storing components in an array below the surface…

eg it lets you do
entity.hasPosition; or
entity.SetPosition(x,y);
instead of
entity.componentsArray[positionIndex] == null; or
entity.componentsArray[positionIndex] = new PositionComponent(x,y);

There would be ways of making entity code just as easy to use in logic… but it wouldnt be as fast as Entitas’ underlying array structure.
conclusion: code generation is (useful) workaround.
Entitas optimisations would look like a mess to use without it… so it works well.

The new unity ECS system doesn’t appear to need this in the same way.
The API isn’t very simple… but youll get use to it. It doesn’t look too complex, at least each thing kindof does something.
Also the performance gain here is incredible vs what entitas attempted, the way it stores components in memory seems vastly better than even Entitas’s array abstraction. Not to mention to job system speeding things up in parallel.

I use Entitas extensively in a very complex project, and since I am partway through a project I will carry on…
However I don’t think that code generation to the same level will be necessary in the new unity ECS. / job system.

Since Unity is not only aiming for best possible performance but also make it as easy as MonoBehaviour.Update(), I think that code generation will help to achieve this goal. One of the intuitive example I can give is accessing Animator. Currently when u access the Parameters u created at Animator, u have no way to direct access to it and the best u can do is hashing it from string. With code generation, u can have direct access to it without needing to go through this kind of string to hash lookup. So, it will not only increase API awesomeness but also performance. The same idea applies to Unity ECS too.

Although dependency injection is really convenient but I dun think the performance will reach by writing explicit code.

Oh yeah. @Joachim_Ante_1 , one more thing I want to mention is about the REST server feature that can sync Unity and IDE changes seamlessly. So, IDEs will not have the problem of keep reloading same project file issue anymore when you just create a new C# script file. I see MonoDevelop has been phased out and it’s removed from the roadmap but I wonder whether the new upcoming C# 7 compiler will still have this REST server feature for other IDEs like Visual Studio and Rider? If we have this feature, I think code generation can move to the next level. When you create a ComponentData and save it, your IDE will inform Unity to generate code instantly and u can continue to work on IDE without needing to switch back to Unity to generate and compile code.

Thats provably not true. In fact using in injection for injecting component groups.
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/content/ecs_in_detail.md#injection

is faster than using ComponentGroup API directly:
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/content/ecs_in_detail.md#componentgroup

This is because we can at construction time of the system, cache some critical piece of information that we don’t have to search for (IndexInComponentGroup). It’s not huge and probably not very measurable in real world code beyond some simple benchmarks. But the fact is that the declarative nature of injection code can actually end up giving you better performance.

What it comes down to is if the injection code is efficient. For component group injection for example, we actuallly store the offset in memory relative to the comonentsystem object ptr and patch against it. We don’t go through any reflection API’s at runtime, so there is really no real overhead to just calling a method… It’s just data…

3 Likes

Wow. How it is really work under the hood is not what I imagine. Btw about Animator, is there any plan to make it even more performant without needing to Animator.StringToHash()?