#Fastest way to get entity position from MonoBehaviour

1 messages · Page 1 of 1 (latest)

slate yarrow
#

Because of certain limitations, I picked a hybrid approach where I use Entities to simulate AI logic and movement and each entity has a counterpart MonoBehaviour in the scene that does other stuff.

The only thing the MonoBehaviour needs from the Entity is the transform position, nothing else. This is what I'm also syncing in FixedUpdate.

Every fixed step, I perform the following call from every MonoBehaviour instance:

var pos = EntitySpawnerSystem.Instance.GetPosition(_entity);
transform.position = new Vector3(pos.x, pos.y, 0);

EntitySpawnerSystem is a SystemBase class - the GetPosition(Entity e) makes the following call:

SystemAPI.GetComponent<LocalToWorld>(e).Position;

But after reaching around 900 entities, I noticed this becoming a bottleneck. This call alone is taking 40% of CPU time. The call stack looks something like this:

GetPosition()  40.7%    
  ComponentLookup`1.get_Item()  16.6%
EntityManager.CompleteDependencyBeforeRO() 9.1%
  ComponentLookup`1.Update() 7.1%
  String.memcpy() 2.5%
  ...

Is there a better, faster way of getting read-only position data of an entity from MonoBehaviour?

edgy glen
#

If you can rearrange and have a managed IComponentData with the gameobject link on the entity

#

You can make a foreach for go over all the entities and have them update the transform in a system
(with a change filter so it would only do it when the position has changed)

#

There might also be a way to do it in a Job with TransformAccess but i have not tried to do that

slate yarrow
#

Will try the managed component approach

edgy glen
#

Note doing it that way it has to be .WithoutBurst().Run() due to the managed component

#

The other way with TransformAccess i think could be parallel and burstable

#

humm actually thinking about it im not sure how useful TransformAccess job would be as you'd need to get the correct entity somehow

slate yarrow
#

I made a managed ComponentData with link to my MonoBehaviour - added to every entity - then a SystemBase that iterated through all of them and also got their LocalToWorld component - same as before

#

It ended up being SLOWER - my frame time went from ~4.1ms to around ~5ms

#

(Though this could also be because I was doing this in OnUpdate in SystemBase without putting it into Fixed simulation group - previous sync call was in FixedUpdate)

#

(But I still expected a significant performance gain... so it surprised me)

#

Will also check the TransformAccess

#

Will play around some more later today

edgy glen
#

I would've thought you'd gain some performance

slate yarrow
#

but even so, I would've still expected more gain as I'm processing all entities at once

#

I doubt the filter will help much as the position is changing basically all the time

#

I also tried using a ComponentLookup<LocalToWorld> inside my SystemBase class - and replacing SystemAPI.GetComponent with lookup.GetRefRO(e)... instead - but am getting errors that it's invalid because of structural changes - even though I am updating it in OnUpdate properly... must be the timing of it

#

I made the lookup work, same performance

edgy glen
spring temple
#

if you have a monobehaviour for every entity, you are very very unlikely IMO to get any perf benefit from ECS. MB's are slow among other reasons because of random memory access, and when each MB has to obtain a different random entity's memory, that's extremely random.

unless you have some other reason to be using entities (or if you have lots of entities with no corresponding monobehaviour at runtime), i would just stick to monobehaviours in this case

slate yarrow
#

I'm already getting a huge perf benefit from using entities - mainly from doing a lot of raycasting in parallel - this was quite slow with only monobehaviours - so I'm pretty happy so far with the current approach. Was just wondering what the most efficient approach to getting entity positions would be, to even further optimize this setup.

#

In the future I'm planning to move everything to Entities anyway, but for now I'm just trying to speed this particular bottleneck up

slate yarrow
#

Alright, so this is what I did to finally speed up my position syncing:
1 - Created a field NativeHashMap<Entity, float3> _cachedPositions; on my SystemBase class
2 - Allocated it in OnCreate and disposed it in OnDestroy (using Persistent allocator)
3 - In OnUpdate I am iterating through all entities and writing to this field - using Burst
4 - MonoBehaviours then read the saved positions from this NativeHashMap

#
var map = _cachedPositions;
Entities.ForEach((ref EnemyAspectBasic enemy) =>
{
    map[enemy.Entity] = enemy.Position;
}).WithBurst().Schedule();
#

Then I just get _cachedPositions[entity] to get the position for entity

#

I tried using the parallel hash map, but the ParallelWriter only has the TryAdd method which does not update existing keys - is this intentional?

wide panther
slate yarrow
#

Updating a value for an existing key is a write operation

wide panther
#

Sorry misread