Merge pull request #23 from taoria/working-in-process

fix: allowing dependency walk
main
taoria 3 years ago committed by GitHub
commit 1ec54cdbb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      TNodeCore/Attribute/InternalModel.cs
  2. 3
      TNodeCore/Attribute/InternalModel.cs.meta
  3. 7
      TNodeCore/Editor/Resources/GraphViewBackground.uss
  4. 2
      TNodeCore/Models/BlackboardDragNodeData.cs
  5. 4
      TNodeCore/Models/IModel.cs
  6. 1
      TNodeCore/Models/NodeData.cs
  7. 3
      TNodeCore/Models/PortInfo.cs
  8. 22
      TNodeCore/Runtime/RuntimeGraph.cs
  9. 33
      TNodeCore/Runtime/RuntimeNode.cs
  10. 8
      TNodeCore/RuntimeCache/IModelPropertyAccessor.cs
  11. 3
      TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta
  12. 127
      TNodeCore/RuntimeCache/RuntimeCache.cs
  13. 5
      TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
  14. 10
      TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  15. 5
      TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs

@ -0,0 +1,10 @@
namespace TNodeCore.Attribute{
/// <summary>
/// Internal use only. so that Editor Cache and Runtime cache could register it globally.
/// </summary>
public class InternalModel:System.Attribute{
public InternalModel(){
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcd3a2f1670c4bb2b5ef98337ba785d6
timeCreated: 1658132721

@ -10,7 +10,7 @@ GridBackground{
left: 45%; left: 45%;
top: 45%; top: 45%;
font-size: 14; font-size: 24px;
} }
#TopMenu{ #TopMenu{
@ -21,3 +21,8 @@ GridBackground{
height: 24px; height: 24px;
background-color: #171717; background-color: #171717;
} }
.unity-text-element--inner-input-field-component{
width: 100%;
min-width: 70px;
max-width: 250px;
}

@ -1,10 +1,12 @@
using System; using System;
using TNodeCore.Attribute;
using TNodeCore.Attribute.Ports; using TNodeCore.Attribute.Ports;
using TNodeCore.RuntimeCache; using TNodeCore.RuntimeCache;
using UnityEngine; using UnityEngine;
namespace TNodeCore.Models{ namespace TNodeCore.Models{
[Serializable] [Serializable]
[InternalModel]
public class BlackboardDragNodeData:NodeData{ public class BlackboardDragNodeData:NodeData{
public string blackDragData; public string blackDragData;
[SerializeReference] [SerializeReference]

@ -1,4 +1,6 @@
namespace TNodeCore.Models{ using System;
namespace TNodeCore.Models{
public interface IModel{ public interface IModel{
} }

@ -22,7 +22,6 @@ namespace TNodeCore.Models{
public string nodeName; public string nodeName;
public bool entryPoint; public bool entryPoint;
public virtual void Process(){ public virtual void Process(){
} }

@ -1,9 +1,10 @@
using System; using System;
using UnityEngine.Serialization;
namespace TNodeCore.Models{ namespace TNodeCore.Models{
[Serializable] [Serializable]
public class PortInfo{ public class PortInfo{
public string portName; public string portEntryName;
public string nodeDataId; public string nodeDataId;
} }
} }

@ -33,8 +33,8 @@ namespace TNodeCore.Runtime{
//out node is node output data //out node is node output data
//in node is node receive data //in node is node receive data
var outValue = outNode.GetOutput(nodeLink.outPort.portName); var outValue = outNode.GetOutput(nodeLink.outPort.portEntryName);
inNode.SetInput(nodeLink.inPort.portName, outValue); inNode.SetInput(nodeLink.inPort.portEntryName, outValue);
} }
public GraphTool(List<RuntimeNode> list, Dictionary<string, RuntimeNode> graphNodes){ public GraphTool(List<RuntimeNode> list, Dictionary<string, RuntimeNode> graphNodes){
RuntimeNodes = graphNodes; RuntimeNodes = graphNodes;
@ -66,7 +66,7 @@ namespace TNodeCore.Runtime{
if(TopologicalOrder.Count!= list.Count){ if(TopologicalOrder.Count!= list.Count){
throw new Exception("Topological sort failed,circular dependency detected"); throw new Exception("Topological sort failed,circular dependency detected");
} }
RuntimeNodes.Clear();
inDegreeCounterForTopologicalSort.Clear(); inDegreeCounterForTopologicalSort.Clear();
queue.Clear(); queue.Clear();
} }
@ -75,7 +75,7 @@ namespace TNodeCore.Runtime{
} }
[SerializeReference] [SerializeReference]
public BlackboardData runtimeBlackboardData; public BlackboardData runtimeBlackboardData;
[NonSerialized]
private bool _build = false; private bool _build = false;
public void Build(){ public void Build(){
@ -85,6 +85,7 @@ namespace TNodeCore.Runtime{
ModifyOrCreateInNode(linkData); ModifyOrCreateInNode(linkData);
ModifyOrCreateOutNode(linkData); ModifyOrCreateOutNode(linkData);
} }
Debug.Log("hi");
var nodeList = RuntimeNodes.Values; var nodeList = RuntimeNodes.Values;
_graphTool = new GraphTool(nodeList.ToList(),RuntimeNodes); _graphTool = new GraphTool(nodeList.ToList(),RuntimeNodes);
_build = true; _build = true;
@ -93,7 +94,6 @@ namespace TNodeCore.Runtime{
public RuntimeNode Get(NodeData nodeData){ public RuntimeNode Get(NodeData nodeData){
if(!_build) if(!_build)
Build(); Build();
if(RuntimeNodes.ContainsKey(nodeData.id)){ if(RuntimeNodes.ContainsKey(nodeData.id)){
return RuntimeNodes[nodeData.id]; return RuntimeNodes[nodeData.id];
} }
@ -104,11 +104,12 @@ namespace TNodeCore.Runtime{
if (RuntimeNodes.ContainsKey(id)){ if (RuntimeNodes.ContainsKey(id)){
return RuntimeNodes[id]; return RuntimeNodes[id];
} }
return null; return null;
} }
//DFS search for resolving dependency //DFS search for resolving dependency
public bool ResolveDependency(NodeData startNode){ public bool ResolveDependency(NodeData startNode){
if(!_build)
Build();
if (_graphTool == null) if (_graphTool == null)
return false; return false;
_graphTool.DependencyTraversal(Get(startNode)); _graphTool.DependencyTraversal(Get(startNode));
@ -141,6 +142,15 @@ namespace TNodeCore.Runtime{
} }
} }
public void OnDisable(){
RuntimeNodes.Clear();
_build = false;
}
public void OnDestroy(){
RuntimeNodes.Clear();
_build = false;
}
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using Codice.Client.Common.TreeGrouper; using Codice.Client.Common.TreeGrouper;
using TNodeCore.Attribute.Ports; using TNodeCore.Attribute.Ports;
using TNodeCore.Models; using TNodeCore.Models;
@ -9,49 +10,33 @@ namespace TNodeCore.Runtime{
public class RuntimeNode{ public class RuntimeNode{
public NodeData NodeData { get; set; } public NodeData NodeData { get; set; }
//the link connect to node's in port //the link connect to node's in port
public List<NodeLink> InputLink; public List<NodeLink> InputLink = new List<NodeLink>();
//the link connect to node's out port //the link connect to node's out port
public List<NodeLink> OutputLink; public List<NodeLink> OutputLink = new List<NodeLink>();
//Cache node data type for fast access //Cache node data type for fast access
private readonly Type _type; private readonly Type _type;
public void SetInput(string portName,object value){ public void SetInput(string portName,object value){
NodeData.SetValue(portName, value); _portAccessors[portName].SetValue(this.NodeData,value);
} }
public object GetOutput(string portName){ public object GetOutput(string portName){
return NodeData.GetValue(portName);
}
return _portAccessors[portName].GetValue(this.NodeData);
}
private Dictionary<string,RuntimeCache.RuntimeCache.SetPropertyValueDelegate> _inputPorts = new();
private Dictionary<string,RuntimeCache.RuntimeCache.GetPropertyValueDelegate> _outputPorts = new();
private readonly Dictionary<string, IModelPropertyAccessor> _portAccessors;
private void CachingPorts(){
var properties = _type.GetProperties();
foreach (var propertyInfo in properties){
//Check if the property is a port
var attribute = propertyInfo.GetCustomAttributes(typeof(InputAttribute),true);
if (attribute.Length > 0){
_inputPorts.Add(propertyInfo.Name,NodeData.CacheSetProperty(propertyInfo.Name));
}
attribute = propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true);
if (attribute.Length > 0){
_outputPorts.Add(propertyInfo.Name,NodeData.CacheGetProperty(propertyInfo.Name));
}
}
}
public RuntimeNode(NodeData nodeData){ public RuntimeNode(NodeData nodeData){
NodeData = nodeData; NodeData = nodeData;
//Caching the type of the node //Caching the type of the node
_type = nodeData.GetType(); _type = nodeData.GetType();
var info = nodeData.GetType().GetProperties(); var info = nodeData.GetType().GetProperties();
CachingPorts(); _portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPropertyAccessors[_type];
} }
public List<string> GetInputNodesId(){ public List<string> GetInputNodesId(){
List<string> dependencies = new List<string>(); List<string> dependencies = new List<string>();

@ -0,0 +1,8 @@
using System;
namespace TNodeCore.RuntimeCache{
public interface IModelPropertyAccessor{
object GetValue(object model);
void SetValue(object model, object value);
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcaebfe910b84f5dbd81bc4330fe6f7b
timeCreated: 1658131431

@ -8,6 +8,35 @@ using TNodeCore.Models;
using UnityEngine; using UnityEngine;
namespace TNodeCore.RuntimeCache{ namespace TNodeCore.RuntimeCache{
public class PropAccessor<T1, T2>:IModelPropertyAccessor{
public readonly Func<T1, T2> Get;
public readonly Action<T1, T2> Set;
public PropAccessor(string propName){
Type t = typeof(T1);
MethodInfo getter = t.GetMethod("get_" + propName);
MethodInfo setter = t.GetMethod("set_" + propName);
if(getter!=null)
Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
if(setter!=null)
Set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
}
public static PropAccessor<T1, T2> Create(string propName){
return new PropAccessor<T1, T2>(propName);
}
public object GetValue(object model){
return Get((T1)model);
}
public void SetValue(object model, object value){
Set((T1)model,(T2)value);
}
}
public class PropertyNotFoundException : Exception{
public PropertyNotFoundException(string path):base("Property not found :"+path){
}
}
public class RuntimeCache{ public class RuntimeCache{
//Singleton instance for the runtime cache //Singleton instance for the runtime cache
private static RuntimeCache _instance; private static RuntimeCache _instance;
@ -18,18 +47,16 @@ namespace TNodeCore.RuntimeCache{
public delegate object GetValueDelegate(IModel nodeData); public delegate object GetValueDelegate(IModel nodeData);
public delegate void SetValueDelegate(IModel nodeData,object value); public delegate void SetValueDelegate(IModel nodeData,object value);
public delegate object GetPropertyValueDelegate();
public delegate void SetPropertyValueDelegate(object value);
public readonly Dictionary<Type, Dictionary<string,GetValueDelegate>> CachedDelegatesForGettingValue = public readonly Dictionary<Type, Dictionary<string,GetValueDelegate>> CachedDelegatesForGettingValue =
new (); new ();
public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue = public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue =
new (); new ();
public readonly Dictionary<Type,Dictionary<string,GetPropertyValueDelegate>> CachedDelegatesForGettingPropertyValue = public readonly Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> CachedPropertyAccessors =
new ();
public readonly Dictionary<Type,Dictionary<string,SetPropertyValueDelegate>> CachedDelegatesForSettingPropertyValue =
new (); new ();
private readonly Dictionary<Type, Type> _graphBlackboardDictionary = new Dictionary<Type, Type>(); private readonly Dictionary<Type, Type> _graphBlackboardDictionary = new Dictionary<Type, Type>();
private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"}; private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"};
@ -46,23 +73,28 @@ namespace TNodeCore.RuntimeCache{
if (attribute is GraphUsageAttribute){ if (attribute is GraphUsageAttribute){
//if the type has GraphUsageAttribute, add it to the cache //if the type has GraphUsageAttribute, add it to the cache
AddTypeToCache(type,attribute as GraphUsageAttribute); AddTypeToCache(type,attribute as GraphUsageAttribute);
}
if (attribute is InternalModel){
AddTypeToCache(type,attribute as InternalModel);
} }
} }
} }
} }
private void AddTypeToCache(Type type,GraphUsageAttribute attribute){ private void AddTypeToCache(Type type,System.Attribute attribute){
//Check if the type is a blackboard data type //Check if the type is a blackboard data type
if(typeof(BlackboardData).IsAssignableFrom(type)){ if(typeof(BlackboardData).IsAssignableFrom(type)){
//if it is, add it to the cache //if it is, add it to the cache
AddBlackboardDataTypeToCache(type,attribute); AddBlackboardDataTypeToCache(type,(GraphUsageAttribute)attribute);
RegisterRuntimeBlackboard(type); CacheRuntimeBlackboard(type);
} }
//Check if the type is a node data type //Check if the type is a node data type
if(typeof(NodeData).IsAssignableFrom(type)){ if(typeof(NodeData).IsAssignableFrom(type)){
//if it is, add it to the cache //if it is, add it to the cache
RegisterRuntimeNodeData(type); CacheRuntimeNodeData(type);
} }
} }
@ -87,7 +119,7 @@ namespace TNodeCore.RuntimeCache{
} }
return null; return null;
} }
public void RegisterRuntimeBlackboard(Type type){ public void CacheRuntimeBlackboard(Type type){
if (type == null) return; if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){ if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>()); CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
@ -114,29 +146,24 @@ namespace TNodeCore.RuntimeCache{
} }
} }
} }
public static IModelPropertyAccessor Create(string propName,Type targetType,Type valueType){
public void RegisterRuntimeNodeData(Type type){ var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType);
var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)});
var instance = constructor?.Invoke(new object[]{propName});
return (IModelPropertyAccessor) instance;
}
public void CacheRuntimeNodeData(Type type){
if (type == null) return; if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){ if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>()); CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>()); CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
CachedPropertyAccessors.Add(type,new Dictionary<string, IModelPropertyAccessor>());
var properties = type.GetProperties(); var properties = type.GetProperties();
foreach(var property in properties){ foreach(var property in properties){
//if the property only has a setter ,skip
// if(property.GetSetMethod(false) != null){
// var setValueDelegate = SetValueDelegateForProperty(property);
// CachedDelegatesForSettingPropertyValue[type].Add(property.Name,setValueDelegate);
// }
// if(property.GetMethod != null){
// var getValueDelegate = GetValueDelegateForProperty(property);
// CachedDelegatesForGettingPropertyValue[type].Add(property.Name,getValueDelegate);
// }
var propertyAccessor = Create(property.Name,type,property.PropertyType);
CachedPropertyAccessors[type].Add(property.Name,propertyAccessor);
} }
//register the fields //register the fields
var fields = type.GetFields(); var fields = type.GetFields();
@ -160,18 +187,9 @@ namespace TNodeCore.RuntimeCache{
return field.SetValue; return field.SetValue;
} }
private GetPropertyValueDelegate GetValueDelegateForProperty(PropertyInfo property){
var getValueDelegate = (GetPropertyValueDelegate)Delegate.CreateDelegate(typeof(GetPropertyValueDelegate), property.GetGetMethod());
return getValueDelegate;
}
private SetPropertyValueDelegate SetValueDelegateForProperty(PropertyInfo property){
Debug.Log(property.GetSetMethod());
var setValueDelegate = (SetPropertyValueDelegate)Delegate.CreateDelegate(typeof(SetPropertyValueDelegate), property.GetSetMethod());
return setValueDelegate;
} }
}
public static class RuntimeExtension{ public static class RuntimeExtension{
//todo latter on i will try some way caching reflection more efficiently //todo latter on i will try some way caching reflection more efficiently
@ -192,46 +210,5 @@ namespace TNodeCore.RuntimeCache{
var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path]; var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path];
method.Invoke(data,value); method.Invoke(data,value);
} }
public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){
var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
return method;
}
/// <summary>
/// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
/// </summary>
/// <param name="data"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="PropertyNotFoundException"></exception>
public static RuntimeCache.GetPropertyValueDelegate CacheGetProperty(this IModel data,string path){
var type = data.GetType();
var property = type.GetProperty(path);
if (property == null) throw new PropertyNotFoundException(path);
var instance = Delegate.CreateDelegate(typeof(RuntimeCache.GetPropertyValueDelegate), data,
property.GetGetMethod());
return instance as RuntimeCache.GetPropertyValueDelegate;
}
/// <summary>
/// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
/// </summary>
/// <param name="data"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="PropertyNotFoundException"></exception>
public static RuntimeCache.SetPropertyValueDelegate CacheSetProperty(this IModel data,string path){
var type = data.GetType();
var property = type.GetProperty(path);
if (property == null) throw new PropertyNotFoundException(path);
var instance = Delegate.CreateDelegate(typeof(RuntimeCache.SetPropertyValueDelegate), data,
property.GetSetMethod());
return instance as RuntimeCache.SetPropertyValueDelegate;
}
}
public class PropertyNotFoundException : Exception{
public PropertyNotFoundException(string path):base("Property not found :"+path){
}
} }
} }

@ -57,7 +57,8 @@ namespace TNode.Editor.Inspector{
} }
var globalTest = GetFirstAncestorOfType<IBaseNodeView>()?.BaseDataGraphView?.TestMode;
var globalTest = GetFirstAncestorOfType<IBaseDataGraphView>()?.TestMode;
if(globalTest??false){ if(globalTest??false){
CreateTestButton(); CreateTestButton();
} }
@ -74,7 +75,7 @@ namespace TNode.Editor.Inspector{
if(!test.IsRuntimeGraph) return; if(!test.IsRuntimeGraph) return;
var runtimeGraph = test.GetRuntimeGraph(); var runtimeGraph = test.GetRuntimeGraph();
if (runtimeGraph != null){ if (runtimeGraph != null){
runtimeGraph.ResolveDependency(_data); var res = runtimeGraph.ResolveDependency(_data);
} }
_data.OnTest(); _data.OnTest();
} }

@ -71,12 +71,10 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
/// Probably reusable in later GTFs version /// Probably reusable in later GTFs version
/// </summary> /// </summary>
private void WaitingForAGraph(){ private void WaitingForAGraph(){
Debug.Log("hello");
VisualElement visualElement = new VisualElement(); VisualElement visualElement = new VisualElement();
//Set background color to white //Set background color to white
visualElement.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1)); visualElement.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1));
Debug.Log("hello2");
visualElement.StretchToParentSize(); visualElement.StretchToParentSize();
visualElement.name = "WaitingForAGraph"; visualElement.name = "WaitingForAGraph";
Add(visualElement); Add(visualElement);
@ -279,8 +277,8 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
var outputNodeView = _nodeDict[outputNode.id]; var outputNodeView = _nodeDict[outputNode.id];
Edge newEdge = new Edge(){ Edge newEdge = new Edge(){
input = inputNodeView.inputContainer.Q<Port>(edge.inPort.portName), input = inputNodeView.inputContainer.Q<Port>(edge.inPort.portEntryName),
output = outputNodeView.outputContainer.Q<Port>(edge.outPort.portName) output = outputNodeView.outputContainer.Q<Port>(edge.outPort.portEntryName)
}; };
newEdge.input?.Connect(newEdge); newEdge.input?.Connect(newEdge);
@ -378,11 +376,11 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
var outputNodeData = outputNode.GetNodeData(); var outputNodeData = outputNode.GetNodeData();
var newNodeLink = new NodeLink(new PortInfo(){ var newNodeLink = new NodeLink(new PortInfo(){
nodeDataId = inputNodeData.id, nodeDataId = inputNodeData.id,
portName = edge.input.portName, portEntryName = edge.input.name,
}, new PortInfo(){ }, new PortInfo(){
nodeDataId = outputNodeData.id, nodeDataId = outputNodeData.id,
portName = edge.output.portName portEntryName = edge.output.name
}); });
links.Add(newNodeLink); links.Add(newNodeLink);
} }

@ -108,7 +108,8 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
this.outputContainer.Add(port); this.outputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo); var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName; port.portName = portName;
port.name = portName; port.name = propertyInfo.Name;
} }
} }
foreach (var propertyInfo in propertyInfos){ foreach (var propertyInfo in propertyInfos){
@ -117,7 +118,7 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
this.inputContainer.Add(port); this.inputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo); var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName; port.portName = portName;
port.name = portName; port.name = propertyInfo.Name;
} }
} }
} }

Loading…
Cancel
Save