The ComponentTypeHandle<...> has been declared as [WriteOnly] in the job, but you are reading from it

I’m trying to create a very simple gravity simulation using ECS and Jobs to gain performance, and after some trial and error i finally got to this code:

using System;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics.Aspects;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using static UnityEngine.InputManagerEntry;
using UnityEngine.UIElements;
using System.Drawing.Printing;

[RequireMatchingQueriesForUpdate]
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(PhysicsSystemGroup))]
public partial struct PhysicsSystem : ISystem, IDisposable
{
    NativeList<RigidBodyAspect> physics;
    NativeList<PlanetECS> planets;
    NativeList<LocalTransform> positions;

    public void Dispose()
    {
        Debug.Log("Disposing");
        if (physics.IsCreated)
        {
            physics.Dispose();
            planets.Dispose();
            positions.Dispose();
        }
    }

    public void OnCreate(ref SystemState state)
    {
        physics = new NativeList<RigidBodyAspect>(Allocator.Persistent);
        planets = new NativeList<PlanetECS>(Allocator.Persistent);
        positions = new NativeList<LocalTransform>(Allocator.Persistent);
    }

    public void OnDestroy(ref SystemState state)
    {
        Dispose();
    }

    public void OnUpdate(ref SystemState state)
    {
        if (physics.Length == 0)
        {
            GetPhysicalObjects(ref state);
        }
        else
        {
            var gravityJob = new ApplyGravityJob()
            {
                planets = planets.AsArray(),
                positions = positions.AsArray(),
                physicalBodies = physics.AsArray(),
                gravitationalConstant = 5,
                distancePower = 1.5f,
                DeltaTime = SystemAPI.Time.DeltaTime
            };

            JobHandle gravityJobHandle = gravityJob.Schedule(physics.Length, 1);
            gravityJobHandle.Complete();
        }
    }

    struct ApplyGravityJob : IJobParallelFor
    {
        [ReadOnly] 
        public NativeArray<PlanetECS> planets;
        [ReadOnly]
        public NativeArray<LocalTransform> positions;

        public NativeArray<RigidBodyAspect> physicalBodies;

        public float gravitationalConstant;
        public float distancePower;
        public float DeltaTime;

        public void Execute(int i)
        {
            for (int j = 0; j < physicalBodies.Length; j++)
            {
                if (i == j) { continue; }
                float3 dir = (positions[j].Position - positions[i].Position);
                float dist = math.length(dir);
                math.normalize(dir);
                float invDist = 1.0f / math.pow(dist, distancePower);
                float3 gravityAcceleration = (gravitationalConstant *
                planets[j].body.gravitationalMass) * invDist * dir;
                
                physicalBodies[i] = new RigidBodyAspect { LinearVelocity = (physicalBodies[i].LinearVelocity + gravityAcceleration * DeltaTime) };
            }
        }
    }
        
    void GetPhysicalObjects(ref SystemState state)
    {
        foreach(RigidBodyAspect rb in SystemAPI.Query<RigidBodyAspect>())
        {
            PlanetECS planet;
            LocalTransform pos;
            state.GetComponentLookup<PlanetECS>().TryGetComponent(rb.Entity, out planet);
            state.GetComponentLookup<LocalTransform>().TryGetComponent(rb.Entity, out pos);
            positions.Add(pos);
            planets.Add(planet);
            physics.Add(rb);
        }
    }
}

But when i try to run it (using a subscene and two elements with PhysicsBody and PhysicsShape components) i get the following error:

InvalidOperationException: The ComponentTypeHandle<Unity.Physics.PhysicsVelocity> has been declared as [WriteOnly] in the job, but you are reading from it.
Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <b0fc6facff52490f8c5788181f70c5cc>:0)
Unity.Entities.RefRW`1[T].get_ValueRO () (at ./Library/PackageCache/com.unity.entities@1.0.8/Unity.Entities/Iterators/RefRW.cs:147)
Unity.Physics.Aspects.RigidBodyAspect.get_LinearVelocity () (at ./Library/PackageCache/com.unity.physics@1.0.0-pre.65/Unity.Physics/ECS/Base/Aspects/RigidBodyAspect.cs:175)
PhysicsSystem+ApplyGravityJob.Execute (System.Int32 i) (at Assets/Scenes/DOTSTesting/DOTS_SCRIPTS/PhysicsSystem.cs:95)
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 <b0fc6facff52490f8c5788181f70c5cc>:0)

After many trial and error i haven’t achieved to get anything close to a solution, if anyone has an idea of what could be happening…

Here i’ll share an overview of the different Components i used:

using Unity.Entities;

public struct PlanetECS : IComponentData
{
    public CosmicalEntity body;
    public float atmosphereRadius;
}
using Unity.Entities;
using UnityEngine;
    
[SerializeField]
public struct CosmicalEntity : IComponentData
{
    public Entity orbitationalParent;
    public float gravitationalMass;    
}
using System.Security;
using Unity.Entities;
using UnityEngine;

public class PlanetAuthoringç : MonoBehaviour
{
    public PlanetECS planet;    
}
    
[SecuritySafeCritical]
class PlanetAutoringçBaker : Baker<PlanetAuthoringç>
{    
    public override void Bake(PlanetAuthoringç authoring)
    {
        var component = new PlanetECS
        {
            body = new CosmicalEntity
            {
                orbitationalParent = Entity.Null,
                gravitationalMass = 100
            },
            atmosphereRadius = 5
        };
    
        var entity = GetEntity(TransformUsageFlags.Dynamic);
        AddComponent(entity, component);
    }
}

@Paraxodon - You’re getting the error because you’re trying to read from a [WriteOnly] component in your job. Here, the component is RigidBodyAspect, which you’re trying to access in the ApplyGravityJob job. You’re reading from it to get the LinearVelocity property, which isn’t allowed because it’s been declared as [WriteOnly].

You can solve this by changing your code to allow reading from the RigidBodyAspect component. You’ll have to remove the [WriteOnly] declaration from the RigidBodyAspect component in your job and replace it with [NativeDisableParallelForRestriction] which allows read/write access.

Modify your ApplyGravityJob to look like this:

struct ApplyGravityJob : IJobParallelFor
{
    [ReadOnly] 
    public NativeArray<PlanetECS> planets;
    [ReadOnly]
    public NativeArray<LocalTransform> positions;
    [NativeDisableParallelForRestriction]
    public NativeArray<RigidBodyAspect> physicalBodies;
    public float gravitationalConstant; 
    public float distancePower; 
    public float DeltaTime;
    public void Execute(int i)
    {
        for (int j = 0; j < physicalBodies.Length; j++)
        {
            if (i == j) { continue; }
            float3 dir = (positions[j].Position - positions[i].Position);
            float dist = math.length(dir);
            math.normalize(dir);
            float invDist = 1.0f / math.pow(dist, distancePower);
            float3 gravityAcceleration = (gravitationalConstant *
            planets[j].body.gravitationalMass) * invDist;
            
            physicalBodies[i] = new RigidBodyAspect { LinearVelocity = (physicalBodies[i].LinearVelocity + gravityAcceleration * DeltaTime) };
        }
    }
}

Please remember that [NativeDisableParallelForRestriction] (link below) disables the safety system that prevents race conditions, so you must be very careful when using it. Ensure that your code does not cause race conditions, otherwise, you’ll get incorrect results.

https://forum.unity.com/threads/nativedisableparallelforrestriction-and-burst-performance.908513/

Don’t hesitate to come back if you have more questions. Happy coding!