Accessing ComponentDataFromEntity by pointer results in undefined.

I am trying to access it through pointer because I am using function pointers. I am getting an error telling me “statesArray” is undefined.

Like this:

[BurstCompile]
[MonoPInvokeCallback( typeof( BehaviourTreeAction ) )]
private unsafe static NodeState TestAction( ref int index , ref AgentData data )
{
    Entity entity = UnsafeUtility.ReadArrayElement<Entity>( data.PtrEntity , index );
    ComponentDataFromEntity<UnitBehaviourState> statesArray = *( ( ComponentDataFromEntity<UnitBehaviourState>* ) data.PtrData );
    UnitBehaviourState state = statesArray[ entity ]; // ERROR IS HERE

    return NodeState.RUNNING;
}

Called like this:

        private NodeState EvaluateLeaf( BehaviourNode node , int index , ref AgentData data )
        {
            return actions[ node.FirstChildIndex ].Invoke( ref index , ref data );
        }

Struct with pointers:

public unsafe struct AgentData
{
    [NativeDisableUnsafePtrRestriction] [NativeDisableParallelForRestriction] public void* PtrEntity;
    [NativeDisableUnsafePtrRestriction] [NativeDisableParallelForRestriction] public void* PtrData;
}

The job:

        [BurstCompile]
        public struct BehaviourTreeJob : IJobParallelFor
        {
            public BehaviourTree Bt;
            [ReadOnly] public NativeArray<Entity> Entities;
            [NativeDisableParallelForRestriction] public ComponentDataFromEntity<UnitBehaviourState> State;
            [NativeDisableParallelForRestriction] public AgentData agentData;

            public void Execute( int index )
            {
                Bt.EvaluateTree( index , ref agentData );
            }
        }

Schedule job:

    private unsafe JobHandle RunBehaviourTree( JobHandle dependency , NativeArray<Entity> frameEntities )
    {
        var state = GetComponentDataFromEntity<UnitBehaviourState>( false );

        var decisionJob = new UnitBehaviourJobs.BehaviourTreeJob
        {
            Bt = bt ,
            Entities = frameEntities ,
            State = state ,
            agentData = new AgentData { PtrEntity = frameEntities.GetUnsafePtr() , PtrData = &state }
        };
        return decisionJob.Schedule( decisionJob.Entities.Length , 32 , dependency );
    }

You haven’t shown how you’re setting data.PtrData

That said, I’ve tried something similar ages ago and could never get it to be stable, though I decided it was a terrible idea and found a much better way to do it so didn’t persist in trying to figure out why it was breaking.

-edit-

actually thinking about it, I think it was because the safety handle never gets set if you pass it in via a pointer.

Sorry, edited my post.

Is there a way around this?

Well for this use case, you could try just pass the ComponentDataFromEntity to the job (which you already do) and get the pointer of it inside the job instead to pass to the function pointer.

1 Like

When I do this:

[BurstCompile]
public struct BehaviourTreeJob : IJobParallelFor
{
    public BehaviourTree Bt;
    [ReadOnly] public NativeArray<Entity> Entities;
    [NativeDisableParallelForRestriction] public ComponentDataFromEntity<UnitBehaviourState> State;

    public unsafe void Execute( int index )
    {
        var agentData = new AgentData
        {
            PtrEntity = Entities.GetUnsafePtr() ,
            PtrData = &State
        };

        Bt.EvaluateTree( index , ref agentData );
    }
}

I get an error saying “You can only take the address of an unfixed expression inside of an fixed statement initializer”.

Edit:

Never-mind, using UnsafeUtility.AddressOf() seems to work.

What would be the reason that addressof operator does not work but UnsafeUtility.AddressOf does?

You are missing the fixed keyword:

public struct Job {
    public State State;
    public unsafe void Execute() {
        fixed (State* ptr = &State) {
            // Your code goes here.
        }
    }
}

You can only get a address without the fixed keywork if is in the stack. In this case the compiler can’t assure that. The reason for this is the GC feature to move objects in memory (defrag) that could invalidate your address. The fixed keyord (GCHandle also have the same feature if you need to store the pointer) tells the GC to not move this object.
Currently the Unity’s GC implementation doesn’t use this feature, so you are “safe” to keep the pointer outside the fixed keyword, but the compiler doesn’t know that. Of course I’m not talking about manual allocations.
UnsafeUtility.AddressOf just bypass this but have the same result. UnsafeUtility also have methods to pin a pointer.

[ ]'s

1 Like