Issue with GetComponentDataFromEntity in loop within Job

Hello gang,

I had some code that worked prior to 0.50/0.51 and now is throwing an error:

InvalidOperationException: The ComponentDataFromEntity ER3D_TrafficSystem_LambdaJob_0_Job.JobData.RoadDetailsFromEntity must be marked [ReadOnly] in the job ER3D_TrafficSystem:ER3D_TrafficSystem_LambdaJob_0_Job, because the container itself is marked read only.

Backstory: I am creating a simple car navigation system using the asset Easy Roads 3D (nice asset, GREAT support. HIGHLY recommend it) This asset has road/lane points. My cars/autos have a NativeArray with their current list of points. When the car reaches the end of the current list (at the end of a road) I need to choose which road to take from the Intersection/Connection and change the Auto’s points with the new data

So by a process of elimination the problem occurs on this line:

RoadDetails roadDetails = RoadDetailsFromEntity[roads[i]];

Here is the entire TrafficSystem code:

using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;


public partial class ER3D_TrafficSystem : SystemBase
{
    private NativeArray<Entity> roadEntities;
    private NativeArray<Entity> connectionEntities;
   
    protected override void OnStartRunning()
    {
        EntityQuery allRoadsQuery = GetEntityQuery(
            typeof(ERRoadTag),
            ComponentType.ReadOnly<RoadDetails>(),
            ComponentType.ReadOnly<LanePoints>());


        roadEntities = allRoadsQuery.ToEntityArray(Allocator.Persistent);

        EntityQuery allConnectionsQuery = GetEntityQuery(
            typeof(ERConnectionTag),
            ComponentType.ReadOnly<ConnectionDetails>(),
            ComponentType.ReadOnly<LanePoints>());

        connectionEntities = allConnectionsQuery.ToEntityArray(Allocator.Persistent);
    }


    [BurstCompile]
    protected override void OnUpdate()
    {
        System.Random random = new System.Random();
        uint randomSeed = (uint)random.Next(88, 1000000);
        float deltaTime = Time.DeltaTime;
        float reachedPositionDistance = 2.5f;
        NativeArray<Entity> roads = roadEntities;
        NativeArray<Entity> connections = connectionEntities;
        ComponentDataFromEntity<RoadDetails> RoadDetailsFromEntity = GetComponentDataFromEntity<RoadDetails>(true);

        ComponentDataFromEntity<ConnectionDetails> ConnectionDetailsFromEntity = GetComponentDataFromEntity<ConnectionDetails>(true);
        BufferFromEntity<LanePoints> LanePointsFromEntity = GetBufferFromEntity<LanePoints>(true);


        Entities.WithAll<ERAutoTag>()
            .ForEach((
            DynamicBuffer<AutoLanePoints> autoLanePoints,
            ref AutoDetails autoDetails,
            ref AutoPosition autoPosition,
            ref Translation translation,
            ref Rotation rotation) =>
            {
                var distance = math.distance(autoPosition.Destination, translation.Value);

                if (distance <= reachedPositionDistance)
                {
                    autoPosition.CurrentPositionIndex += 1;
                    if (autoPosition.CurrentPositionIndex >= autoLanePoints.Length)
                    {
                        autoPosition.CurrentPositionIndex = 0; //TEMP, for testing

                        //autoLanePoints.Clear();

                        //int laneIndex = 0;
                        //int roadIdentity = 0;
                        //int connectionIdentityEnd = 0;

                        for (int i = 0; i < roads.Length; i++)
                        {
                            RoadDetails roadDetails = RoadDetailsFromEntity[roads[i]];  //ISSUE HAPPENS HERE

                            //if ((roadDetails.RoadIdentity == autoPosition.RoadIdentity) &&
                            //    (roadDetails.LaneIndex == autoPosition.LaneIndex) &&
                            //    (roadDetails.ConnectionIdentityStart == autoPosition.ConnectionIdentity) &&
                            //    (roadDetails.ConnectionIndexStart == autoPosition.ConnectionIndex)
                            //    )
                            //{
                            //    laneIndex = roadDetails.LaneIndex;
                            //    roadIdentity = roadDetails.RoadIdentity;
                            //    connectionIdentityEnd = roadDetails.ConnectionIdentityEnd;

                            //    var roadLanePointsBuffer = LanePointsFromEntity[roads[i]];
                            //    var roadLanePoints = roadLanePointsBuffer.ToNativeArray(Allocator.Temp);
                            //    var lanePointAuto = new AutoLanePoints();
                            //    for (int pointIndex = 0; pointIndex < roadLanePoints.Length; pointIndex++)
                            //    {
                            //        lanePointAuto.value = roadLanePoints[pointIndex].value;
                            //        autoLanePoints.Add(lanePointAuto);
                            //    }
                            //    break;
                            //}
                        }

                        ////Get the Connection's Lane's points
                        ////First find all options that have the same Idenity and Index. 
                        ////Then we can randomly select one of the routes through the connection
                        //NativeList<Entity> availableConnections = new NativeList<Entity>(Allocator.Temp);

                        //for (int i = 0; i < connections.Length; i++)
                        //{
                        //    var connectionDetails = ConnectionDetailsFromEntity[connections[i]];

                        //    if ((connectionDetails.ConnectionIdentity == connectionIdentityEnd) &&
                        //        (connectionDetails.LaneIndexStart == laneIndex) &&
                        //        (connectionDetails.RoadIdentityStart == roadIdentity) &&
                        //        (connectionDetails.ConnectionIndexStart == autoPosition.ConnectionIndex))
                        //    {
                        //        availableConnections.Add(connections[i]);
                        //    }
                        //}

                        //Unity.Mathematics.Random mathRandom = new Unity.Mathematics.Random(randomSeed);
                        //int randomValue = mathRandom.NextInt(0, availableConnections.Length);
                        //var connectionDetailsNew = ConnectionDetailsFromEntity[availableConnections[randomValue]];
                        //var connectionLanePointsBuffer = LanePointsFromEntity[availableConnections[randomValue]];
                        //var connectionLanePoints = connectionLanePointsBuffer.ToNativeArray(Allocator.Temp);
                        //var lanePoint = new AutoLanePoints();

                        //for (int x = 0; x < connectionLanePoints.Length; x++)
                        //{
                        //    lanePoint.value = connectionLanePoints[x].value;
                        //    autoLanePoints.Add(lanePoint);
                        //}

                        ////Reset the Auto's variables for the next Road/Connection selection
                        //autoPosition.LaneIndex = connectionDetailsNew.LaneIndexEnd;
                        //autoPosition.RoadIdentity = connectionDetailsNew.RoadIdentityEnd;
                        //autoPosition.ConnectionIdentity = connectionDetailsNew.ConnectionIdentity;
                        //autoPosition.ConnectionIndex = connectionDetailsNew.ConnectionIndexEnd;

                        //availableConnections.Dispose();
                    }

                    autoPosition.Destination = autoLanePoints[autoPosition.CurrentPositionIndex].value;
                    autoPosition.Destination.y += autoDetails.yOffset;
                }


                #region Move Auto
                //float3 lookVector = autoPosition.Destination - translation.Value;
                UnityEngine.Vector3 lookVector = autoPosition.Destination - translation.Value;

                //if (!lookVector.Equals(float3.zero))
                //if (math.any(lookVector))
                //if (!lookVector.Equals(UnityEngine.Vector3.zero))
                if (lookVector.sqrMagnitude > 0.01f)
                {
                    UnityEngine.Quaternion rotationLookAt = UnityEngine.Quaternion.LookRotation(lookVector);
                    rotation.Value = rotationLookAt;
                }

                float3 smoothedPosition = math.lerp(translation.Value, autoPosition.Destination, autoDetails.speed * deltaTime);
                translation.Value = smoothedPosition;
                #endregion

            }).Schedule(); 
    }

   
    protected override void OnDestroy()
    {
        roadEntities.Dispose();
        connectionEntities.Dispose();
    }
}

Thanks for any feedback.

You are missing a .WithReadOnly() on your lambda with each read-only CDFE you are trying to use.

Hello again,

I hate when I need to do this, but where EXACTLY does this go? Notice that DynamicBuffer<AutoLanePoints> autoLanePoints, this section needs to be read/write.

Thanks

You can put it before .Schedule() and you need to pass in the CDFE you want to be read-only. You need to repeat call that function in the fluent API chain for each container (in this case CDFE) you want to be read-only.

If you continue to struggle to figure this out, I suggest switching to IJobEntity which will make the issue more obvious and you might even resolve the issue without realizing it.

1 Like

I BELIEVE I got it.

Entities
.WithReadOnly(RoadDetailsFromEntity) //THIS IS THE ITEM THAT WAS ADDED
.WithAll()
.ForEach((
DynamicBuffer autoLanePoints,
ref AutoDetails autoDetails,
ref AutoPosition autoPosition,
ref Translation translation,
ref Rotation rotation) =>
{