It looks like when you call World.GetOrCreateSystem() and pass a generic system type as the Type parameter, Unity will create the system, but it won’t be added to the update loop:
public class GenericSystem<T> : JobComponentSystem
{
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
// do work
}
}
public class OtherSystem : JobComponentSystem
{
private GenericSystem<Foo> fooSystem;
protected override void OnCreate()
{
fooSystem = World.GetOrCreateSystem<GenericSystem<Foo>>();
}
// system continues...
}
In this case, a new instance of GenericSystem will be created, but it’s OnUpdate() method will never be called. It also won’t show up in the Entity Inspector’s system list. I’m guessing it’s not being added to a ComponentSystemGroup.
I don’t expect Unity to spawn instances of generic systems automatically. But if you’re intentionally creating one with a concrete Type parameter, I would expect Unity to know how to update that system.
What can be done about this? Do I need to create and add a GenericSystem manually, in a custom world via an ICustomBootStrap? Will that even work?
I think your alternative is to create a custom world Bootstrap. You can take a look at the NetCode samples, which has an example of it
EDIT:
class MyCustomBootStrap : ICustomBootstrap
{
public bool Initialize(string defaultWorldName)
{
Debug.Log("Executing bootstrap");
var world = new World("Custom world");
World.DefaultGameObjectInjectionWorld = world;
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);
ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
return true;
}
}
(and yes I should’ve read your original post more thoroughly before answering, sorry )
You create your generic systems via code and add them to the “systems” list here. And then do the “DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);” and “ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);”
In that code example, can DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups actually add the systems to Unity’s default world?
I read recently that they may have changed the execution order, such that the default world isn’t available to ICustomBootstraps…Am I recalling that correctly?
AddSystemsToRootLevelSystemGroups creates and injects the systems and system groups into the InitializationSystemGroup, SimulationSystemGroup, and PresentationSystemGroup. You can use World.GetExistingSystem paired with ComponentSystemGroup.AddSystemToUpdateList to inject a system into a group.
Keep in mind that Burst doesn’t work with generic jobs in a build where the instance of the job is created with a generic parameter (the instance must be concrete at compile time).
For future readers: I was concerned this might also be true for job types instantiated from generic systems. But through the Burst Inspector, I confirmed that it’s not. Jobs instantiated from generic systems (even ones which use the system’s generic type arguments) seem to compile with Burst.
Can confirm that generics do not work in Build with latest Burst release. However it was working on previous versions. Even though Burst inspector shows a compiled job the job won’t be Burstable in build.
Jobs being burstable in editor but not build sounds like a problem - like a potential bug. At the least, one would expect the editor to inform the dev that the job won’t be burstable in the build. But what you’d really expect is parity across editor and build for a major feature like this (even if that means jobs aren’t burstable in the editor). What is the use case for the way it is now?
Does anyone know a reason why they would intentionally want jobs to be burstable in editor but in builds? Maybe it does inform you, and I’m not looking in the right place?
It isn’t a bug nor a design decision. It ultimately has to do with the fact that in the Editor, Burst jobs are compiled in JIT fashion, whereas in a build, they are compiled AOT. Currently, the method they use to discover jobs to be compiled AOT doesn’t handle generic jobs with generic arguments. That is something they have suggested they will implement in the future (sometime in 2020 was the estimate I got) but it requires a more powerful discovery tool than the one they have for Burst. You can trick the system by having concrete instances of the generic systems declared somewhere in your code that doesn’t get stripped.
AOT was working before with explicitly typed jobs somewhere in code. Examlpe. public static readonly IJob Job; that would be visible to burst inspector and compiled in build.
So all in all, I think the cleanest approach may be to just codegen your concrete classes (as in Post #2 ) if you are dealing with a mass number of them. Certainly will save a lot of headaches down the line! Unless ofc those classes cannot reasonably be determined at compile time (doesn’t seem like your case though). @PublicEnumE
Out of curiosity do you have a preferred method to codegen things? I have a ton of things where codegen is the seemingly perfect answer for, but I haven’t figured out a quick and clean way to do codegen without modifying the Entities package.
For me, simply using attributes+reflection to find out what needs codegen, and then writing a simple script that generates a text file usually does the trick. You can also make your own template system by writing a code text file manually and writing like $$$$MY_TYPE_NAME$$$$ in places where your codegen script should be replacing stuff. I try to avoid using third party solutions for as long as I can for this sort of thing. I like keeping things ultra simple.
Out of curiosity, what sort of codegen scenario requires you to modify the Entities package?
So for these cases, you don’t really need to use a template engine like T4. You can just process them in
OnPostprocessAllAssets or on-demand in your own editor tooling. Also I may add that even if you need control flow within the “template”, you can just cleanly use ternary operators and $ string interpolations in your logic and still forego a full-fledged template engine.
The use case that most closely relates to the this thread’s topic is I have an API that defines new interface types that users can tack onto their custom types. I want to create two tag components for each type the user defines that implements those interfaces. I do not have the ability to depend on the user’s assemblies nor do I want to trash any file the user may define. Currently I am trying to use reflection to add types into the TypeManager at runtime.
However, most of my codegen use cases involve extending the lambda codegen with custom types. Specifically, I want to be able to build EntityQueries and I want to be able to convert a lambda into a callback from inside a job with access to captured variables.