Search Unity

Any tips on a parallel (or just some much faster) way to call for entityManager->SetComponentData()?

Discussion in 'Entity Component System' started by Aelita1992, Jan 21, 2019.

  1. Aelita1992

    Aelita1992

    Joined:
    Jan 30, 2017
    Posts:
    2
    Hi, we're trying to make an RTS game that uses pure ECS for swarms of units being moved around. The jobs itself work great and compute the unit's position and rotation change for around 100k units right now, but we're stuck on one problem - which is this piece of code:

    for (int i = 0; i < nativeArrayShips.Length; i++)
    {
    _entityManager.SetComponentData<Position>(shipEntities, new Position { Value = position });
    }

    We're obviously trying to apply the new positions to every unit available in-game and it's obviously terribly slow for that many units. We've tried running this code in multiple threads but entityManager is blocking this possibility for us (complaining that we can't do that in non-main-thread code.

    Do you have any ideas of how we could move solve that problem and apply new possitions that we store in a NativeArray for whole group of units? Thanks in advance for all the help!
     
  2. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    EntityCommandBuffer.Concurrent has a RemoveComponent and AddComponent. It can be used inside of a IJobParallelFor. However, based on what you're trying to accomplish it's probably better for you to do
    Code (CSharp):
    1. var position = positions[i];
    2. position.value = position;
    3. positions[i] = position;
    Please tell me if this is what you're trying to achieve.
     
  3. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Use JobComponentSystem and ComponentDataFromEntity.
     
  4. RecursiveEclipse

    RecursiveEclipse

    Joined:
    Sep 6, 2018
    Posts:
    298
    More specifically, you should probably be using IJobProcessComponentData<Position, (whatever else you need)>, possibly with a RequireComponentTag attribute(mark your entities with a marker/empty component). IJobProcessComponentData is usually more friendly code size wise, and faster. For example(written freehand so there may be a small error somewhere):

    Code (CSharp):
    1. public class ShipMovementSystem : JobComponentSystem {
    2.     //Pure calculations are almost always burst compilable.
    3.     [BurstCompile]
    4.     [RequireComponentTag(typeof(ShipTag))]
    5.     struct ShipMovementJob : IJobProcessComponentData<Position> {
    6.         public void Execute(ref Position position) {
    7.             position.Value = //Calculate new position
    8.         }
    9.     }
    10.  
    11.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    12.         return new ShipMovementJob {}.Schedule(this, inputDeps);
    13.     }
    14. }
    15.  
    I think the only time you should be using SetComponent is when you are creating your entities. If you're using EntityManager in a job (through an EntityCommandBuffer) you need a barrier, and barrier playback is costly since they are a sync point and commands are played back in the main thread anyway.
     
    Last edited: Jan 22, 2019
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    I'm guessing that since you are using NativeArrays to calculate positions that your position calculations require a rigid ordering of the entities and cannot use IJobProcessComponentData. In that case you simply want an IJobParallelFor that uses your NativeArrays and ComponentDataFromEntity<Position> with NativeDisableParallelForRestriction.

    But if you don't really need order, use RecursiveEclipse's approach instead. It will be faster as you won't be performing entity lookups or random-ish writes.
     
  6. RecursiveEclipse

    RecursiveEclipse

    Joined:
    Sep 6, 2018
    Posts:
    298
    I'm actually wondering how OP is creating/using their NativeArrays right now in a full example. It's sometimes possible to use(or perhaps abuse?) IJobProcessComponentDataWithEntity's index argument.
     
  7. Aelita1992

    Aelita1992

    Joined:
    Jan 30, 2017
    Posts:
    2
    Hey, it may be totally wrong as we've just started learning it like a couple of weeks ago, but here it is: https://pastebin.com/X0SLKz4v

    The VelocityJob calculated new positions fast and does its job. The problem happens around line 133, when we're reapplying new positions to our ships. I'll try to read about and experiment with IJobProcessComponentData, JobComponentSystem and ComponentDataFromEntity.

    Wow, the IJobProcessComponentData<Position, Heading> hint helped us the most! We've rewritten how the code works and it went crazy fast :D Thanks ppl for the help <3
     
    Last edited: Jan 22, 2019