Consider this situation:
- MyJob lives in a package
- MyJob takes a function pointer or generic struct type to extend its functionality in its update loop logic
- I am in my project using the MyJob package, and the function pointer I want to pass to MyJob would need component data access that MyJob couldn’t have planned for in advance. For example it could want to check if a certain entity has a certain component specific to my project
I’d like this use case to be as simple, straightforward, and not-weird as possible for users that aren’t very DOTS-savvy and wish to use such functionality. (the kind of users that would probably just stick to a simple Entities.ForEach for everything). I do know about ways around this problem currently, but they all feel a bit too complex and scary for beginners
So I’m interested in the possibility of a codegen’d job type that would allow this: I want to be able to create a job that doesn’t necessarily have to know in advance all the component data access it will require. It could just be generated based on whatever component types/accesses it ends up needing in all the places in the codebase where we ask for specific component accesses from this job. This would allow us great flexibility in creating several “variants” of a generic job. Kind of a replacement for what you’d do with virtual function overrides in OOP.
In this code example, the “DoSomethingInJobIteration()” function reads/writes to rotation even though the “MyJob” calling this function was never made aware in its own code that it might need rotation access in its own update loop. MyJob would know which component sets to operate on based only on an EntityQuery:
Basic code example
public static class MyUtilities
{
public static void DoSomethingInJobIteration(in MyJob job)
{
// make the entity rotate
Rotation rotation = job.GetComponent<Rotation>();
RotationSpeed speed = job.GetComponent<RotationSpeed>();
rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), speed.RadiansPerSecond * job.DeltaTime));
job.SetComponent(rotation);
}
}
[BurstCompile]
struct MyJob : IJobEntityGeneric
{
public float DeltaTime;
public void OnUpdateEntity()
{
MyUtilities.DoSomethingInJobIteration(in this);
}
}
With this sort of job API, we could imagine that creating a custom “variant” of a system/job would be as simple as this:
Extending job functionality code example
public class MyCustomSystemVariant : MyBaseSystem<MyCustomLogicHandler>
{ }
public struct MyCustomLogicHandler : IJobCustomLogicHandler
{
// This gets called by the MyBaseJob<T> job in MyBaseSystem<T>
// MyBaseJob also does logic of its own before/after this gets called
public void DoSomethingInJobIteration(ref MyBaseJob<IJobCustomLogicHandler> job)
{
// This function uses Translation access even though no Translation access was declared in MyBaseJob in any way
MoveToTarget moveToTarget = job.GetComponent<MoveToTarget>();
Translation selfTranslation = job.GetComponent<Translation>();
Translation targetTranslation = job.GetComponentOnEntity<Translation>(moveToTarget.TargetEntity);
selfTranslation += math.normalizesafe(targetTranslation - selfTranslation) * moveToTarget.speed;
job.SetComponent(selfTranslation)
}
}
Note: The generated data arrays in this job would either be ComponentTypeHandles, or ComponentDataFromEntity, or [ReadOnly], etc… depending on all of the combined needs of every function in the codebase that uses MyJob.Get/SetComponent(…). If the Base job had write access to Rotation on the current entity in iteration, we would just generate a ComponentTypeHandle and write in chunks. BUT, if one of the extension functions requires read access to RotationFromEntity as well, it would end up generating a [NativeDisableParallelForRestriction] ComponentDataFromEntity instead. Etc…
It would be a very “accessible” option for making jobs I think, despite the drawbacks of manual writeback into component arrays, and not having clear visibility on what types those jobs get access to. We’d basically not have to care anymore about how to obtain access to the data we need in a job, and the end result would be as highly-optimized as doing things manually in an IJobChunk. We’d also have to figure out how to inform users of parallel writing from entity risks and make them manually acknowledge that they understand what they’re doing. Maybe with an attribute over their custom function