diff options
author | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
commit | 8722a9920c1f6119bf6e769cba270e63097f8e25 (patch) | |
tree | 2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs | |
parent | 3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff) |
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs new file mode 100644 index 0000000..8264ac6 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs @@ -0,0 +1,223 @@ +#if MODULE_ENTITIES +using Unity.Collections.LowLevel.Unsafe; +using Unity.Entities; + +namespace Pathfinding.ECS { + /// <summary>Helper for EntityAccess</summary> + static class EntityAccessHelper { + public static readonly int GlobalSystemVersionOffset = UnsafeUtility.GetFieldOffset(typeof(ComponentTypeHandle<int>).GetField("m_GlobalSystemVersion", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + } + + /// <summary> + /// Wrapper for a pointer. + /// + /// Very similar to the entities package RefRW<T> struct. But unfortunately that one cannot be used because the required constructor is not exposed. + /// </summary> + public ref struct ComponentRef<T> where T : unmanaged { + unsafe byte* ptr; + + public unsafe ComponentRef(byte* ptr) { + this.ptr = ptr; + } + + public ref T value { + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get { + unsafe { + return ref *(T*)ptr; + } + } + } + } + + /// <summary>Utility for efficient random access to entity storage data from the main thread</summary> + public struct EntityStorageCache { + EntityStorageInfo storage; + Entity entity; + int lastWorldHash; + + /// <summary> + /// Retrieves the storage for a given entity. + /// + /// This method is very fast if the entity is the same as the last call to this method. + /// If the entity is different, it will be slower. + /// + /// Returns: True if the entity exists, and false if it does not. + /// </summary> + // Inlining makes this method about 20% faster. It's hot when accessing properties on the FollowerEntity component. + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public bool Update (World world, Entity entity, out EntityManager entityManager, out EntityStorageInfo storage) { + entityManager = default; + storage = this.storage; + if (world == null) return false; + entityManager = world.EntityManager; + // We must use entityManager.EntityOrderVersion here, not GlobalSystemVersion, because + // the GlobalSystemVersion does not necessarily update when structural changes happen. + var worldHash = entityManager.EntityOrderVersion ^ ((int)world.SequenceNumber << 8); + if (worldHash != lastWorldHash || entity != this.entity) { + if (!entityManager.Exists(entity)) return false; + this.storage = storage = entityManager.GetStorageInfo(entity); + this.entity = entity; + lastWorldHash = worldHash; + } + return true; + } + + /// <summary> + /// Retrieves a component for a given entity. + /// + /// This is a convenience method to call <see cref="Update"/> on this object and update on the access object, and then retrieve the component data. + /// + /// This method is very fast if the entity is the same as the last call to this method. + /// If the entity is different, it will be slower. + /// + /// Warning: You must not store the returned reference past a structural change in the ECS world. + /// + /// Returns: True if the entity exists, and false if it does not. + /// Throws: An exception if the entity does not have the given component. + /// </summary> + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public bool GetComponentData<A>(Entity entity, ref EntityAccess<A> access, out ComponentRef<A> value) where A : unmanaged, IComponentData { + if (Update(World.DefaultGameObjectInjectionWorld, entity, out var entityManager, out var storage)) { + access.Update(entityManager); + unsafe { + value = new ComponentRef<A>((byte*)UnsafeUtility.AddressOf(ref access[storage])); + } + return true; + } else { + value = default; + return false; + } + } + } + + /// <summary> + /// Utility for efficient random access to entity component data from the main thread. + /// + /// Since this struct returns a reference to the component data, it is faster than using EntityManager.GetComponentData, + /// in particular for larger component types. + /// + /// Warning: Some checks are not enforced by this API. It is the user's responsibility to ensure that + /// this struct does not survive past an ECS system update. If you only use this struct from the main thread + /// and only store it locally on the stack, this should not be a problem. + /// This struct also does not enforce that you only read to the component data if the readOnly flag is set. + /// </summary> + public struct EntityAccess<T> where T : unmanaged, IComponentData { + public ComponentTypeHandle<T> handle; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + SystemHandle systemHandle; +#endif + uint lastSystemVersion; + ulong worldSequenceNumber; + bool readOnly; + + public EntityAccess(bool readOnly) { + handle = default; + this.readOnly = readOnly; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + systemHandle = default; +#endif + + // Version 0 is never used by EntityManager.GlobalSystemVersion + lastSystemVersion = 0; + worldSequenceNumber = 0; + } + + /// <summary> + /// Update the component type handle if necessary. + /// + /// This must be called if any jobs or system might have been scheduled since the struct was created or since the last call to Update. + /// </summary> + public void Update (EntityManager entityManager) { + // If the global system version has changed, jobs may have been scheduled which writes + // to the component data. Therefore we need to complete all dependencies before we can + // safely read or write to the component data. + + var systemVersion = entityManager.GlobalSystemVersion; + var sequenceNumber = entityManager.WorldUnmanaged.SequenceNumber; + if (systemVersion != lastSystemVersion || worldSequenceNumber != sequenceNumber) { + if (lastSystemVersion == 0 || worldSequenceNumber != sequenceNumber) { + handle = entityManager.GetComponentTypeHandle<T>(readOnly); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + entityManager.WorldUnmanaged.IsSystemValid(systemHandle); + systemHandle = entityManager.WorldUnmanaged.GetExistingUnmanagedSystem<AIMoveSystem>(); +#endif + } + + lastSystemVersion = systemVersion; + worldSequenceNumber = sequenceNumber; + if (readOnly) entityManager.CompleteDependencyBeforeRO<T>(); + else entityManager.CompleteDependencyBeforeRW<T>(); + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + handle.Update(ref entityManager.WorldUnmanaged.ResolveSystemStateRef(systemHandle)); +#else + // handle.Update just does the same thing as this unsafe code, but in a much more roundabout way + unsafe { + var ptr = (byte*)UnsafeUtility.AddressOf(ref handle); + *(uint*)(ptr + EntityAccessHelper.GlobalSystemVersionOffset) = systemVersion; + } +#endif + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public bool HasComponent (EntityStorageInfo storage) { + return storage.Chunk.Has<T>(ref handle); + } + + public ref T this[EntityStorageInfo storage] { + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get { + unsafe { + var ptr = readOnly ? ((T*)storage.Chunk.GetRequiredComponentDataPtrRO(ref handle) + storage.IndexInChunk) : ((T*)storage.Chunk.GetRequiredComponentDataPtrRW(ref handle) + storage.IndexInChunk); + return ref *ptr; + } + } + } + } + + /// <summary> + /// Utility for efficient random access to managed entity component data from the main thread. + /// + /// Warning: Some checks are not enforced by this API. It is the user's responsibility to ensure that + /// this struct does not survive past an ECS system update. If you only use this struct from the main thread + /// and only store it locally on the stack, this should not be a problem. + /// This struct also does not enforce that you only read to the component data if the readOnly flag is set. + /// </summary> + public struct ManagedEntityAccess<T> where T : class, IComponentData { + EntityManager entityManager; + ComponentTypeHandle<T> handle; + bool readOnly; + + public ManagedEntityAccess(bool readOnly) { + entityManager = default; + handle = default; + this.readOnly = readOnly; + } + + public ManagedEntityAccess(EntityManager entityManager, bool readOnly) : this(readOnly) { + Update(entityManager); + } + + public void Update (EntityManager entityManager) { + if (readOnly) entityManager.CompleteDependencyBeforeRO<T>(); + else entityManager.CompleteDependencyBeforeRW<T>(); + handle = entityManager.GetComponentTypeHandle<T>(readOnly); + this.entityManager = entityManager; + } + + public T this[EntityStorageInfo storage] { + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get { + return storage.Chunk.GetManagedComponentAccessor<T>(ref handle, entityManager)[storage.IndexInChunk]; + } + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + set { + var accessor = storage.Chunk.GetManagedComponentAccessor<T>(ref handle, entityManager); + accessor[storage.IndexInChunk] = value; + } + } + } +} +#endif |