Hello.
Is it possible to call methods in parallel?
Tried to make them private or even add atribute for aggressive method inlining but have no success yet.
Hello.
Is it possible to call methods in parallel?
Tried to make them private or even add atribute for aggressive method inlining but have no success yet.
Don’t understand. Do you mean create a job ?
Yes.
Please check samples
https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/ECSSamples/Assets
You use methods as normal.
Here is an example of boids, with method inside a IJob.
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/Advanced/Boids/Scripts/BoidSystem.cs
Thank you. I see that all I need is to avoid capture “this” reference, so the methods which I want to call from a parallel should be pure and static. But, here is an example where I tried a lot but still don’t know how to force it work in parallel (in single thread by Run(); it works):
using Components;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using Unity.Physics.Systems;
namespace Systems
{
public partial class RaycastHitSystem : SystemBase
{
private BuildPhysicsWorld _physicsWorld;
protected override void OnCreate()
{
_physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
}
protected override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.E))
{
Entities
.WithAll<CameraComponent>()
.ForEach((Entity entity, int entityInQueryIndex,
Translation translation, LocalToWorld ltw) =>
{
var targetEntity = Raycast(translation.Value,
translation.Value + ltw.Forward * 100);
if (targetEntity == Entity.Null)
return;
Debug.LogError(targetEntity);
})
.WithBurst()
.ScheduleParallel();
}
}
private static Entity Raycast(float3 start, float3 end)
{
var physicsWorld = World.DefaultGameObjectInjectionWorld
.GetOrCreateSystem<BuildPhysicsWorld>();
var collisionWorld = physicsWorld.PhysicsWorld.CollisionWorld;
var raycastInput = new RaycastInput
{
Start = start,
End = end,
Filter = new CollisionFilter
{
BelongsTo = ~0u,
CollidesWith = ~0u,
GroupIndex = 0
}
};
return collisionWorld.CastRay(raycastInput, out var raycastHit) ?
raycastHit.Entity : Entity.Null;
}
}
}
Error:
InvalidOperationException: The Unity.Collections.NativeArray`1[Unity.Physics.BoundingVolumeHierarchy+Node] has been declared as [WriteOnly] in the job, but you are reading from it.
Also tried to do not make Raycast method static to use BuildPhysicsWorld cached in current system but in that case i receiving another error because reference type will be cached by lambda.
Possible solution I see is using RaycastCommand, but firstly I need to be sure that the example above is completely wrong and could not be fixed by the simple way.
Method calls are a pain in Entities.ForEach. They are much easier in struct jobs as you don’t have to do anything really to make them work. For Burst static methods which are compiled to function pointers only simple types work. I’m pretty sure float3 won’t work as input parameter but someone can correct me on that.
That said you have several issues in your code:
Translation translation, LocalToWorld ltw
Set in or ref dependent on if you’re writing. Don’t just leave it blank. In your case:
in Translation translation, in LocalToWorld ltw
Getting a managed system in a ForEach doesn’t work for a bursted job. Capture the var collisionWorld = physicsWorld.PhysicsWorld.CollisionWorld;
outside the ForEach but inside the OnUpdate. It’s a struct and can be safely used as parameter for burst jobs.
As you are using Physics. Any System that requires a component that Physics writes to and you’re reading from needs to be registered in your system so the Dependencies are correct. Easily done with:
protected override void OnStartRunning()
{
this.RegisterPhysicsRuntimeSystemReadOnly();
}
Thank you. Code changed to this:
using Components;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using Unity.Physics.Systems;
namespace Systems
{
public partial class RaycastHitSystem : SystemBase
{
private BuildPhysicsWorld _physicsWorld;
protected override void OnCreate()
{
_physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
}
protected override void OnStartRunning()
{
this.RegisterPhysicsRuntimeSystemReadOnly();
}
protected override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.E))
{
var collisionWorld = _physicsWorld.PhysicsWorld.CollisionWorld;
Entities
.WithAll<CameraComponent>()
.ForEach((Entity entity, int entityInQueryIndex,
in Translation translation, in LocalToWorld ltw) =>
{
var targetEntity = Raycast(
collisionWorld,
translation.Value,
translation.Value + ltw.Forward * 100);
if (targetEntity == Entity.Null)
return;
Debug.LogError(targetEntity);
})
.WithBurst()
.ScheduleParallel();
}
}
private static Entity Raycast(CollisionWorld collisionWorld, float3 start, float3 end)
{
var raycastInput = new RaycastInput
{
Start = start,
End = end,
Filter = new CollisionFilter
{
BelongsTo = ~0u,
CollidesWith = ~0u,
GroupIndex = 0
}
};
return collisionWorld.CastRay(raycastInput, out var raycastHit) ?
raycastHit.Entity : Entity.Null;
}
}
}
And still get an error:
InvalidOperationException: RaycastHitSystem_LambdaJob_0_Job.JobData.collisionWorld.EntityBodyIndexMap is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
I saw a similar error when I tried to create structural changes using EntityManager instead of
EndSimulationEntityCommandBufferSystem but what is wrong now?
I suggest using IJobEntity. with the methods in the Job. Then it will work without any problems, my own code does that. Anything in Systems should be treated single threaded both architecturally and functionally.
So then the system is for any single thread prep → kick off the job with a ScheduleParallel.
That’s one of the reasons I’m a big fan of IJobEntity, because I’m separating it like that and the program flow is extremely clear.
You don’t need. WithBurst in foreach.
ForEach by default is bursted, unless otherwise specified, i.e. using WithoutBurst.
Using Debug with variable inside for each, will disable burst for that ForEach.
collisionWorld need be passed into the job with ReadOnly.
Also, use IN inside method for collisionWorld
On the side note. Try first without method. Extract all from the method. Make code work. Then create method. You dealing with multiple issues, but method is least of it.
First of all i’m very thankful for responses, but still have no success =(
using Components;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using Unity.Physics.Systems;
namespace Systems
{
public partial class RaycastHitSystem : SystemBase
{
private BuildPhysicsWorld _physicsWorld;
protected override void OnCreate()
{
_physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
}
protected override void OnStartRunning()
{
this.RegisterPhysicsRuntimeSystemReadOnly();
}
protected override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.E))
{
var collisionWorld = _physicsWorld.PhysicsWorld.CollisionWorld;
Entities
.WithAll<CameraComponent>()
.ForEach((Entity entity, int entityInQueryIndex,
in Translation translation, in LocalToWorld ltw) =>
{
var raycastStart = translation.Value;
var raycastEnd = translation.Value + ltw.Forward * 100;
var raycastInput = new RaycastInput
{
Start = raycastStart,
End = raycastEnd,
Filter = new CollisionFilter
{
BelongsTo = ~0u,
CollidesWith = ~0u,
GroupIndex = 0
}
};
if (collisionWorld.CastRay(raycastInput, out var raycastHit))
{
//Debug.LogError(raycastHit.Entity);
}
})
.ScheduleParallel();
}
}
Error: InvalidOperationException: RaycastHitSystem_LambdaJob_0_Job.JobData.collisionWorld.EntityBodyIndexMap is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
How to mark collision world as readonly in this code:
var collisionWorld = _physicsWorld.PhysicsWorld.CollisionWorld;
public partial struct EntitiesRaycastJob : IJobEntity
{
[ReadOnly] public CollisionWorld collisionWorld;
private void Execute(in Translation translation, in LocalToWorld ltw)
{
var targetEntity = Raycast(
collisionWorld,
translation.Value,
translation.Value + ltw.Forward * 100);
if (targetEntity == Entity.Null)
return;
Debug.LogError(targetEntity);
}
This code actually works, and I will use it in case if there is no another way with pure SystemBase inherited system.
As far as I know IJobEntity is a little higher performance than the naïve entity job approach as well as it’s operating on chunks.
I think you’re missing:
Entities
.WithReadOnly(collisionWorld)
I haven’t tested personally, but as far I remember from while ago, it was mentioned, that Entities.ForEach is just a wrapper to IJobChunks, with less boilerplate. So for the equivalent simplistic code, these two should be the same in terms of performance.
The difference comes with job complexity, where IJobChunks potentially gain benefits.
Great! Many thanks!
Exactly what I’ve looked for. In similar situations now will know what to try.
It’s not. Using Debug with or without variables in ForEach wouldn’t disable Burst. Simple proof:
Did you try without ? But yes, using is one way to keep burst enabled for foreach with Debug.
Actually the major issue with debug and burst is, when using + inside it, constructing new string.
But unless my memory fails, there was some odd cases, where debug log didn’t like strings, even if used $. But I won’t put the bet on it now.
I do actually wonder, if burst packages become more torelable with debug logs over time? Probably could be back tracked, but is not that important.