Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Accessing ComponentDataFromEntity by pointer results in undefined.

Discussion in 'Entity Component System' started by MintTree117, May 5, 2021.

  1. MintTree117

    MintTree117

    Joined:
    Dec 2, 2018
    Posts:
    340
    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:

    Code (CSharp):
    1. [BurstCompile]
    2. [MonoPInvokeCallback( typeof( BehaviourTreeAction ) )]
    3. private unsafe static NodeState TestAction( ref int index , ref AgentData data )
    4. {
    5.     Entity entity = UnsafeUtility.ReadArrayElement<Entity>( data.PtrEntity , index );
    6.     ComponentDataFromEntity<UnitBehaviourState> statesArray = *( ( ComponentDataFromEntity<UnitBehaviourState>* ) data.PtrData );
    7.     UnitBehaviourState state = statesArray[ entity ]; // ERROR IS HERE
    8.  
    9.     return NodeState.RUNNING;
    10. }
    Called like this:

    Code (CSharp):
    1.         private NodeState EvaluateLeaf( BehaviourNode node , int index , ref AgentData data )
    2.         {
    3.             return actions[ node.FirstChildIndex ].Invoke( ref index , ref data );
    4.         }
    Struct with pointers:

    Code (CSharp):
    1. public unsafe struct AgentData
    2. {
    3.     [NativeDisableUnsafePtrRestriction] [NativeDisableParallelForRestriction] public void* PtrEntity;
    4.     [NativeDisableUnsafePtrRestriction] [NativeDisableParallelForRestriction] public void* PtrData;
    5. }
    The job:

    Code (CSharp):
    1.         [BurstCompile]
    2.         public struct BehaviourTreeJob : IJobParallelFor
    3.         {
    4.             public BehaviourTree Bt;
    5.             [ReadOnly] public NativeArray<Entity> Entities;
    6.             [NativeDisableParallelForRestriction] public ComponentDataFromEntity<UnitBehaviourState> State;
    7.             [NativeDisableParallelForRestriction] public AgentData agentData;
    8.  
    9.             public void Execute( int index )
    10.             {
    11.                 Bt.EvaluateTree( index , ref agentData );
    12.             }
    13.         }
    Schedule job:

    Code (CSharp):
    1.     private unsafe JobHandle RunBehaviourTree( JobHandle dependency , NativeArray<Entity> frameEntities )
    2.     {
    3.         var state = GetComponentDataFromEntity<UnitBehaviourState>( false );
    4.  
    5.         var decisionJob = new UnitBehaviourJobs.BehaviourTreeJob
    6.         {
    7.             Bt = bt ,
    8.             Entities = frameEntities ,
    9.             State = state ,
    10.             agentData = new AgentData { PtrEntity = frameEntities.GetUnsafePtr() , PtrData = &state }
    11.         };
    12.         return decisionJob.Schedule( decisionJob.Entities.Length , 32 , dependency );
    13.     }
     
    Last edited: May 5, 2021
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    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.
     
    Last edited: May 5, 2021
  3. MintTree117

    MintTree117

    Joined:
    Dec 2, 2018
    Posts:
    340
    Sorry, edited my post.

    Is there a way around this?
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    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.
     
    MintTree117 likes this.
  5. MintTree117

    MintTree117

    Joined:
    Dec 2, 2018
    Posts:
    340
    When I do this:
    Code (CSharp):
    1. [BurstCompile]
    2. public struct BehaviourTreeJob : IJobParallelFor
    3. {
    4.     public BehaviourTree Bt;
    5.     [ReadOnly] public NativeArray<Entity> Entities;
    6.     [NativeDisableParallelForRestriction] public ComponentDataFromEntity<UnitBehaviourState> State;
    7.  
    8.     public unsafe void Execute( int index )
    9.     {
    10.         var agentData = new AgentData
    11.         {
    12.             PtrEntity = Entities.GetUnsafePtr() ,
    13.             PtrData = &State
    14.         };
    15.  
    16.         Bt.EvaluateTree( index , ref agentData );
    17.     }
    18. }
    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.
     
    Last edited: May 5, 2021
  6. MintTree117

    MintTree117

    Joined:
    Dec 2, 2018
    Posts:
    340
    What would be the reason that addressof operator does not work but UnsafeUtility.AddressOf does?
     
  7. elcionap

    elcionap

    Joined:
    Jan 11, 2016
    Posts:
    138
    You are missing the fixed keyword:
    Code (CSharp):
    1. public struct Job {
    2.     public State State;
    3.     public unsafe void Execute() {
    4.         fixed (State* ptr = &State) {
    5.             // Your code goes here.
    6.         }
    7.     }
    8. }
    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
     
    MintTree117 likes this.