2026-05-10 11:47:55 -04:00
#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Graph Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
2026-06-27 12:52:03 -04:00
namespace Opsive.GraphDesigner.Runtime.Variables
2026-05-10 11:47:55 -04:00
{
using System ;
using System.Collections.Generic ;
2026-06-27 12:52:03 -04:00
using Opsive.GraphDesigner.Runtime.ECS.Core ;
2026-05-10 11:47:55 -04:00
using Unity.Entities ;
using UnityEngine ;
/// <summary>
/// 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<SharedVariableElement>, and supports syncing runtime values between the ECS buffer and managed SharedVariables.
/// </summary>
public class ECSVariableRegistry
{
2026-06-27 12:52:03 -04:00
private readonly ECSVariableRegistryCore m_Core = new ( ) ;
2026-05-10 11:47:55 -04:00
private readonly List < Action < DynamicBuffer < SharedVariableElement > > > m_SyncToManagedActions = new ( ) ;
private readonly List < Action < DynamicBuffer < SharedVariableElement > > > m_SyncToECSActions = new ( ) ;
/// <summary>
/// Gets the number of variables registered so far.
/// </summary>
2026-06-27 12:52:03 -04:00
public int Count = > m_Core . Count ;
/// <summary>
/// Returns authoring metadata for a registered SharedVariable.
/// </summary>
/// <param name="index">The SharedVariableElement buffer index.</param>
/// <param name="name">The registered variable name.</param>
/// <param name="elementType">The registered variable value type.</param>
/// <returns>True if the index maps to a tracked SharedVariable.</returns>
public bool TryGetVariableMetadata ( int index , out string name , out Type elementType )
{
return m_Core . TryGetVariableMetadata ( index , out name , out elementType ) ;
}
2026-05-10 11:47:55 -04:00
/// <summary>
/// Registers a SharedVariable with the registry and returns its buffer index.
/// </summary>
/// <param name="sharedVariable">The SharedVariable that should be registered.</param>
/// <returns>The buffer index of the registered SharedVariable, or -1 if the SharedVariable is null.</returns>
2026-06-27 12:52:03 -04:00
public int Register < T > ( SharedVariable < T > sharedVariable ) where T : unmanaged
2026-05-10 11:47:55 -04:00
{
if ( sharedVariable = = null ) {
return - 1 ;
}
2026-06-27 12:52:03 -04:00
var size = SharedVariableBufferCore . SizeOf < T > ( ) ;
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." ) ;
2026-05-10 11:47:55 -04:00
2026-06-27 12:52:03 -04:00
if ( m_Core . TryGetExistingIndex ( sharedVariable , out var existingIndex ) ) {
2026-05-10 11:47:55 -04:00
return existingIndex ;
}
2026-06-27 12:52:03 -04:00
var index = m_Core . Register ( sharedVariable ) ;
if ( index < 0 ) {
return index ;
2026-05-10 11:47:55 -04:00
}
// 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 < T > ( capturedIndex ) ;
} ) ;
m_SyncToECSActions . Add ( ( buffer ) = > {
buffer . Set ( capturedIndex , capturedVar . Value ) ;
} ) ;
return index ;
}
/// <summary>
/// Creates the DynamicBuffer<SharedVariableElement> on the entity and writes all registered initial values. Call once after all tasks have registered their variables.
/// </summary>
/// <param name="world">The world that owns the entity.</param>
/// <param name="entity">The entity that should receive the shared variable buffer.</param>
public void Bake ( World world , Entity entity )
{
2026-06-27 12:52:03 -04:00
if ( m_Core . Count = = 0 ) {
2026-05-10 11:47:55 -04:00
return ;
}
DynamicBuffer < SharedVariableElement > buffer ;
if ( world . EntityManager . HasBuffer < SharedVariableElement > ( entity ) ) {
buffer = world . EntityManager . GetBuffer < SharedVariableElement > ( entity ) ;
buffer . Clear ( ) ;
} else {
buffer = world . EntityManager . AddBuffer < SharedVariableElement > ( entity ) ;
}
2026-06-27 12:52:03 -04:00
for ( int i = 0 ; i < m_Core . Count ; + + i ) {
buffer . Add ( new SharedVariableElement { Value = m_Core . GetInitialValue ( i ) } ) ;
2026-05-10 11:47:55 -04:00
}
}
/// <summary>
/// Writes the current managed SharedVariable values into the ECS buffer.
/// </summary>
/// <param name="world">The world that owns the entity.</param>
/// <param name="entity">The entity whose shared variable buffer should be synced.</param>
public void SyncToECS ( World world , Entity entity )
{
if ( m_SyncToECSActions . Count = = 0 | | world = = null | | entity = = Entity . Null ) {
return ;
}
if ( ! world . EntityManager . HasBuffer < SharedVariableElement > ( entity ) ) {
return ;
}
var buffer = world . EntityManager . GetBuffer < SharedVariableElement > ( entity ) ;
for ( int i = 0 ; i < m_SyncToECSActions . Count ; + + i ) {
2026-06-27 12:52:03 -04:00
if ( ! m_Core . IsManagedValueDirty ( i ) ) {
2026-05-10 11:47:55 -04:00
continue ;
}
m_SyncToECSActions [ i ] ( buffer ) ;
2026-06-27 12:52:03 -04:00
m_Core . MarkSyncedToECS ( i , buffer [ i ] . Value ) ;
2026-05-10 11:47:55 -04:00
}
}
/// <summary>
/// Reads current ECS buffer values back into the managed SharedVariable instances.
/// </summary>
/// <param name="world">The world that owns the entity.</param>
/// <param name="entity">The entity whose shared variable buffer should be synced.</param>
public void SyncToManaged ( World world , Entity entity )
{
if ( m_SyncToManagedActions . Count = = 0 | | world = = null | | entity = = Entity . Null ) {
return ;
}
if ( ! world . EntityManager . HasBuffer < SharedVariableElement > ( entity ) ) {
return ;
}
var buffer = world . EntityManager . GetBuffer < SharedVariableElement > ( entity ) ;
2026-06-27 12:52:03 -04:00
m_Core . SuppressManagedValueTracking ( ( ) = > {
2026-05-10 11:47:55 -04:00
for ( int i = 0 ; i < m_SyncToManagedActions . Count ; + + i ) {
var bufferValue = buffer [ i ] . Value ;
2026-06-27 12:52:03 -04:00
if ( ! m_Core . ShouldSyncToManaged ( i , bufferValue ) ) {
2026-05-10 11:47:55 -04:00
continue ;
}
m_SyncToManagedActions [ i ] ( buffer ) ;
2026-06-27 12:52:03 -04:00
m_Core . MarkSyncedToManaged ( i , bufferValue ) ;
2026-05-10 11:47:55 -04:00
}
2026-06-27 12:52:03 -04:00
} ) ;
2026-05-10 11:47:55 -04:00
}
/// <summary>
/// Removes any managed SharedVariable change listeners registered by the registry.
/// </summary>
public void Dispose ( )
{
2026-06-27 12:52:03 -04:00
m_Core . Dispose ( ) ;
2026-05-10 11:47:55 -04:00
}
}
}
#endif