Skip to content

Commit

Permalink
Merge pull request #15 from AkiKurisu/dev/v0.1.4
Browse files Browse the repository at this point in the history
Merge dev v0.1.4
  • Loading branch information
AkiKurisu authored Jan 25, 2025
2 parents e773ad3 + 38fef3b commit efbedad
Show file tree
Hide file tree
Showing 31 changed files with 815 additions and 138 deletions.
51 changes: 48 additions & 3 deletions Docs/flow_startup.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ public class MyComponent: Component
}
```

2. For static method, create a new class and implement `ExecutableFunctionLibrary` to
add static executable functions, then add `ExecutableFunctionAttribute`.
2. For static method, create a new <b>partial</b> class and implement `ExecutableFunctionLibrary` to
add static executable functions, then add `ExecutableFunctionAttribute`.

>You must add `partial` modifier to let source generator work. Source generator will register static function pointer to the flow reflection system instead of using MethodInfo to enhance runtime performance.

```C#
public class UnityExecutableFunctionLibrary: ExecutableFunctionLibrary
public partial class UnityExecutableFunctionLibrary: ExecutableFunctionLibrary
{
// IsScriptMethod will consider UObject as function target type
// IsSelfTarget will let graph pass self reference as first parameter if self is UObject
Expand Down Expand Up @@ -472,6 +474,49 @@ public void Test()
}
```

#### Source Generator

In [executable function part](#executable-function), it is mentioned that source generator will register static methods to improve runtime performance.

The following shows what SourceGenerator does.

Source code:

```C#
/// <summary>
/// Executable function library for ceres
/// </summary>
[CeresGroup("Ceres")]
public partial class CeresExecutableLibrary: ExecutableFunctionLibrary
{
[ExecutableFunction, CeresLabel("Set LogLevel")]
public static void Flow_SetLogLevel(LogType logType)
{
CeresAPI.LogLevel = logType;
}

[ExecutableFunction(ExecuteInDependency = true), CeresLabel("Get LogLevel")]
public static LogType Flow_GetLogLevel()
{
return CeresAPI.LogLevel;
}
}
```

Generated code:

```C#
[CompilerGenerated]
public partial class CeresExecutableLibrary
{
protected override unsafe void CollectExecutableFunctions()
{
RegisterExecutableFunctions<CeresExecutableLibrary>(nameof(Flow_SetLogLevel), 1, (delegate* <LogType, void>)&Flow_SetLogLevel);
RegisterExecutableFunctions<CeresExecutableLibrary>(nameof(Flow_GetLogLevel), 0, (delegate* <LogType>)&Flow_GetLogLevel);
}
}
```

## Debug

To enable and disable debug mode, click `debug` button in the upper right corner.
Expand Down
13 changes: 10 additions & 3 deletions Runtime/Core/Components/SceneVariableScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ public class SceneVariableScope : MonoBehaviour, IVariableScope, IVariableSource
{
[SerializeReference]
private List<SharedVariable> sharedVariables = new();

public List<SharedVariable> SharedVariables => sharedVariables;

[SerializeField]
private GameVariableScope parentScope;

public GlobalVariables GlobalVariables { get; private set; }
private bool initialized = false;

private bool _initialized;

private void Awake()
{
if (!initialized)
if (!_initialized)
{
Initialize();
}
}

public void Initialize()
{
initialized = true;
_initialized = true;
if (parentScope && parentScope.IsCurrentScope())
{
GlobalVariables = new GlobalVariables(sharedVariables, parentScope);
Expand All @@ -30,6 +36,7 @@ public void Initialize()
GlobalVariables = new GlobalVariables(sharedVariables);
}
}

private void OnDestroy()
{
GlobalVariables.Dispose();
Expand Down
5 changes: 5 additions & 0 deletions Runtime/Core/Models/CeresAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public static class CeresAPI
/// </summary>
public static LogType LogLevel { get; set; } = LogType.Log;

/// <summary>
/// Whether to log <see cref="UnityEngine.Object"/> relink details
/// </summary>
public static bool LogUObjectRelink { get; set; }

public static void LogWarning(string message)
{
if(LogLevel >= LogType.Warning)
Expand Down
38 changes: 22 additions & 16 deletions Runtime/Core/Models/GlobalVariables.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
using UObject = UnityEngine.Object;
namespace Ceres
{
/// <summary>
Expand All @@ -11,46 +10,53 @@ namespace Ceres
public class GlobalVariables : IVariableSource, IDisposable
{
public List<SharedVariable> SharedVariables { get; }
private static GlobalVariables instance;
public static GlobalVariables Instance => instance ?? FindOrCreateDefault();
private readonly IVariableScope parentScope;

private static GlobalVariables _instance;

public static GlobalVariables Instance => _instance ?? FindOrCreateDefault();

private readonly IVariableScope _parentScope;

public GlobalVariables(List<SharedVariable> sharedVariables)
{
instance = this;
_instance = this;
SharedVariables = new List<SharedVariable>(sharedVariables);
}

public GlobalVariables(List<SharedVariable> sharedVariables, IVariableScope parentScope)
{
instance = this;
this.parentScope = parentScope;
_instance = this;
_parentScope = parentScope;
SharedVariables = new List<SharedVariable>(sharedVariables);
if (parentScope != null)
{
sharedVariables.AddRange(parentScope.GlobalVariables.SharedVariables);
}
}

private static GlobalVariables FindOrCreateDefault()
{
var scope = Object.FindObjectOfType<SceneVariableScope>();
var scope = UObject.FindObjectOfType<SceneVariableScope>();
if (scope != null)
{
scope.Initialize();
return scope.GlobalVariables;
}
instance = new(new());
return instance;
_instance = new GlobalVariables(new List<SharedVariable>());
return _instance;
}

public void Dispose()
{
if (instance != this)
if (_instance != this)
{
Debug.LogWarning("Only scope current used should be disposed!");
CeresAPI.LogWarning("Global variables can only be disposed in top level scope");
return;
}
instance = null;
if (parentScope != null)
_instance = null;
if (_parentScope != null)
{
instance = parentScope.GlobalVariables;
_instance = _parentScope.GlobalVariables;
}
}
}
Expand Down
77 changes: 24 additions & 53 deletions Runtime/Core/Models/Graph/CeresGraph.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if CERES_DISABLE_ILPP
using Ceres.Utilities;
using System.Reflection;
using Chris;
using System.Collections;
#endif
Expand Down Expand Up @@ -81,9 +82,13 @@ public BlackBoard BlackBoard

private readonly HashSet<SharedVariable> _internalVariables = new();

private readonly HashSet<CeresPort> _internalPorts = new();

#if CERES_DISABLE_ILPP
private static readonly Dictionary<Type, List<FieldInfo>> VariableFieldInfoLookup = new();

private static readonly Dictionary<Type, List<FieldInfo>> PortFieldInfoLookup = new();
#endif

[SerializeReference]
public List<SharedVariable> variables;
Expand Down Expand Up @@ -142,8 +147,8 @@ protected static void InitVariables_Imp(CeresGraph graph)
var internalVariables = graph._internalVariables;
foreach (var node in graph.GetAllNodes())
{
/* Variables will be collected by node using ILPP */
#if !CERES_DISABLE_ILPP
/* Variables will be collected by node using ILPP */
node.InitializeVariables();
foreach (var variable in node.SharedVariables)
{
Expand All @@ -156,7 +161,7 @@ protected static void InitVariables_Imp(CeresGraph graph)
{
fields = nodeType
.GetAllFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(x => x.FieldType.IsSubclassOf(typeof(SharedVariable)) || IsIListVariable(x.FieldType))
.Where(x => x.FieldType.IsSubclassOf(typeof(SharedVariable)) || x.FieldType.IsIListVariable())
.ToList();
VariableFieldInfoLookup.Add(nodeType, fields);
}
Expand Down Expand Up @@ -187,47 +192,29 @@ protected static void InitVariables_Imp(CeresGraph graph)
}
}

private static bool IsIListVariable(Type fieldType)
{
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
{
var genericArgument = fieldType.GetGenericArguments()[0];
if (typeof(SharedVariable).IsAssignableFrom(genericArgument))
{
return true;
}
}
else if (fieldType.IsArray)
{
var elementType = fieldType.GetElementType();
if (typeof(SharedVariable).IsAssignableFrom(elementType))
{
return true;
}
}
return false;
}

/// <summary>
/// Traverse the graph and init all ports automatically
/// </summary>
/// <param name="graph"></param>
protected static void InitPorts_Imp(CeresGraph graph)
{
var internalPorts = graph._internalPorts;
foreach (var node in graph.GetAllNodes())
{
/* Ports will be collected by node using ILPP */
#if !CERES_DISABLE_ILPP
/* Ports will be collected by node using ILPP */
node.InitializePorts();
foreach (var pair in node.Ports)
{
graph.LinkPort(pair.Value, pair.Key, node);
internalPorts.Add(pair.Value);
}
foreach (var pair in node.PortLists)
{
for(int i = 0; i < pair.Value.Count; i++)
{
graph.LinkPort((CeresPort)pair.Value[i], pair.Key, i, node);
internalPorts.Add((CeresPort)pair.Value[i]);
}
}
#else
Expand All @@ -236,7 +223,7 @@ protected static void InitPorts_Imp(CeresGraph graph)
{
fields = nodeType
.GetAllFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(x => x.FieldType.IsSubclassOf(typeof(CeresPort)) || IsIListPort(x.FieldType))
.Where(x => x.FieldType.IsSubclassOf(typeof(CeresPort)) || x.FieldType.IsIListPort())
.ToList();
PortFieldInfoLookup.Add(nodeType, fields);
}
Expand Down Expand Up @@ -324,27 +311,6 @@ protected virtual void LinkPort(CeresPort port, CeresNode ownerNode, CeresPortDa
targetNode.NodeData.AddDependency(ownerNode.Guid);
}
}

private static bool IsIListPort(Type fieldType)
{
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
{
var genericArgument = fieldType.GetGenericArguments()[0];
if (typeof(CeresPort).IsAssignableFrom(genericArgument))
{
return true;
}
}
else if (fieldType.IsArray)
{
var elementType = fieldType.GetElementType();
if (typeof(CeresPort).IsAssignableFrom(elementType))
{
return true;
}
}
return false;
}

protected static void CollectDependencyPath(CeresGraph graph)
{
Expand Down Expand Up @@ -417,23 +383,28 @@ public virtual void Dispose()
{
foreach (var variable in variables)
{
variable.Unbind();
variable.Dispose();
}
variables.Clear();
foreach (var variable in _internalVariables)
{
variable.Unbind();
variable.Dispose();
}
_internalVariables.Clear();
foreach (var port in _internalPorts)
{
port.Dispose();
}
_internalPorts.Clear();

_nodeDependencyPath = null;
variables.Clear();
_internalVariables.Clear();
foreach (var node in GetAllNodes())
{
node.Dispose();
}
nodes.Clear();

if(_disposables != null)
if (_disposables != null)
{
foreach (var disposable in _disposables)
{
Expand Down Expand Up @@ -482,7 +453,7 @@ void VisitDependency(int destinationIndex, CeresNode current)
foreach (var dependency in dependencies)
{
var dependencyNode = graph.FindNode(dependency);
if(dependencyNode == null || dependencyNode.NodeData.executionPath == ExecutionPath.Forward)
if (dependencyNode == null || dependencyNode.NodeData.executionPath == ExecutionPath.Forward)
{
continue;
}
Expand Down
Loading

0 comments on commit efbedad

Please sign in to comment.