FEEDBACK:
Instead of needing to explicitly have fields for each ComponentDataFromEntity you want to to check, allow using entityManager.HasComponent() and .GetComponentData(). This would reduce the amount of boilerplate needed and be more intuitive to DOTS beginners.
Original post is as follows:
given a field declared in IJobParallelFor
[NativeDisableParallelForRestriction, ReadOnly]
public EntityManager em;
trying to call either em.HasComponent() or em.GetComponentData() results in a runtime error:
InvalidOperationException: The Unity.Entities.EntityManager has been declared as [ReadOnly] in the job, but you are writing to it.
Callstack
Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at :0)
Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrow (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at :0)
Unity.Entities.EntityManager.GetCheckedEntityDataAccess () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManager.cs:83)
Unity.Entities.EntityManager.HasComponent[T] (Unity.Entities.Entity entity) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityManagerValidate.cs:44)
MessageSystem+EventMsgParseJob._processMsg (EventMsg& msg) (at Assets/Scripts/Systems/AudioSystem.cs:100)
MessageSystem+EventMsgParseJob.Execute (System.Int32 index) (at Assets/Scripts/Systems/AudioSystem.cs:84)
Unity.Jobs.IJobParallelForExtensions+ParallelForJobStruct`1[T].Execute (T& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at :0)
I am assuming this error is shown as a mistake, as I’m querying, not writing. to EntityManager. In addition, if I use ComponentDataFromEntity that works, where EntityManager does not.
Below is the full example job that shows the problem, errors at runtime on line 45:
private struct EventMsgParseJob : IJobParallelFor
{
[Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndex]
public int nativeThreadIndex;
/// <summary>
/// don't use this reference, but it's included so we tell burst that we do read-write with it so other jobs touching it don't run at the same time.
/// other jobs using it should use the [ReadOnly] attribute.
/// </summary>
public NativeArray<UnsafeList<EventMsg>> threadQueue;
/// <summary>
/// Thread local storge. ptr to NativeArray<UnsafeList<EventMsg>>
/// </summary>
[NativeDisableUnsafePtrRestriction]
public UnsafeList<EventMsg>* p_threadQueue;
[NativeDisableParallelForRestriction, ReadOnly]
public ComponentDataFromEntity<OnKill> onKillData;
[NativeDisableParallelForRestriction]
public NativeQueue<AudioSystem.AudioMessage>.ParallelWriter audioIn;
[NativeDisableParallelForRestriction, ReadOnly]
public EntityManager em;
public unsafe void Execute(int index)
{
//Debug.Log($"EventTest Thread=${nativeThreadIndex}, index={index}");
var p_list = p_threadQueue[index].Ptr;
var count = p_threadQueue[index].length;
for (var i = 0; i < count; i++)
{
_processMsg(ref p_list[i]);
}
if (count != p_threadQueue[index].length)
{
throw new Exception("race, shouldn't happen!");
}
p_threadQueue[index].Clear();
}
private void _processMsg(ref EventMsg msg)
{
switch (msg.type)
{
case EventMsgType.Kill:
if (em.HasComponent<OnKill>(msg.target)) //BUG: this causes runtime "you are writing to it" error
//if(onKillData.HasComponent(msg.target)) //BUG: this works fine.
{
//var onKill = onKillData[msg.target]; //BUG: this works fine.
var onKill = em.GetComponentData<OnKill>(msg.target); //BUG: this causes runtime "you are writing to it" error
audioIn.Enqueue(new AudioSystem.AudioMessage() { type = SystemMessageType.Audio_Sfx, audioFile = onKill.sfxName }); //onKill.
}
break;
}
}
}