using Unity.Entities;
using Unity.Jobs;
using Unity.Collections;
using Unity.Mathematics;
public class HashTestSystem : JobComponentSystem
{
[ReadOnly] public NativeHashMap<int3, Entity> HashMap;
protected override void OnCreate()
{
var e = EntityManager.CreateEntity(typeof(HashtableTest));
HashMap = new Unity.Collections.NativeHashMap<Unity.Mathematics.int3, Entity>(10000, Allocator.Persistent);
for (int x = 0; x < 100; x++)
{
for (int y = 0; y < 100; y++)
{
for (int z = 0; z < 100; z++)
{
HashMap.Add(new int3(x, y, z), e);
}
}
}
}
protected override JobHandle OnUpdate(JobHandle handle)
{
var hm = HashMap;
Entities.ForEach((ref HashtableTest t) =>
{
for (int x = 0; x < 1; x++)
{
for (int y = 0; y < 1000; y++)
{
for (int z = 0; z < 1000; z++)
{
t.b = hm.TryGetValue(new int3(x, y, z), out var e);
}
}
}
}).WithName("HASH_TEST").Schedule(handle);
return handle;
}
}
public struct HashtableTest : IComponentData
{
public bool b;
}
Getting error: InvalidOperationException: <>c__DisplayClass_HASH_TEST.Data.hm is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
As you can see I’m not writing to the container.
The job activates a write mode for all captured containers unless you add .WithReadOnly(hm), or wait for ScheduleSingle support for ForEach and let the write mode on for hash map.
[ReadOnly] you put doesn’t work because it isn’t part of internal job fields. (imagine public NativeHashMap<int3, Entity> hm; declared automatically which connects with whats captured to lambda, without [ReadOnly]) If you have a custom struct with NativeHashMap inside then you put [ReadOnly] inside this struct then capturing [ReadOnly] works.
var matrices = GetComponentDataFromEntity<LocalToWorld>(true);
It is explicitly read only. Shouldn’t it just work? It throws the same error message.
Might be different error. It complains that the container is not ReadOnly.
protected override JobHandle OnUpdate(JobHandle handle)
{
var matrices = GetComponentDataFromEntity<LocalToWorld>(true);
handle = Entities.ForEach((ref Translation translation, in FollowHorizontalPositopn transform) =>
{
var pos = matrices[transform.Entity].Position;
translation.Value = new float3(pos.x, translation.Value.y, pos.z);
}).Schedule(handle);
return handle;
}
InvalidOperationException: <>c__DisplayClass_OnUpdate_LambdaJob0.Data.matrices is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
var matrices = GetComponentDataFromEntity<LocalToWorld>(true);
handle = Entities.
WithReadOnly(matrices).
WithNativeDisableParallelForRestriction(matrices).
ForEach((ref Translation translation, in FollowHorizontalPositopn transform) =>
{
var pos = matrices[transform.Entity].Position;
translation.Value = new float3(pos.x, translation.Value.y, pos.z);
}).Schedule(handle);
The safety system would complain if you tried to use CDFE in IJobForeach too, you would need to either ScheduleSingle or turn off the parallel restrictions.
Edit: Actually you shouldn’t even need the NativeDisableParallelForRestriction call as long as you’re using WithReadOnly, it should support parallel reading.
Internally GetComponentDataFromEntity uses an entity query to initialize entity access. The query needs to know whether it’s accessing as read-only or not. You still need to tell any jobs you pass it to that you’re explicitly accessing it as read-only.
Nop. You always had to declare permissions in the job, manually created or not, even if the container already has any permission already. If you don’t, the permission will be the default (write) and your job will not run parallel with another job reading the same component.
To mark the container as readonly for safe checks only and to track internal dependencies in the ECS. For example, calling Complete to dependencies of a ComponentSystem or a JobComponentSystem with AlwaysSynchronizeSystem.
public struct BufferData : IBufferElementData
{
public float Value;
}
public class BufferLookupSystem : SystemBase
{
protected override void OnUpdate()
{
BufferFromEntity<BufferData> buffersOfAllEntities
= this.GetBufferFromEntity<BufferData>(true);
Entities
.ForEach((ref Rotation orientation,
in LocalToWorld transform,
in Target target) =>
{
// Check to make sure the target Entity still exists
if (!buffersOfAllEntities.Exists(target.entity))
return;
// Get a reference to the buffer
DynamicBuffer<BufferData> bufferOfOneEntity =
buffersOfAllEntities[target.entity];
// Use the data in the buffer
float avg = 0;
for (var i = 0; i < bufferOfOneEntity.Length; i++)
{
avg += bufferOfOneEntity[i].Value;
}
if (bufferOfOneEntity.Length > 0)
avg /= bufferOfOneEntity.Length;
})
.ScheduleParallel();
}
}