#if GRAPH_DESIGNER /// --------------------------------------------- /// Graph Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.GraphDesigner.Runtime.Variables { using System; using System.Collections.Generic; using Opsive.GraphDesigner.Runtime.ECS.Core; using Unity.Entities; using UnityEngine; /// /// Managed registry created once per entity during graph initialization. Collects SharedVariable registrations from all ECS tasks, deduplicates by name+scope, /// bakes initial values into DynamicBuffer, and supports syncing runtime values between the ECS buffer and managed SharedVariables. /// public class ECSVariableRegistry { private readonly ECSVariableRegistryCore m_Core = new(); private readonly List>> m_SyncToManagedActions = new(); private readonly List>> m_SyncToECSActions = new(); /// /// Gets the number of variables registered so far. /// public int Count => m_Core.Count; /// /// Returns authoring metadata for a registered SharedVariable. /// /// The SharedVariableElement buffer index. /// The registered variable name. /// The registered variable value type. /// True if the index maps to a tracked SharedVariable. public bool TryGetVariableMetadata(int index, out string name, out Type elementType) { return m_Core.TryGetVariableMetadata(index, out name, out elementType); } /// /// Registers a SharedVariable with the registry and returns its buffer index. /// /// The SharedVariable that should be registered. /// The buffer index of the registered SharedVariable, or -1 if the SharedVariable is null. public int Register(SharedVariable sharedVariable) where T : unmanaged { if (sharedVariable == null) { return -1; } var size = SharedVariableBufferCore.SizeOf(); Debug.Assert(size <= SharedVariableBufferCore.MaxValueSize, $"SharedVariable<{typeof(T).Name}> value size ({size} bytes) exceeds the {SharedVariableBufferCore.MaxValueSize}-byte SharedVariableElement limit. Use a smaller unmanaged type."); if (m_Core.TryGetExistingIndex(sharedVariable, out var existingIndex)) { return existingIndex; } var index = m_Core.Register(sharedVariable); if (index < 0) { return index; } // Capture sync delegates so managed and ECS tasks stay in sync while the tree is running. var capturedVar = sharedVariable; var capturedIndex = index; m_SyncToManagedActions.Add((buffer) => { capturedVar.Value = buffer.Get(capturedIndex); }); m_SyncToECSActions.Add((buffer) => { buffer.Set(capturedIndex, capturedVar.Value); }); return index; } /// /// Creates the DynamicBuffer on the entity and writes all registered initial values. Call once after all tasks have registered their variables. /// /// The world that owns the entity. /// The entity that should receive the shared variable buffer. public void Bake(World world, Entity entity) { if (m_Core.Count == 0) { return; } DynamicBuffer buffer; if (world.EntityManager.HasBuffer(entity)) { buffer = world.EntityManager.GetBuffer(entity); buffer.Clear(); } else { buffer = world.EntityManager.AddBuffer(entity); } for (int i = 0; i < m_Core.Count; ++i) { buffer.Add(new SharedVariableElement { Value = m_Core.GetInitialValue(i) }); } } /// /// Writes the current managed SharedVariable values into the ECS buffer. /// /// The world that owns the entity. /// The entity whose shared variable buffer should be synced. public void SyncToECS(World world, Entity entity) { if (m_SyncToECSActions.Count == 0 || world == null || entity == Entity.Null) { return; } if (!world.EntityManager.HasBuffer(entity)) { return; } var buffer = world.EntityManager.GetBuffer(entity); for (int i = 0; i < m_SyncToECSActions.Count; ++i) { if (!m_Core.IsManagedValueDirty(i)) { continue; } m_SyncToECSActions[i](buffer); m_Core.MarkSyncedToECS(i, buffer[i].Value); } } /// /// Reads current ECS buffer values back into the managed SharedVariable instances. /// /// The world that owns the entity. /// The entity whose shared variable buffer should be synced. public void SyncToManaged(World world, Entity entity) { if (m_SyncToManagedActions.Count == 0 || world == null || entity == Entity.Null) { return; } if (!world.EntityManager.HasBuffer(entity)) { return; } var buffer = world.EntityManager.GetBuffer(entity); m_Core.SuppressManagedValueTracking(() => { for (int i = 0; i < m_SyncToManagedActions.Count; ++i) { var bufferValue = buffer[i].Value; if (!m_Core.ShouldSyncToManaged(i, bufferValue)) { continue; } m_SyncToManagedActions[i](buffer); m_Core.MarkSyncedToManaged(i, bufferValue); } }); } /// /// Removes any managed SharedVariable change listeners registered by the registry. /// public void Dispose() { m_Core.Dispose(); } } } #endif