Wondering if someone can point me in the direction of how to achieve the following via the new lambda method? Or if it’s not possible? Quite a common use case in my code base. Caution: Pseudo code. Though it really highlights just how much boilerplate the lambda’s can save - nice!
public class TestJCS : JobComponentSystem
{
EntityQuery query;
protected override void OnCreate()
{
query = GetEntityQuery(
new EntityQueryDesc
{
// Can't use 'All' for Translation as 'Any' requires presence of at least one component so use Any instead
Any = new ComponentType[] {
ComponentType.ReadOnly<Translation>(),
ComponentType.ReadOnly<OptionalComponent>()
},
}
);
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
TestJob job = newTestJob()
{
TranslationType = GetArchetypeChunkComponentType<Translation>(),
OptionalComponentType = GetArchetypeChunkComponentType<OptionalComponent>(),
};
job.Schedule(query, inputDeps).Complete();
}
struct TestJob : IJobChunk
{
internal ArchetypeChunkComponentType<Translation> TranslationType;
[ReadOnly] internal ArchetypeChunkComponentType<OptionalComponent> OptionalComponentType;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
NativeArray<Translation> translations = chunk.GetNativeArray(TranslationType);
NativeArray<OptionalComponent> optionalComponents = chunk.GetNativeArray(OptionalComponentType);
bool hasOptionalComponent = chunk.Has(OptionalComponentType);
for (int i = 0; i < chunk.Count; ++i)
{
//if(hasOptionalComponent) optionalComponents[i]...etc
}
}
}
}
// Equivalent Lambda method possible?
public class TestJCS_Lambda : JobComponentSystem
{
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
Entities
.ForEach((ref Translation translation, in OptionalComponent optionalComponent) =>
{
//if(optionalComponent).. ??
// Very verbose body of code - keen to avoid duplication using two ForEach's
}).Schedule(inputDeps);
}
}
}
public class TestJCD_Lambda : JobComponentSystem
{
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var optionalComponentFromEntity = GetComponentDataFromEntity<OptionalComponent>();
return Entities
// If you need to write to the component
//.WithNativeDisableParallelForRestriction(optionalComponentFromEntity)
.ForEach((Entity entity, ref Translation translation) => {
if (optionalComponentFromEntity.Exists(entity))
{
var optionalComponent = optionalComponentFromEntity[entity];
//
}
})
.Schedule(inputDeps);
}
}
TBH chunk iteration will be faster since you can check the component per chunk instead per entity.
struct TestJob : IJobChunk
{
internal ArchetypeChunkComponentType<Translation> TranslationType;
[ReadOnly] internal ArchetypeChunkComponentType<OptionalComponent> OptionalComponentType;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
NativeArray<Translation> translations = chunk.GetNativeArray(TranslationType);
NativeArray<OptionalComponent> optionalComponents = chunk.GetNativeArray(OptionalComponentType);
if (chunk.Has(OptionalComponentType))
{
for (int i = 0; i < chunk.Count; ++i)
{
//optionalComponents[i]...etc
}
else
{
for (int i = 0; i < chunk.Count; ++i)
{
//
}
}
}
}
Sometimes I think that a Chunks.ForEach would be really nice in a JobComponentSystem.
Thanks for the reply elcionap. The reason for the if inside the chunk iteration is because out of a large body of code, the conditional is a one-liner so avoids duplicating (& maintaining) masses of code. Plus the bool is set at the chunk level so we’re only talking one bool comparison per entity. Also thank you for the CDFE idea - obviously was keen to avoid this to get the same performance as longhand. Would be great to know if this is a pattern that may be supported in the future or if I should just stick to the verbose method.
“JobComponentSystem’s Entites.ForEach codegens into an IJobChunk. Both IJobChunk and IJobForEach iterate over an array of chunk pointers assembled by a prepareFilteredChunksList job.”, according to this new thread . So, you should be able to do the same code as above in an Entities.ForEach with same results.
Unfortunately the chunk API required to do this is abstracted away from the lambda. Having a chunk-level lambda would be really cool. I use IJobChunk quite a bit.
Thank you for correcting me, DreamingImLatios. I’m just scratching the surface trying to understand it all. I should be more careful, to not spread false info.
Thank you for the reply. I am for now (as the lambda saves ~100+ loc) but it is in the hot path so it’s a bit of a shame. I take that to mean I shouldn’t expect any feature like this in the future? No problem, just good to know. For now I’ll likely work with the lambda while developing and then profile the difference when written it out in full to see if it’s worth changing.
[For context, I’m building a tween library that supports composition (nesting), looping, hybrid & pure etc so the logic and combination of components can get a little tricky. As this is potentially something a lot of people will use, I’m trying to balance performance vs maintainability.]