Search Unity

Cannot trace root cause of "ArgumentException: The entity does not exist"

Discussion in 'Entity Component System' started by DK_A5B, Apr 5, 2019.

  1. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    I have a demonstration project where I keep getting "ArgumentException: The entity does not exist" errors from ParentSystem.AddChildToParent(). My setup is that I have two Systems. The first System is responsible for creating and destroying entities based on the distance from camera. This System is a JobComponentSystem running in the InitializationSystemGroup and using the End Intialization ECB.

    The second System creates child Entities for all Entities created by the first system that don't already have child entities. This System is a JobComponentSystem running in the SimulationSystemGroup and using the Being Simulation ECB.

    The problem I'm having appears to be because there are Entities being used as Parents by the 2nd System which have been deleted by the first. However, I don't understand how this is possible since 2nd System runs in a later SystemGroup and uses a different ECB (one that comes later in the update cycle). Therefore, it is my (perhaps mistaken?) understanding that any Entity that is picked up by the criteria for a Job by the 2nd System group should exist (i.e. all entity destruction from the previous SystemGroup/ECB should already be applied).

    Clearly I'm missing something, but I can get enough information while debugging to understand what.
     
  2. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    You are probably experiencing the effect that all of the Entity Indexes used when using ECB are all temporary. So if you set them in a field, that field indexes an invalid entity.

    You'll probably need an additional temporary identifier that is stable between the two systems that can then be removed in a fixup phase.

    I've asked about the possibility of "fixup" jobs that can run after an ECB has processed to do this a bit more seamlessly. I think they'll eventually have a solution but nothing on the horizon yet.
     
  3. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    I'm not following what you mean about the identifier not being stable across the two systems. I'm not trying to use the Entity id across multiple systems. The problematic code is the Job in my 2nd system, which has the following code:

    Code (CSharp):
    1.  
    2.  
    3.         [ExcludeComponent (typeof (Child))]
    4.         struct CreateFloors : IJobProcessComponentDataWithEntity<MyParentData> {
    5.             // command buffer
    6.             public EntityCommandBuffer.Concurrent commandBuffer;
    7.          
    8.             // floor archetype
    9.             [ReadOnly] public EntityArchetype floorArchetype;
    10.          
    11.             public void Execute ( Entity parentEntity, int index, ref MyParentData data ) {
    12.                 Entity childEntity = commandBuffer.CreateEntity (index, floorArchetype);
    13.  
    14.                 // set local translation
    15.                 commandBuffer.SetComponent<Translation> (index, childEntity, new Translation {
    16.                     Value = float3.zero
    17.                 });
    18.  
    19.                 // setup the Parent-Child relationship
    20.                 commandBuffer.SetComponent<Parent> (index, childEntity, new Parent {
    21.                     Value = parentEntity
    22.                 });
    23.             }
    24.         }
    The problem is that when the ParentSystem operates on the newly added Parent relationship (to complete the setup by adding a DynamicBuffer<Child> component to the parent Entity and populate that with a reference to the child Entity), in some cases the parentEntity has been deleted. But I don't understand why this would be, because all of the deletions occur in the first System which runs in the InitializationSystemGroup. So any potential parent Entity (i.e. an Entity with MyParentData and no Child Component) that is picked up in the SimulationSystemGroup (where the 2nd System runs) shouldn't be deleted until at least the next Update.

    Given this, I can't understand why the ParentSystem is choking. Clearly I'm doing something wrong, but I can't see what it is with the setup that I have.
     
  4. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    What @recursive means is that childEntity is temportay because it is created with ECB.
    And when you set the parent it is an invalid entity.
     
  5. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    So, I understand how the id of the child entity created by the ECB would be temporary. And it makes sense that I cannot use that entity id to setup relationships because it is temporary (if you inspect it, it's a negative value). However, if you look at my code, I'm not using the child entity id to establish the relationship. I'm adding a component (Parent) to the child Entity, and then setting the parent entity id (which already exists, it shouldn't be temporary) as the Value of the Parent component on the child Entity.

    So, since the parent entity id isn't temporary (it was created outside this job, and outside the context of the ECB), I still don't see why this is generating the error. If I was configuring the relationship in the other way (by adding the child entity id to the Child component on the parent Entity) I would understand how this would result in an error.
     
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Show code in Initialization, where you setup and scheduling your job. Did you added your job handle to ECB producer?
     
  7. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    I did add the job handle to the producer. Here's the scheduling code:
    Code (CSharp):
    1.         protected override JobHandle OnUpdate ( JobHandle inputDeps ) {
    2.             // schedule CreateFloors job
    3.             JobHandle job = new CreateFloors {
    4.                 commandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer ().ToConcurrent (),
    5.                 floorArchetype = floorArchetype
    6.             }.Schedule (this, inputDeps);
    7.  
    8.             // add job handle to ECB system
    9.             m_EntityCommandBufferSystem.AddJobHandleForProducer (job);
    10.  
    11.             // return job handle
    12.             return job;
    13.         }

    And in case this might have something to do with it, here's the code for initializing the ECB system property and the archetype property:
    Code (CSharp):
    1.         protected override void OnCreateManager () {
    2.             // setup the ECB
    3.             m_EntityCommandBufferSystem = World.GetOrCreateManager<BeginSimulationEntityCommandBufferSystem> ();
    4.  
    5.             // configure the floor archetype
    6.             floorArchetype = EntityManager.CreateArchetype (new ComponentType[] {
    7.                 typeof(FloorData),
    8.                 typeof(LocalToWorld),
    9.                 typeof(Parent),
    10.                 typeof(LocalToParent),
    11.                 typeof(Translation)
    12.             });
    13.         }

    Thanks for the help.
     
  8. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Show please attributes on this system
     
  9. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    98
    I can provide an update on that: in the next release of the Entities package (preview31 I think?), any temporary Entities created by an ECB & subsequently processed by that same ECB (e.g. added as components on other Entities, added to Buffers/DynamicBuffers, etc.) will be automatically fixed-up during that ECB's playback.
     
    FROS7 likes this.
  10. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    98
    If I'm following this description correctly, please recall that using the BeginSimulationSystem ECB means that the ECBs from the second System will not be played back until the subsequent frame (and critically, after the next iteration of the first System has updated & played back). Here's an example of the first two frames:
    • Frame 1:
      • BeginInitialization
        • first System Update() // records ECB1_1
      • EndInitialization // plays back ECB1_1
      • BeginSimulation // nothing to play back yet
        • second System Update() // records ECB2_1
      • EndSimulation
      • // Presentation group here
    • Frame 2:
      • BeginInitialization
        • first System Update() // records ECB1_2
      • EndInitialization // plays back ECB1_2
      • BeginSimulation // plays back ECB2_1 -- any entities deleted in ECB1_2 will be invalid/nonexistent
        • second System Update() // records ECB2_2
      • EndSimulation
      • // Presentation group here
     
  11. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    Wow... this definitely explains why I'm having a problem and it also means my understanding of how ECBs worked was completely incorrect.

    If I'm understanding the statement above correctly, this means that a BeginXXX ECB system doesn't replay the commands entered during the Update_n pass until the Update_n+1 pass, is that correct? Previously I thought that all ECB systems formed a barrier during the current update (Update_n), but based on the above it looks like only EndXXX ECB systems replay on Update_n, while BeginXXX ECB systems will replay on Update_n+1. Is that correct?

    It looks like if I switch my 2nd system to use the EndSimulation ECB, it have the following execution order:
    • Frame 1:
      • BeginInitialization
        • first System Update() // records ECB1_1
      • EndInitialization // plays back ECB1_1
      • BeginSimulation // no longer doing playbacks here, switched to EndSimulation
        • second System Update() // records ECB2_1
      • EndSimulation // now plays back ECB2_1
    • Frame 2:
      • BeginInitialization
        • first System Update() // records ECB1_2
      • EndInitialization // plays back ECB1_2
      • BeginInitialization // again, no playback here. ECB2_1 already played back in previous update before ECB1_2, so no error
        • second System Update() // records ECB2_2
      • EndSimulation // plays back ECB2_2
     
  12. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    98
    Yes, that would be the resulting execution order. However, note that this may introduce new issues: if the new entities need to be processed by any systems in the simulation step, using EndSimulation to replay the ECBs means they new entities won't be processed by those systems until the next frame. A common example of this would be new entities with a LocalToWorld component; if they're created at the end of the simulation phase, they will miss that frame's update of the TransformSystemGroup (which writes LocalToWorld based on the entity's other transform-related components). The net result would therefore be that a new entity would be rendered for one frame with an identity LocalToWorld matrix (i.e. at the origin) before snapping to its correct position the next frame.

    Deferred entity creation is challenging; normally the answer for the transform bug I just described is "play back your entity-creating ECBs at BeginSimulation instead of EndSimulation", but clearly that just trades one problem for another in your use case. Perhaps your creation ECBs could be played back at BeginInitialization?