I have this existing system that uses a Transform to transform the vertices of a sprite.
public class TransformGameObjectSpriteVerticesSystem : JobComponentSystem {
private EntityQuery query;
[BurstCompile]
private struct OldJob : IJobParallelForTransform {
public ComponentDataArray<Sprite> sprites;
public void Execute(int index, TransformAccess transform) {
Sprite sprite = this.sprites[index];
float4x4 rotationTranslationMatrix = new float4x4(transform.rotation, transform.position);
float4x4 scaleMatrix = float4x4.Scale(transform.localScale);
float4x4 matrix = math.mul(rotationTranslationMatrix, scaleMatrix);
sprite.Transform(ref matrix);
this.sprites[index] = sprite; // Modify the data
}
}
protected override void OnCreateManager() {
// All entities with Sprite and Transform, but without Static (non Static sprites)
this.query = GetEntityQuery(typeof(Sprite), typeof(Transform), ComponentType.Exclude<Static>());
}
protected override JobHandle OnUpdate(JobHandle inputDeps) {
OldJob oldJob = new OldJob() {
sprites = this.query.GetComponentDataArray<Sprite>()
};
return oldJob.Schedule(this.query.GetTransformAccessArray(), inputDeps);
}
}
Now that ComponentDataArray is removed, how do I do something like this now?
ToComponentDataArray → job → FromComponentDataArray
Sorry, I don’t get it. Mind explaining it?
This was posted yesterday on discord, I guess it does what you want
struct AddForces : IJobForEachWithEntity<PhysicsVelocity, PhysicsMass>
{
[ReadOnly][DeallocateOnJobCompletion] public NativeArray<Impulse> Impulses;
public void Execute(Entity entity, int index, ref PhysicsVelocity c0, ref PhysicsMass c1) {
for (int i = 0; i < Impulses.Length; i++) {
if (Impulses[i].Target.Equals(entity)) {
c0.Linear += Impulses[i].Force / c1.InverseMass;
}
}
}
}
//OnCreate
m_Impulses = GetEntityQuery(typeof(Impulse));
//OnUpdate
var job = new AddForces
{
Impulses = m_Impulses.ToComponentDataArray<Impulse>(Allocator.TempJob)
};
You call ToComponentDataArray on an EntityQuery to get a NativeArray of components to pass to the job. But those are a copy, so when the job is done you need to copy the array back using CopyFromComponentDataArray on the same EntityQuery. Both copies are on the main thread and the second one will I’m pretty sure force a sync point.
Basically don’t use IJobParallelForTransform for anything but writing to the transform. For transform read access it’s going to be better using GetTransformAccessArray and then pass that to say an IJobForEachWithEntity so you have the index into the transform array. That will avoid some extra copying and syncing.
2 Likes
I tried this but I’m getting this error. Even adding the mentioned attribute did not work.
InvalidOperationException: Job.Data.transforms.m_TransformArray uses unsafe Pointers which is not allowed. Unsafe Pointers can lead to crashes and no safety against race conditions can be provided.
If you really need to use unsafe pointers, you can disable this check using [NativeDisableUnsafePtrRestriction].
[BurstCompile]
private struct Job : IJobForEachWithEntity<Sprite> {
[NativeDisableUnsafePtrRestriction]
public TransformAccessArray transforms;
public void Execute(Entity entity, int index, ref Sprite sprite) {
Transform transform = this.transforms[index];
float4x4 rotationTranslationMatrix = new float4x4(transform.rotation, transform.position);
float4x4 scaleMatrix = float4x4.Scale(transform.localScale);
float4x4 matrix = math.mul(rotationTranslationMatrix, scaleMatrix);
sprite.Transform(ref matrix);
}
}
Maybe because it’s using Transform and not TransformAccess. However, I can’t find any accessor of TransformAccess from TransformAccessArray.
This works but it’s not multithreaded. Still better than nothing:
protected override void OnUpdate() {
NativeArray<Sprite> sprites = this.query.ToComponentDataArray<Sprite>(Allocator.TempJob);
TransformJob transformJob = new TransformJob() {
sprites = sprites
};
transformJob.Schedule(this.query.GetTransformAccessArray()).Complete();
this.query.CopyFromComponentDataArray(sprites);
sprites.Dispose();
}
[BurstCompile]
private struct TransformJob : IJobParallelForTransform {
public NativeArray<Sprite> sprites;
public void Execute(int index, TransformAccess transform) {
Sprite sprite = this.sprites[index];
float4x4 rotationTranslationMatrix = new float4x4(transform.rotation, transform.position);
float4x4 scaleMatrix = float4x4.Scale(transform.localScale);
float4x4 matrix = math.mul(rotationTranslationMatrix, scaleMatrix);
sprite.Transform(ref matrix);
this.sprites[index] = sprite; // Modify the data
}
}
Look at the out JobHandle version of ToComponentDataArray/CopyFromComponentDataArray. You can use those, combine them and chain the entire thing.
I’m not doing a copy back in my case, but this is from a working example that illustrates what I mean.
var proxies = TransformGroup.ToComponentDataArray<CombatEntityProxy>(Allocator.TempJob, out JobHandle proxyHandle);
var steerings = TransformGroup.ToComponentDataArray<AgentSteering>(Allocator.TempJob, out JobHandle steeringHandle);
inputDeps = JobHandle.CombineDependencies(inputDeps, proxyHandle, steeringHandle);
var transforms = TransformGroup.GetTransformAccessArray();
CombatProxyTransformSyncJob tjob = new CombatProxyTransformSyncJob
{
DeltaTime = Time.deltaTime,
Time = Time.time,
Proxies = proxies,
AgentSteerings = steerings,
};
inputDeps = tjob.Schedule(transforms, inputDeps);
1 Like