fix: allowing dependency walk

main
taoria 3 years ago
parent 82a78b84da
commit afd64d215e
  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. 6
      TNodeCore/Models/IModel.cs
  6. 1
      TNodeCore/Models/NodeData.cs
  7. 3
      TNodeCore/Models/PortInfo.cs
  8. 26
      TNodeCore/Runtime/RuntimeGraph.cs
  9. 37
      TNodeCore/Runtime/RuntimeNode.cs
  10. 8
      TNodeCore/RuntimeCache/IModelPropertyAccessor.cs
  11. 3
      TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta
  12. 133
      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%;
top: 45%;
font-size: 14;
font-size: 24px;
}
#TopMenu{
@ -20,4 +20,9 @@ GridBackground{
width: 100%;
height: 24px;
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 TNodeCore.Attribute;
using TNodeCore.Attribute.Ports;
using TNodeCore.RuntimeCache;
using UnityEngine;
namespace TNodeCore.Models{
[Serializable]
[InternalModel]
public class BlackboardDragNodeData:NodeData{
public string blackDragData;
[SerializeReference]

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

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

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

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

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Codice.Client.Common.TreeGrouper;
using TNodeCore.Attribute.Ports;
using TNodeCore.Models;
@ -9,49 +10,33 @@ namespace TNodeCore.Runtime{
public class RuntimeNode{
public NodeData NodeData { get; set; }
//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
public List<NodeLink> OutputLink;
public List<NodeLink> OutputLink = new List<NodeLink>();
//Cache node data type for fast access
private readonly Type _type;
public void SetInput(string portName,object value){
NodeData.SetValue(portName, value);
_portAccessors[portName].SetValue(this.NodeData,value);
}
public object GetOutput(string portName){
return NodeData.GetValue(portName);
return _portAccessors[portName].GetValue(this.NodeData);
}
private readonly Dictionary<string, IModelPropertyAccessor> _portAccessors;
private Dictionary<string,RuntimeCache.RuntimeCache.SetPropertyValueDelegate> _inputPorts = new();
private Dictionary<string,RuntimeCache.RuntimeCache.GetPropertyValueDelegate> _outputPorts = new();
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){
NodeData = nodeData;
//Caching the type of the node
_type = nodeData.GetType();
var info = nodeData.GetType().GetProperties();
CachingPorts();
_portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPropertyAccessors[_type];
}
public List<string> GetInputNodesId(){
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;
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{
//Singleton instance for the runtime cache
private static RuntimeCache _instance;
@ -18,18 +47,16 @@ namespace TNodeCore.RuntimeCache{
public delegate object GetValueDelegate(IModel nodeData);
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 =
new ();
public readonly Dictionary<Type,Dictionary<string,SetValueDelegate>> CachedDelegatesForSettingValue =
new ();
public readonly Dictionary<Type,Dictionary<string,GetPropertyValueDelegate>> CachedDelegatesForGettingPropertyValue =
new ();
public readonly Dictionary<Type,Dictionary<string,SetPropertyValueDelegate>> CachedDelegatesForSettingPropertyValue =
public readonly Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> CachedPropertyAccessors =
new ();
private readonly Dictionary<Type, Type> _graphBlackboardDictionary = new Dictionary<Type, Type>();
private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"};
@ -46,23 +73,28 @@ namespace TNodeCore.RuntimeCache{
if (attribute is GraphUsageAttribute){
//if the type has GraphUsageAttribute, add it to the cache
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
if(typeof(BlackboardData).IsAssignableFrom(type)){
//if it is, add it to the cache
AddBlackboardDataTypeToCache(type,attribute);
RegisterRuntimeBlackboard(type);
AddBlackboardDataTypeToCache(type,(GraphUsageAttribute)attribute);
CacheRuntimeBlackboard(type);
}
//Check if the type is a node data type
if(typeof(NodeData).IsAssignableFrom(type)){
//if it is, add it to the cache
RegisterRuntimeNodeData(type);
CacheRuntimeNodeData(type);
}
}
@ -87,7 +119,7 @@ namespace TNodeCore.RuntimeCache{
}
return null;
}
public void RegisterRuntimeBlackboard(Type type){
public void CacheRuntimeBlackboard(Type type){
if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
@ -114,29 +146,24 @@ namespace TNodeCore.RuntimeCache{
}
}
}
public void RegisterRuntimeNodeData(Type type){
public static IModelPropertyAccessor Create(string propName,Type targetType,Type valueType){
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(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
CachedPropertyAccessors.Add(type,new Dictionary<string, IModelPropertyAccessor>());
var properties = type.GetProperties();
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
var fields = type.GetFields();
@ -160,18 +187,9 @@ namespace TNodeCore.RuntimeCache{
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{
//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];
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){
CreateTestButton();
}
@ -74,7 +75,7 @@ namespace TNode.Editor.Inspector{
if(!test.IsRuntimeGraph) return;
var runtimeGraph = test.GetRuntimeGraph();
if (runtimeGraph != null){
runtimeGraph.ResolveDependency(_data);
var res = runtimeGraph.ResolveDependency(_data);
}
_data.OnTest();
}

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

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

Loading…
Cancel
Save