mirror of
https://github.com/Siccity/xNode.git
synced 2025-12-20 01:06:01 +08:00
Initialization performance updates (wip)
This commit is contained in:
parent
e502e23b50
commit
76e8b70316
9
Resources.meta
Normal file
9
Resources.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65ed9b337b7f0b74ab007799bb407a5e
|
||||
folderAsset: yes
|
||||
timeCreated: 1507567488
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Resources/NodeDataCache.asset
Normal file
BIN
Resources/NodeDataCache.asset
Normal file
Binary file not shown.
9
Resources/NodeDataCache.asset.meta
Normal file
9
Resources/NodeDataCache.asset.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6799b9b87b05a14f9053390f4500d7b
|
||||
timeCreated: 1507567505
|
||||
licenseType: Free
|
||||
NativeFormatImporter:
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -63,14 +63,14 @@ public class NodeEditor {
|
||||
/// <summary> Draw node port GUI using automatic layouting. Returns port handle position. </summary>
|
||||
protected Vector2 DrawNodePortGUI(NodePort port) {
|
||||
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputPort : NodeEditorResources.styles.outputPort;
|
||||
Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name.PrettifyCamelCase()), style);
|
||||
Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.fieldName.PrettifyCamelCase()), style);
|
||||
return DrawNodePortGUI(rect, port);
|
||||
}
|
||||
|
||||
/// <summary> Draw node port GUI in rect. Returns port handle position. </summary>
|
||||
protected Vector2 DrawNodePortGUI(Rect rect, NodePort port) {
|
||||
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputPort : NodeEditorResources.styles.outputPort;
|
||||
GUI.Label(rect, new GUIContent(port.name.PrettifyCamelCase()), style);
|
||||
GUI.Label(rect, new GUIContent(port.fieldName.PrettifyCamelCase()), style);
|
||||
|
||||
Vector2 handlePoint = rect.center;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ public partial class NodeEditorWindow {
|
||||
if (IsHoveringNode) {
|
||||
GUILayout.Space(20);
|
||||
string hoverInfo = hoveredNode.GetType().ToString();
|
||||
if (IsHoveringPort) hoverInfo += " > " + hoveredPort.name;
|
||||
if (IsHoveringPort) hoverInfo += " > " + hoveredPort.fieldName;
|
||||
GUILayout.Label(hoverInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,12 +20,13 @@ public abstract class Node : ScriptableObject {
|
||||
public int OutputCount { get { return outputs.Count; } }
|
||||
|
||||
protected Node() {
|
||||
CachePorts(); //Cache the ports at creation time so we don't have to use reflection at runtime
|
||||
|
||||
GetPorts(); //Cache the ports at creation time so we don't have to use reflection at runtime
|
||||
}
|
||||
|
||||
protected void OnEnable() {
|
||||
VerifyConnections();
|
||||
CachePorts();
|
||||
GetPorts();
|
||||
Init();
|
||||
}
|
||||
|
||||
@ -117,45 +118,7 @@ public abstract class Node : ScriptableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Use reflection to find all fields with <see cref="InputAttribute"/> or <see cref="OutputAttribute"/>, and write to <see cref="inputs"/> and <see cref="outputs"/> </summary>
|
||||
private void CachePorts() {
|
||||
List<NodePort> inputPorts = new List<NodePort>();
|
||||
List<NodePort> outputPorts = new List<NodePort>();
|
||||
|
||||
System.Reflection.FieldInfo[] fieldInfo = GetType().GetFields();
|
||||
for (int i = 0; i < fieldInfo.Length; i++) {
|
||||
|
||||
//Get InputAttribute and OutputAttribute
|
||||
object[] attribs = fieldInfo[i].GetCustomAttributes(false);
|
||||
InputAttribute inputAttrib = null;
|
||||
OutputAttribute outputAttrib = null;
|
||||
for (int k = 0; k < attribs.Length; k++) {
|
||||
if (attribs[k] is InputAttribute) inputAttrib = attribs[k] as InputAttribute;
|
||||
else if (attribs[k] is OutputAttribute) outputAttrib = attribs[k] as OutputAttribute;
|
||||
}
|
||||
|
||||
if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
|
||||
else if (inputAttrib != null) inputPorts.Add(new NodePort(fieldInfo[i], this));
|
||||
else if (outputAttrib != null) outputPorts.Add(new NodePort(fieldInfo[i], this));
|
||||
}
|
||||
|
||||
//Remove
|
||||
for (int i = inputs.Count-1; i >= 0; i--) {
|
||||
//If input nodeport does not exist, remove it
|
||||
if (!inputPorts.Any(x => inputs[i].fieldName == x.fieldName)) inputs.RemoveAt(i);
|
||||
}
|
||||
for (int i = outputs.Count - 1; i >= 0; i--) {
|
||||
//If output nodeport does not exist, remove it
|
||||
if (!outputPorts.Any(x => outputs[i].fieldName == x.fieldName)) outputs.RemoveAt(i);
|
||||
}
|
||||
//Add
|
||||
for (int i = 0; i < inputPorts.Count; i++) {
|
||||
//If inputports contains a new port, add it
|
||||
if (!inputs.Any(x => x.fieldName == inputPorts[i].fieldName)) inputs.Add(inputPorts[i]);
|
||||
}
|
||||
for (int i = 0; i < outputPorts.Count; i++) {
|
||||
//If inputports contains a new port, add it
|
||||
if (!outputs.Any(x => x.fieldName == outputPorts[i].fieldName)) outputs.Add(outputPorts[i]);
|
||||
}
|
||||
private void GetPorts() {
|
||||
NodeDataCache.GetPorts(this, out inputs, out outputs);
|
||||
}
|
||||
}
|
||||
|
||||
113
Scripts/NodeDataCache.cs
Normal file
113
Scripts/NodeDataCache.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary> Precaches reflection data in editor so we won't have to do it runtime </summary>
|
||||
public sealed class NodeDataCache : ScriptableObject {
|
||||
public static NodeDataCache instance { get { return _instance; } }
|
||||
private static NodeDataCache _instance;
|
||||
|
||||
[SerializeField]
|
||||
private PortDataCache portDataCache = new PortDataCache();
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void InitializeInstance() {
|
||||
Debug.Log("INIT");
|
||||
NodeDataCache[] ndc = Resources.FindObjectsOfTypeAll<NodeDataCache>();
|
||||
if (ndc == null || ndc.Length == 0) {
|
||||
Debug.LogWarning("No NodeDataCache found. Creating.");
|
||||
_instance = ScriptableObject.CreateInstance<NodeDataCache>();
|
||||
_instance.BuildCache();
|
||||
}
|
||||
else if (ndc.Length > 1) {
|
||||
Debug.LogWarning("Multiple NodeDataCaches found.");
|
||||
}
|
||||
_instance = ndc[0];
|
||||
}
|
||||
|
||||
/// <summary> Return port data from cache </summary>
|
||||
public static void GetPorts(Node node, out List<NodePort> inputs, out List<NodePort> outputs) {
|
||||
if (_instance == null) InitializeInstance();
|
||||
|
||||
System.Type nodeType = node.GetType();
|
||||
inputs = new List<NodePort>();
|
||||
outputs = new List<NodePort>();
|
||||
if (!_instance.portDataCache.ContainsKey(nodeType)) return;
|
||||
for (int i = 0; i < _instance.portDataCache[nodeType].Count; i++) {
|
||||
if (_instance.portDataCache[nodeType][i].direction == NodePort.IO.Input) inputs.Add(new NodePort(_instance.portDataCache[nodeType][i], node));
|
||||
else outputs.Add(new NodePort(_instance.portDataCache[nodeType][i], node));
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.InitializeOnLoadMethod]
|
||||
#endif
|
||||
private static void Init() {
|
||||
instance.BuildCache();
|
||||
}
|
||||
|
||||
private void BuildCache() {
|
||||
System.Type baseType = typeof(Node);
|
||||
Assembly assembly = Assembly.GetAssembly(baseType);
|
||||
System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
|
||||
!t.IsAbstract &&
|
||||
baseType.IsAssignableFrom(t)
|
||||
).ToArray();
|
||||
portDataCache.Clear();
|
||||
|
||||
for (int i = 0; i < nodeTypes.Length; i++) {
|
||||
CachePorts(nodeTypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void CachePorts(System.Type nodeType) {
|
||||
List<NodePort> inputPorts = new List<NodePort>();
|
||||
List<NodePort> outputPorts = new List<NodePort>();
|
||||
|
||||
System.Reflection.FieldInfo[] fieldInfo = nodeType.GetFields();
|
||||
for (int i = 0; i < fieldInfo.Length; i++) {
|
||||
|
||||
//Get InputAttribute and OutputAttribute
|
||||
object[] attribs = fieldInfo[i].GetCustomAttributes(false);
|
||||
Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
|
||||
Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
|
||||
|
||||
if (inputAttrib == null && outputAttrib == null) continue;
|
||||
|
||||
if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
|
||||
else {
|
||||
if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List<NodePort>());
|
||||
portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
private class PortDataCache : Dictionary<System.Type, List<NodePort>>, ISerializationCallbackReceiver {
|
||||
[SerializeField] private List<System.Type> keys = new List<System.Type>();
|
||||
[SerializeField] private List<List<NodePort>> values = new List<List<NodePort>>();
|
||||
|
||||
// save the dictionary to lists
|
||||
public void OnBeforeSerialize() {
|
||||
keys.Clear();
|
||||
values.Clear();
|
||||
foreach (var pair in this) {
|
||||
keys.Add(pair.Key);
|
||||
values.Add(pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// load dictionary from lists
|
||||
public void OnAfterDeserialize() {
|
||||
this.Clear();
|
||||
|
||||
if (keys.Count != values.Count)
|
||||
throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
|
||||
|
||||
for (int i = 0; i < keys.Count; i++)
|
||||
this.Add(keys[i], values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/NodeDataCache.cs.meta
Normal file
12
Scripts/NodeDataCache.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64ea6af1e195d024d8df0ead1921e517
|
||||
timeCreated: 1507566823
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -19,7 +19,6 @@ public class NodePort {
|
||||
public bool IsOutput { get { return direction == IO.Output; } }
|
||||
|
||||
public Node node { get; private set; }
|
||||
[SerializeField] public string name;
|
||||
public bool enabled { get { return _enabled; } set { _enabled = value; } }
|
||||
public string fieldName { get { return _fieldName; } }
|
||||
|
||||
@ -30,11 +29,9 @@ public class NodePort {
|
||||
[SerializeField] private bool _enabled = true;
|
||||
[SerializeField] private IO _direction;
|
||||
|
||||
public NodePort(FieldInfo fieldInfo, Node node) {
|
||||
public NodePort(FieldInfo fieldInfo) {
|
||||
_fieldName = fieldInfo.Name;
|
||||
name = _fieldName;
|
||||
type = fieldInfo.FieldType;
|
||||
this.node = node;
|
||||
|
||||
var attribs = fieldInfo.GetCustomAttributes(false);
|
||||
for (int i = 0; i < attribs.Length; i++) {
|
||||
@ -43,6 +40,13 @@ public class NodePort {
|
||||
}
|
||||
}
|
||||
|
||||
public NodePort(NodePort nodePort, Node node) {
|
||||
_fieldName = nodePort._fieldName;
|
||||
type = nodePort.type;
|
||||
this.node = node;
|
||||
_direction = nodePort.direction;
|
||||
}
|
||||
|
||||
/// <summary> Checks all connections for invalid references, and removes them. </summary>
|
||||
public void VerifyConnections() {
|
||||
for (int i = 0; i < connections.Count; i++) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user