您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

928 行
34 KiB

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine.Profiling;
namespace Unity.Entities
{
[StructLayout(LayoutKind.Sequential)]
internal struct BasicCommand
{
public int CommandType;
public int TotalSize;
}
[StructLayout(LayoutKind.Sequential)]
internal struct CreateCommand
{
public BasicCommand Header;
public EntityArchetype Archetype;
public int BatchCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct EntityCommand
{
public BasicCommand Header;
public Entity Entity;
public int BatchCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct EntityComponentCommand
{
public EntityCommand Header;
public int ComponentTypeIndex;
public int ComponentSize;
// Data follows if command has an associated component payload
}
[StructLayout(LayoutKind.Sequential)]
internal struct EntityBufferCommand
{
public EntityCommand Header;
public int ComponentTypeIndex;
public int ComponentSize;
public BufferHeader TempBuffer;
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct EntitySharedComponentCommand
{
public EntityCommand Header;
public int ComponentTypeIndex;
public int HashCode;
public GCHandle BoxedObject;
public EntitySharedComponentCommand* Prev;
internal object GetBoxedObject()
{
if (BoxedObject.IsAllocated)
return BoxedObject.Target;
return null;
}
}
internal unsafe struct EntityCommandBufferChain
{
public ECBChunk* m_Tail;
public ECBChunk* m_Head;
public EntitySharedComponentCommand* m_CleanupList;
public CreateCommand* m_PrevCreateCommand;
public EntityCommand* m_PrevEntityCommand;
}
internal enum ECBCommand
{
InstantiateEntity,
CreateEntity,
DestroyEntity,
AddComponent,
RemoveComponent,
SetComponent,
AddBuffer,
SetBuffer,
AddSharedComponentData,
SetSharedComponentData
}
/// <summary>
/// Organized in memory like a single block with Chunk header followed by Size bytes of data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct ECBChunk
{
internal int Used;
internal int Size;
internal ECBChunk* Next;
internal ECBChunk* Prev;
internal int Capacity => Size - Used;
internal int Bump(int size)
{
var off = Used;
Used += size;
return off;
}
}
internal unsafe struct EntityCommandBufferData
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
public AtomicSafetyHandle m_BufferSafety;
public AtomicSafetyHandle m_ArrayInvalidationSafety;
#endif
public EntityCommandBufferChain m_MainThreadChain;
public EntityCommandBufferChain* m_ThreadedChains;
public int m_MinimumChunkSize;
public Allocator m_Allocator;
public bool m_ShouldPlayback;
public const int kMaxBatchCount = 512;
internal void InitConcurrentAccess()
{
if (m_ThreadedChains != null)
return;
// PERF: It's be great if we had a way to actually get the number of worst-case threads so we didn't have to allocate 128.
int allocSize = sizeof(EntityCommandBufferChain) * JobsUtility.MaxJobThreadCount;
m_ThreadedChains = (EntityCommandBufferChain*) UnsafeUtility.Malloc(allocSize, 8, m_Allocator);
UnsafeUtility.MemClear(m_ThreadedChains, allocSize);
}
internal void DestroyConcurrentAccess()
{
if (m_ThreadedChains != null)
{
UnsafeUtility.Free(m_ThreadedChains, m_Allocator);
m_ThreadedChains = null;
}
}
internal void AddCreateCommand(EntityCommandBufferChain* chain, ECBCommand op, EntityArchetype archetype)
{
if (chain->m_PrevCreateCommand != null &&
chain->m_PrevCreateCommand->Archetype == archetype &&
chain->m_PrevCreateCommand->BatchCount < kMaxBatchCount)
{
++chain->m_PrevCreateCommand->BatchCount;
var data = (CreateCommand*) Reserve(chain, sizeof(CreateCommand));
data->Header.CommandType = (int) op;
data->Header.TotalSize = sizeof(CreateCommand);
data->Archetype = archetype;
data->BatchCount = 0;
}
else
{
var data = (CreateCommand*) Reserve(chain, sizeof(CreateCommand));
data->Header.CommandType = (int) op;
data->Header.TotalSize = sizeof(CreateCommand);
data->Archetype = archetype;
data->BatchCount = 1;
chain->m_PrevCreateCommand = data;
}
}
internal void AddEntityCommand(EntityCommandBufferChain* chain, ECBCommand op, Entity e)
{
if (chain->m_PrevEntityCommand != null &&
chain->m_PrevEntityCommand->Entity == e &&
chain->m_PrevEntityCommand->BatchCount < kMaxBatchCount)
{
++chain->m_PrevEntityCommand->BatchCount;
var data = (EntityCommand*) Reserve(chain, sizeof(EntityCommand));
data->Header.CommandType = (int) op;
data->Header.TotalSize = sizeof(EntityCommand);
data->Entity = e;
data->BatchCount = 0;
}
else
{
var data = (EntityCommand*) Reserve(chain, sizeof(EntityCommand));
data->Header.CommandType = (int) op;
data->Header.TotalSize = sizeof(EntityCommand);
data->Entity = e;
data->BatchCount = 1;
chain->m_PrevEntityCommand = data;
}
}
internal void AddEntityComponentCommand<T>(EntityCommandBufferChain* chain, ECBCommand op, Entity e, T component) where T : struct
{
var typeSize = UnsafeUtility.SizeOf<T>();
var typeIndex = TypeManager.GetTypeIndex<T>();
var sizeNeeded = Align(sizeof(EntityComponentCommand) + typeSize, 8);
var data = (EntityComponentCommand*)Reserve(chain, sizeNeeded);
data->Header.Header.CommandType = (int)op;
data->Header.Header.TotalSize = sizeNeeded;
data->Header.Entity = e;
data->ComponentTypeIndex = typeIndex;
data->ComponentSize = typeSize;
UnsafeUtility.CopyStructureToPtr(ref component, (byte*)(data + 1));
}
internal BufferHeader* AddEntityBufferCommand<T>(EntityCommandBufferChain* chain, ECBCommand op, Entity e) where T : struct, IBufferElementData
{
var typeIndex = TypeManager.GetTypeIndex<T>();
var type = TypeManager.GetTypeInfo<T>();
var sizeNeeded = Align(sizeof(EntityBufferCommand) + type.SizeInChunk, 8);
var data = (EntityBufferCommand*)Reserve(chain, sizeNeeded);
data->Header.Header.CommandType = (int)op;
data->Header.Header.TotalSize = sizeNeeded;
data->Header.Entity = e;
data->ComponentTypeIndex = typeIndex;
data->ComponentSize = type.SizeInChunk;
BufferHeader.Initialize(&data->TempBuffer, type.BufferCapacity);
return &data->TempBuffer;
}
internal static int Align(int size, int alignmentPowerOfTwo)
{
return (size + alignmentPowerOfTwo - 1) & ~(alignmentPowerOfTwo - 1);
}
internal void AddEntityComponentTypeCommand(EntityCommandBufferChain* chain, ECBCommand op, Entity e, ComponentType t)
{
var sizeNeeded = Align(sizeof(EntityComponentCommand), 8);
var data = (EntityComponentCommand*)Reserve(chain, sizeNeeded);
data->Header.Header.CommandType = (int)op;
data->Header.Header.TotalSize = sizeNeeded;
data->Header.Entity = e;
data->ComponentTypeIndex = t.TypeIndex;
}
internal void AddEntitySharedComponentCommand<T>(EntityCommandBufferChain* chain, ECBCommand op, Entity e, int hashCode, object boxedObject)
where T : struct
{
var typeIndex = TypeManager.GetTypeIndex<T>();
var sizeNeeded = Align(sizeof(EntitySharedComponentCommand), 8);
var data = (EntitySharedComponentCommand*)Reserve(chain, sizeNeeded);
data->Header.Header.CommandType = (int)op;
data->Header.Header.TotalSize = sizeNeeded;
data->Header.Entity = e;
data->ComponentTypeIndex = typeIndex;
data->HashCode = hashCode;
if (boxedObject != null)
{
data->BoxedObject = GCHandle.Alloc(boxedObject);
// We need to store all GCHandles on a cleanup list so we can dispose them later, regardless of if playback occurs or not.
data->Prev = chain->m_CleanupList;
chain->m_CleanupList = data;
}
else
{
data->BoxedObject = new GCHandle();
}
}
internal byte* Reserve(EntityCommandBufferChain* chain, int size)
{
if (chain->m_Tail == null || chain->m_Tail->Capacity < size)
{
var chunkSize = math.max(m_MinimumChunkSize, size);
var c = (ECBChunk*)UnsafeUtility.Malloc(sizeof(ECBChunk) + chunkSize, 16, m_Allocator);
var prev = chain->m_Tail;
c->Next = null;
c->Prev = prev;
c->Used = 0;
c->Size = chunkSize;
if (prev != null) prev->Next = c;
if (chain->m_Head == null) chain->m_Head = c;
chain->m_Tail = c;
}
var offset = chain->m_Tail->Bump(size);
var ptr = (byte*)chain->m_Tail + sizeof(ECBChunk) + offset;
return ptr;
}
public DynamicBuffer<T> CreateBufferCommand<T>(ECBCommand commandType, EntityCommandBufferChain* chain, Entity e) where T : struct, IBufferElementData
{
BufferHeader* header = AddEntityBufferCommand<T>(chain, commandType, e);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
var safety = m_BufferSafety;
AtomicSafetyHandle.UseSecondaryVersion(ref safety);
var arraySafety = m_ArrayInvalidationSafety;
return new DynamicBuffer<T>(header, safety, arraySafety);
#else
return new DynamicBuffer<T>(header);
#endif
}
}
/// <summary>
/// A thread-safe command buffer that can buffer commands that affect entities and components for later playback.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[NativeContainer]
public unsafe struct EntityCommandBuffer : IDisposable
{
/// <summary>
/// The minimum chunk size to allocate from the job allocator.
/// </summary>
/// We keep this relatively small as we don't want to overload the temp allocator in case people make a ton of command buffers.
private const int kDefaultMinimumChunkSize = 4 * 1024;
[NativeDisableUnsafePtrRestriction] private EntityCommandBufferData* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
private AtomicSafetyHandle m_Safety;
[NativeSetClassTypeToNullOnSchedule] private DisposeSentinel m_DisposeSentinel;
#endif
/// <summary>
/// Allows controlling the size of chunks allocated from the temp job allocator to back the command buffer.
/// </summary>
/// Larger sizes are more efficient, but create more waste in the allocator.
public int MinimumChunkSize
{
get => m_Data->m_MinimumChunkSize > 0 ? m_Data->m_MinimumChunkSize : kDefaultMinimumChunkSize;
set => m_Data->m_MinimumChunkSize = Math.Max(0, value);
}
/// <summary>
/// Controls whether this command buffer should play back.
/// </summary>
///
/// This property is normally true, but can be useful to prevent
/// the buffer from playing back when the user code is not in control
/// of the site of playback.
///
/// For example, is a buffer has been aquired from a barrier and partially
/// filled in with data, but it is discovered that the work should be aborted,
/// this property can be set to true to prevent the buffer from playing back.
public bool ShouldPlayback
{
get { return m_Data != null ? m_Data->m_ShouldPlayback : false; }
set { if (m_Data != null) m_Data->m_ShouldPlayback = value; }
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private void EnforceSingleThreadOwnership()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
}
/// <summary>
/// Creates a new command buffer.
/// </summary>
/// <param name="label">Memory allocator to use for chunks and data</param>
public EntityCommandBuffer(Allocator label)
{
m_Data = (EntityCommandBufferData*)UnsafeUtility.Malloc(sizeof(EntityCommandBufferData),
UnsafeUtility.AlignOf<EntityCommandBufferData>(), label);
m_Data->m_Allocator = label;
m_Data->m_MinimumChunkSize = kDefaultMinimumChunkSize;
m_Data->m_ShouldPlayback = true;
m_Data->m_MainThreadChain.m_CleanupList = null;
m_Data->m_MainThreadChain.m_Tail = null;
m_Data->m_MainThreadChain.m_Head = null;
m_Data->m_MainThreadChain.m_PrevCreateCommand = null;
m_Data->m_MainThreadChain.m_PrevEntityCommand = null;
m_Data->m_ThreadedChains = null;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
// Used for all buffers returned from the API, so we can invalidate them once Playback() has been called.
m_Data->m_BufferSafety = AtomicSafetyHandle.Create();
// Used to invalidate array aliases to buffers
m_Data->m_ArrayInvalidationSafety = AtomicSafetyHandle.Create();
#if UNITY_2018_3_OR_NEWER
DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 8, label);
#else
DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 8);
#endif
#endif
}
public void Dispose()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
#if UNITY_2018_3_OR_NEWER
DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
#else
DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
#endif
#endif
if (m_Data != null)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.Release(m_Data->m_ArrayInvalidationSafety);
AtomicSafetyHandle.Release(m_Data->m_BufferSafety);
#endif
FreeChain(&m_Data->m_MainThreadChain);
if (m_Data->m_ThreadedChains != null)
{
for (int i = 0; i < JobsUtility.MaxJobThreadCount; ++i)
{
FreeChain(&m_Data->m_ThreadedChains[i]);
}
m_Data->DestroyConcurrentAccess();
}
UnsafeUtility.Free(m_Data, m_Data->m_Allocator);
m_Data = null;
}
}
private void FreeChain(EntityCommandBufferChain* chain)
{
var cleanup_list = chain->m_CleanupList;
while (cleanup_list != null)
{
cleanup_list->BoxedObject.Free();
cleanup_list = cleanup_list->Prev;
}
chain->m_CleanupList = null;
while (chain->m_Tail != null)
{
var prev = chain->m_Tail->Prev;
UnsafeUtility.Free(chain->m_Tail, m_Data->m_Allocator);
chain->m_Tail = prev;
}
chain->m_Head = null;
}
public void CreateEntity()
{
EnforceSingleThreadOwnership();
m_Data->AddCreateCommand(&m_Data->m_MainThreadChain, ECBCommand.CreateEntity, new EntityArchetype());
}
public void CreateEntity(EntityArchetype archetype)
{
EnforceSingleThreadOwnership();
m_Data->AddCreateCommand(&m_Data->m_MainThreadChain, ECBCommand.CreateEntity, archetype);
}
public void Instantiate(Entity e)
{
EnforceSingleThreadOwnership();
m_Data->AddEntityCommand(&m_Data->m_MainThreadChain, ECBCommand.InstantiateEntity, e);
}
public void DestroyEntity(Entity e)
{
EnforceSingleThreadOwnership();
m_Data->AddEntityCommand(&m_Data->m_MainThreadChain, ECBCommand.DestroyEntity, e);
}
public DynamicBuffer<T> AddBuffer<T>() where T : struct, IBufferElementData
{
return AddBuffer<T>(Entity.Null);
}
public DynamicBuffer<T> AddBuffer<T>(Entity e) where T : struct, IBufferElementData
{
EnforceSingleThreadOwnership();
return m_Data->CreateBufferCommand<T>(ECBCommand.AddBuffer, &m_Data->m_MainThreadChain, e);
}
public DynamicBuffer<T> SetBuffer<T>() where T : struct, IBufferElementData
{
return SetBuffer<T>(Entity.Null);
}
public DynamicBuffer<T> SetBuffer<T>(Entity e) where T : struct, IBufferElementData
{
EnforceSingleThreadOwnership();
return m_Data->CreateBufferCommand<T>(ECBCommand.SetBuffer, &m_Data->m_MainThreadChain, e);
}
public void AddComponent<T>(Entity e, T component) where T : struct, IComponentData
{
EnforceSingleThreadOwnership();
m_Data->AddEntityComponentCommand(&m_Data->m_MainThreadChain, ECBCommand.AddComponent, e, component);
}
public void AddComponent<T>(T component) where T : struct, IComponentData
{
AddComponent(Entity.Null, component);
}
public void SetComponent<T>(T component) where T : struct, IComponentData
{
SetComponent(Entity.Null, component);
}
public void SetComponent<T>(Entity e, T component) where T : struct, IComponentData
{
EnforceSingleThreadOwnership();
m_Data->AddEntityComponentCommand(&m_Data->m_MainThreadChain, ECBCommand.SetComponent, e, component);
}
public void RemoveComponent<T>(Entity e)
{
RemoveComponent(e, ComponentType.Create<T>());
}
public void RemoveComponent(Entity e, ComponentType componentType)
{
EnforceSingleThreadOwnership();
m_Data->AddEntityComponentTypeCommand(&m_Data->m_MainThreadChain, ECBCommand.RemoveComponent, e, componentType);
}
private static bool IsDefaultObject<T>(ref T component, out int hashCode) where T : struct, ISharedComponentData
{
var typeIndex = TypeManager.GetTypeIndex<T>();
var typeInfo = TypeManager.GetTypeInfo(typeIndex).FastEqualityTypeInfo;
var defaultValue = default(T);
hashCode = FastEquality.GetHashCode(ref component, typeInfo);
return FastEquality.Equals(ref defaultValue, ref component, typeInfo);
}
public void AddSharedComponent<T>(T component) where T : struct, ISharedComponentData
{
AddSharedComponent(Entity.Null, component);
}
public void AddSharedComponent<T>(Entity e, T component) where T : struct, ISharedComponentData
{
EnforceSingleThreadOwnership();
int hashCode;
if (IsDefaultObject(ref component, out hashCode))
m_Data->AddEntitySharedComponentCommand<T>(&m_Data->m_MainThreadChain, ECBCommand.AddSharedComponentData, e, hashCode, null);
else
m_Data->AddEntitySharedComponentCommand<T>(&m_Data->m_MainThreadChain, ECBCommand.AddSharedComponentData, e, hashCode, component);
}
public void SetSharedComponent<T>(T component) where T : struct, ISharedComponentData
{
SetSharedComponent(Entity.Null, component);
}
public void SetSharedComponent<T>(Entity e, T component) where T : struct, ISharedComponentData
{
EnforceSingleThreadOwnership();
int hashCode;
if (IsDefaultObject(ref component, out hashCode))
m_Data->AddEntitySharedComponentCommand<T>(&m_Data->m_MainThreadChain, ECBCommand.SetSharedComponentData, e, hashCode, null);
else
m_Data->AddEntitySharedComponentCommand<T>(&m_Data->m_MainThreadChain, ECBCommand.SetSharedComponentData, e, hashCode, component);
}
/// <summary>
/// Play back all recorded operations against an entity manager.
/// </summary>
/// <param name="mgr">The entity manager that will receive the operations</param>
public void Playback(EntityManager mgr)
{
if (mgr == null)
throw new NullReferenceException($"{nameof(mgr)} cannot be null");
EnforceSingleThreadOwnership();
if (!ShouldPlayback || m_Data == null)
return;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Data->m_BufferSafety);
AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Data->m_ArrayInvalidationSafety);
#endif
Profiler.BeginSample("EntityCommandBuffer.Playback");
PlaybackChain(mgr, &m_Data->m_MainThreadChain);
var threadedChains = m_Data->m_ThreadedChains;
if (threadedChains != null)
{
for (int i = 0; i < JobsUtility.MaxJobThreadCount; ++i)
{
var chain = &threadedChains[i];
PlaybackChain(mgr, chain);
}
}
Profiler.EndSample();
}
private static unsafe void PlaybackChain(EntityManager mgr, EntityCommandBufferChain* chain)
{
var head = chain->m_Head;
Entity currentEntity = new Entity();
int createEntityBatchOffset = 0;
int instantiateEntityBatchOffset = 0;
Entity* createEntityBatch = stackalloc Entity[EntityCommandBufferData.kMaxBatchCount];
Entity* instantiateEntityBatch = stackalloc Entity[EntityCommandBufferData.kMaxBatchCount];
while (head != null)
{
var off = 0;
var buf = (byte*)head + sizeof(ECBChunk);
while (off < head->Used)
{
var header = (BasicCommand*)(buf + off);
switch ((ECBCommand)header->CommandType)
{
case ECBCommand.DestroyEntity:
{
mgr.DestroyEntity(((EntityCommand*)header)->Entity);
}
break;
case ECBCommand.RemoveComponent:
{
var cmd = (EntityComponentCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.RemoveComponent(entity, TypeManager.GetType(cmd->ComponentTypeIndex));
}
break;
case ECBCommand.CreateEntity:
{
var cmd = (CreateCommand*)header;
if (cmd->BatchCount > 0)
{
createEntityBatchOffset = 0;
EntityArchetype at = cmd->Archetype;
if (!at.Valid)
{
at = mgr.CreateArchetype(null, 0);
}
mgr.CreateEntityInternal(at, createEntityBatch, cmd->BatchCount);
}
currentEntity = createEntityBatch[createEntityBatchOffset++];
}
break;
case ECBCommand.InstantiateEntity:
{
var cmd = (EntityCommand*)header;
if (cmd->BatchCount > 0)
{
instantiateEntityBatchOffset = 0;
mgr.InstantiateInternal(cmd->Entity, instantiateEntityBatch, cmd->BatchCount);
}
currentEntity = instantiateEntityBatch[instantiateEntityBatchOffset++];
}
break;
case ECBCommand.AddComponent:
{
var cmd = (EntityComponentCommand*)header;
var componentType = (ComponentType)TypeManager.GetType(cmd->ComponentTypeIndex);
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.AddComponent(entity, componentType);
mgr.SetComponentDataRaw(entity, cmd->ComponentTypeIndex, cmd + 1, cmd->ComponentSize);
}
break;
case ECBCommand.SetComponent:
{
var cmd = (EntityComponentCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.SetComponentDataRaw(entity, cmd->ComponentTypeIndex, cmd + 1, cmd->ComponentSize);
}
break;
case ECBCommand.AddBuffer:
{
var cmd = (EntityBufferCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.AddComponent(entity, ComponentType.FromTypeIndex(cmd->ComponentTypeIndex));
mgr.SetBufferRaw(entity, cmd->ComponentTypeIndex, &cmd->TempBuffer, cmd->ComponentSize);
}
break;
case ECBCommand.SetBuffer:
{
var cmd = (EntityBufferCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.SetBufferRaw(entity, cmd->ComponentTypeIndex, &cmd->TempBuffer, cmd->ComponentSize);
}
break;
case ECBCommand.AddSharedComponentData:
{
var cmd = (EntitySharedComponentCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.AddSharedComponentDataBoxed(entity, cmd->ComponentTypeIndex, cmd->HashCode,
cmd->GetBoxedObject());
}
break;
case ECBCommand.SetSharedComponentData:
{
var cmd = (EntitySharedComponentCommand*)header;
var entity = cmd->Header.Entity == Entity.Null ? currentEntity : cmd->Header.Entity;
mgr.SetSharedComponentDataBoxed(entity, cmd->ComponentTypeIndex, cmd->HashCode,
cmd->GetBoxedObject());
}
break;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
default:
throw new System.InvalidOperationException("Invalid command buffer");
#endif
}
off += header->TotalSize;
}
head = head->Next;
}
}
/// <summary>
/// Allows concurrent (non-deterministic) command buffer recording.
/// </summary>
[NativeContainer]
[NativeContainerIsAtomicWriteOnly]
unsafe public struct Concurrent
{
[NativeDisableUnsafePtrRestriction] EntityCommandBufferData* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
private AtomicSafetyHandle m_Safety;
#endif
[NativeSetThreadIndex]
private int m_ThreadIndex;
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private void CheckWriteAccess()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
}
unsafe public static implicit operator EntityCommandBuffer.Concurrent(EntityCommandBuffer buffer)
{
EntityCommandBuffer.Concurrent concurrent;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(buffer.m_Safety);
concurrent.m_Safety = buffer.m_Safety;
AtomicSafetyHandle.UseSecondaryVersion(ref concurrent.m_Safety);
#endif
concurrent.m_Data = buffer.m_Data;
concurrent.m_ThreadIndex = 0;
if (concurrent.m_Data != null)
{
concurrent.m_Data->InitConcurrentAccess();
}
return concurrent;
}
private EntityCommandBufferChain* ThreadChain
{
get { return &m_Data->m_ThreadedChains[m_ThreadIndex]; }
}
public void CreateEntity()
{
CheckWriteAccess();
m_Data->AddCreateCommand(ThreadChain, ECBCommand.CreateEntity, new EntityArchetype());
}
public void CreateEntity(EntityArchetype archetype)
{
CheckWriteAccess();
m_Data->AddCreateCommand(ThreadChain, ECBCommand.CreateEntity, archetype);
}
public void Instantiate(Entity e)
{
CheckWriteAccess();
m_Data->AddEntityCommand(ThreadChain, ECBCommand.InstantiateEntity, e);
}
public void DestroyEntity(Entity e)
{
CheckWriteAccess();
m_Data->AddEntityCommand(ThreadChain, ECBCommand.DestroyEntity, e);
}
public void AddComponent<T>(Entity e, T component) where T : struct, IComponentData
{
CheckWriteAccess();
m_Data->AddEntityComponentCommand(ThreadChain, ECBCommand.AddComponent, e, component);
}
public DynamicBuffer<T> AddBuffer<T>() where T : struct, IBufferElementData
{
return AddBuffer<T>(Entity.Null);
}
public DynamicBuffer<T> AddBuffer<T>(Entity e) where T : struct, IBufferElementData
{
CheckWriteAccess();
return m_Data->CreateBufferCommand<T>(ECBCommand.AddBuffer, ThreadChain, e);
}
public DynamicBuffer<T> SetBuffer<T>() where T : struct, IBufferElementData
{
return SetBuffer<T>(Entity.Null);
}
public DynamicBuffer<T> SetBuffer<T>(Entity e) where T : struct, IBufferElementData
{
CheckWriteAccess();
return m_Data->CreateBufferCommand<T>(ECBCommand.SetBuffer, ThreadChain, e);
}
public void AddComponent<T>(T component) where T : struct, IComponentData
{
AddComponent(Entity.Null, component);
}
public void SetComponent<T>(T component) where T : struct, IComponentData
{
SetComponent(Entity.Null, component);
}
public void SetComponent<T>(Entity e, T component) where T : struct, IComponentData
{
CheckWriteAccess();
m_Data->AddEntityComponentCommand(ThreadChain, ECBCommand.SetComponent, e, component);
}
public void RemoveComponent<T>(Entity e)
{
RemoveComponent(e, ComponentType.Create<T>());
}
public void RemoveComponent(Entity e, ComponentType componentType)
{
CheckWriteAccess();
m_Data->AddEntityComponentTypeCommand(ThreadChain, ECBCommand.RemoveComponent, e, componentType);
}
public void AddSharedComponent<T>(T component) where T : struct, ISharedComponentData
{
AddSharedComponent(Entity.Null, component);
}
public void AddSharedComponent<T>(Entity e, T component) where T : struct, ISharedComponentData
{
CheckWriteAccess();
int hashCode;
if (IsDefaultObject(ref component, out hashCode))
m_Data->AddEntitySharedComponentCommand<T>(ThreadChain, ECBCommand.AddSharedComponentData, e, hashCode, null);
else
m_Data->AddEntitySharedComponentCommand<T>(ThreadChain, ECBCommand.AddSharedComponentData, e, hashCode, component);
}
public void SetSharedComponent<T>(T component) where T : struct, ISharedComponentData
{
SetSharedComponent(Entity.Null, component);
}
public void SetSharedComponent<T>(Entity e, T component) where T : struct, ISharedComponentData
{
CheckWriteAccess();
int hashCode;
if (IsDefaultObject(ref component, out hashCode))
m_Data->AddEntitySharedComponentCommand<T>(ThreadChain, ECBCommand.SetSharedComponentData, e, hashCode, null);
else
m_Data->AddEntitySharedComponentCommand<T>(ThreadChain, ECBCommand.SetSharedComponentData, e, hashCode, component);
}
}
}
}