Browse Source

FlowchartWindow refactored Selected Block drawing to avoid O(2N) .Contains calls

Added a testing scene for editor performance, for many blocks on 1 flowhcart
master
desktop-maesty/steve 6 years ago
parent
commit
7b4aaea648
  1. 16
      Assets/Fungus/Scripts/Components/Flowchart.cs
  2. 250
      Assets/Fungus/Scripts/Editor/FlowchartWindow.cs
  3. 20382
      Assets/Tests/Perf/ManyBlocks_700+.unity
  4. 7
      Assets/Tests/Perf/ManyBlocks_700+.unity.meta

16
Assets/Fungus/Scripts/Components/Flowchart.cs

@ -341,7 +341,10 @@ namespace Fungus
{ {
get get
{ {
return selectedBlocks.FirstOrDefault(); if (selectedBlocks == null || selectedBlocks.Count == 0)
return null;
return selectedBlocks[0];
} }
set set
{ {
@ -1135,15 +1138,20 @@ namespace Fungus
public virtual bool DeselectBlock(Block block) public virtual bool DeselectBlock(Block block)
{ {
if (!selectedBlocks.Contains(block)) if (selectedBlocks.Contains(block))
{ {
block.IsSelected = false; DeselectBlockNoCheck(block);
selectedBlocks.Remove(block);
return true; return true;
} }
return false; return false;
} }
public virtual void DeselectBlockNoCheck(Block b)
{
b.IsSelected = false;
selectedBlocks.Remove(b);
}
#endif #endif
/// <summary> /// <summary>
/// Reset the commands and variables in the Flowchart. /// Reset the commands and variables in the Flowchart.

250
Assets/Fungus/Scripts/Editor/FlowchartWindow.cs

@ -168,7 +168,14 @@ namespace Fungus.EditorUtils
protected void UpdateBlockCollection() protected void UpdateBlockCollection()
{ {
flowchart = GetFlowchart(); flowchart = GetFlowchart();
blocks = flowchart.GetComponents<Block>(); if (flowchart == null)
{
blocks = new Block[0];
}
else
{
blocks = flowchart.GetComponents<Block>();
}
filterStale = true; filterStale = true;
UpdateFilteredBlocks(); UpdateFilteredBlocks();
} }
@ -393,7 +400,6 @@ namespace Fungus.EditorUtils
//prevBlockCount = blocks.Length; //prevBlockCount = blocks.Length;
UpdateFilteredBlocks(); UpdateFilteredBlocks();
BringSelectedBlockToFront();
HandleEarlyEvents(Event.current); HandleEarlyEvents(Event.current);
@ -679,20 +685,6 @@ namespace Fungus.EditorUtils
return null; return null;
} }
protected void BringSelectedBlockToFront()
{
var block = flowchart.SelectedBlock;
if (block != null)
{
var index = Array.IndexOf(blocks, block);
for (int i = index; i < blocks.Length - 1; ++i)
{
blocks[i] = blocks[i + 1];
}
blocks[blocks.Length - 1] = block;
}
}
protected override void OnMouseDown(Event e) protected override void OnMouseDown(Event e)
{ {
var hitBlock = GetBlockAtPoint(e.mousePosition); var hitBlock = GetBlockAtPoint(e.mousePosition);
@ -793,6 +785,9 @@ namespace Fungus.EditorUtils
// Selection box // Selection box
else if (startSelectionBoxPosition.x >= 0 && startSelectionBoxPosition.y >= 0) else if (startSelectionBoxPosition.x >= 0 && startSelectionBoxPosition.y >= 0)
{ {
if (Mathf.Approximately(e.delta.magnitude, 0))
break;
var topLeft = Vector2.Min(startSelectionBoxPosition, e.mousePosition); var topLeft = Vector2.Min(startSelectionBoxPosition, e.mousePosition);
var bottomRight = Vector2.Max(startSelectionBoxPosition, e.mousePosition); var bottomRight = Vector2.Max(startSelectionBoxPosition, e.mousePosition);
selectionBox = Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); selectionBox = Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
@ -805,11 +800,12 @@ namespace Fungus.EditorUtils
for (int i = 0; i < blocks.Length; ++i) for (int i = 0; i < blocks.Length; ++i)
{ {
var block = blocks[i]; var block = blocks[i];
if (zoomSelectionBox.Overlaps(block._NodeRect)) var doesMarqueOverlap = zoomSelectionBox.Overlaps(block._NodeRect);
if (doesMarqueOverlap)
{ {
if (mouseDownSelectionState.Contains(block)) if (mouseDownSelectionState.Contains(block))
{ {
flowchart.DeselectBlock(block); flowchart.DeselectBlockNoCheck(block);
} }
else else
{ {
@ -820,9 +816,10 @@ namespace Fungus.EditorUtils
{ {
flowchart.AddSelectedBlock(block); flowchart.AddSelectedBlock(block);
} }
else
if (block.IsSelected && !doesMarqueOverlap)
{ {
flowchart.DeselectBlock(block); flowchart.DeselectBlockNoCheck(block);
} }
} }
e.Use(); e.Use();
@ -961,11 +958,11 @@ namespace Fungus.EditorUtils
e.Use(); e.Use();
} }
} }
protected virtual void DrawFlowchartView(Event e) protected virtual void DrawFlowchartView(Event e)
{ {
// Calc rect for script view // Calc rect for script view
Rect scriptViewRect = new Rect(0, 0, this.position.width / flowchart.Zoom, this.position.height / flowchart.Zoom); Rect scriptViewRect = CalcFlowchartWindowViewRect();
EditorZoomArea.Begin(flowchart.Zoom, scriptViewRect); EditorZoomArea.Begin(flowchart.Zoom, scriptViewRect);
@ -976,105 +973,26 @@ namespace Fungus.EditorUtils
// Draw connections // Draw connections
foreach (var block in blocks) foreach (var block in blocks)
{ {
DrawConnections(block, false); DrawConnections(block);
} }
foreach (var block in blocks)
//draw all non selected
for (int i = 0; i < blocks.Length; ++i)
{ {
DrawConnections(block, true); var block = blocks[i];
if (!block.IsSelected)
DrawBlock(blocks[i], scriptViewRect);
} }
//draw all selected
for (int i = 0; i < blocks.Length; ++i) for (int i = 0; i < blocks.Length; ++i)
{ {
var block = blocks[i]; var block = blocks[i];
if (block.IsSelected)
float nodeWidthA = nodeStyle.CalcSize(new GUIContent(block.BlockName)).x + 10; DrawBlock(blocks[i], scriptViewRect);
float nodeWidthB = 0f;
if (block._EventHandler != null)
{
nodeWidthB = nodeStyle.CalcSize(new GUIContent(block._EventHandler.GetSummary())).x + 10;
}
Rect tempRect = block._NodeRect;
tempRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120);
tempRect.height = 40;
block._NodeRect = tempRect;
Rect windowRect = new Rect(block._NodeRect);
windowRect.position += flowchart.ScrollPos;
// Draw blocks
bool selected = block.IsSelected;
GUIStyle nodeStyleCopy = new GUIStyle(nodeStyle);
var graphics = GetBlockGraphics(block);
// Make sure node is wide enough to fit the node name text
float width = nodeStyleCopy.CalcSize(new GUIContent(block.BlockName)).x;
tempRect = block._NodeRect;
tempRect.width = Mathf.Max(block._NodeRect.width, width);
block._NodeRect = tempRect;
// Draw untinted highlight
if (selected)
{
GUI.backgroundColor = Color.white;
nodeStyleCopy.normal.background = graphics.onTexture;
GUI.Box(windowRect, "", nodeStyleCopy);
}
// Draw tinted block; ensure text is readable
var brightness = graphics.tint.r * 0.3 + graphics.tint.g * 0.59 + graphics.tint.b * 0.11;
nodeStyleCopy.normal.textColor = brightness >= 0.5 ? Color.black : Color.white;
if (GUI.GetNameOfFocusedControl() == searchFieldName && !block.IsFiltered)
{
graphics.tint.a *= 0.2f;
}
nodeStyleCopy.normal.background = graphics.offTexture;
GUI.backgroundColor = graphics.tint;
GUI.Box(windowRect, block.BlockName, nodeStyleCopy);
GUI.backgroundColor = Color.white;
if (block.Description.Length > 0)
{
GUIStyle descriptionStyle = new GUIStyle(EditorStyles.helpBox);
descriptionStyle.wordWrap = true;
var content = new GUIContent(block.Description);
windowRect.y += windowRect.height;
windowRect.height = descriptionStyle.CalcHeight(content, windowRect.width);
GUI.Label(windowRect, content, descriptionStyle);
}
GUI.backgroundColor = Color.white;
// Draw Event Handler labels
if (block._EventHandler != null)
{
string handlerLabel = "";
EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(block._EventHandler.GetType());
if (info != null)
{
handlerLabel = "<" + info.EventHandlerName + "> ";
}
GUIStyle handlerStyle = new GUIStyle(EditorStyles.whiteLabel);
handlerStyle.wordWrap = true;
handlerStyle.margin.top = 0;
handlerStyle.margin.bottom = 0;
handlerStyle.alignment = TextAnchor.MiddleCenter;
Rect rect = new Rect(block._NodeRect);
rect.height = handlerStyle.CalcHeight(new GUIContent(handlerLabel), block._NodeRect.width);
rect.x += flowchart.ScrollPos.x;
rect.y += flowchart.ScrollPos.y - rect.height;
GUI.Label(rect, handlerLabel, handlerStyle);
}
} }
} }
// Draw play icons beside all executing blocks // Draw play icons beside all executing blocks
if (Application.isPlaying) if (Application.isPlaying)
{ {
@ -1117,6 +1035,11 @@ namespace Fungus.EditorUtils
EditorZoomArea.End(); EditorZoomArea.End();
} }
private Rect CalcFlowchartWindowViewRect()
{
return new Rect(0, 0, this.position.width / flowchart.Zoom, this.position.height / flowchart.Zoom);
}
public virtual Vector2 GetBlockCenter(Block[] blocks) public virtual Vector2 GetBlockCenter(Block[] blocks)
{ {
if (blocks.Length == 0) if (blocks.Length == 0)
@ -1220,7 +1143,7 @@ namespace Fungus.EditorUtils
return newBlock; return newBlock;
} }
protected virtual void DrawConnections(Block block, bool highlightedOnly) protected virtual void DrawConnections(Block block)
{ {
if (block == null) if (block == null)
{ {
@ -1252,12 +1175,6 @@ namespace Fungus.EditorUtils
bool highlight = command.IsExecuting || (blockIsSelected && commandIsSelected); bool highlight = command.IsExecuting || (blockIsSelected && commandIsSelected);
if (highlightedOnly && !highlight ||
!highlightedOnly && highlight)
{
continue;
}
connectedBlocks.Clear(); connectedBlocks.Clear();
command.GetConnectedBlocks(ref connectedBlocks); command.GetConnectedBlocks(ref connectedBlocks);
@ -1400,7 +1317,7 @@ namespace Fungus.EditorUtils
if (isSelected) if (isSelected)
{ {
// Deselect // Deselect
flowchart.DeselectBlock(deleteBlock); flowchart.DeselectBlockNoCheck(deleteBlock);
// Revert to showing properties for the Flowchart // Revert to showing properties for the Flowchart
Selection.activeGameObject = flowchart.gameObject; Selection.activeGameObject = flowchart.gameObject;
@ -1642,5 +1559,98 @@ namespace Fungus.EditorUtils
return graphics; return graphics;
} }
private void DrawBlock(Block block, Rect scriptViewRect)
{
float nodeWidthA = nodeStyle.CalcSize(new GUIContent(block.BlockName)).x + 10;
float nodeWidthB = 0f;
if (block._EventHandler != null)
{
nodeWidthB = nodeStyle.CalcSize(new GUIContent(block._EventHandler.GetSummary())).x + 10;
}
Rect tempRect = block._NodeRect;
tempRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120);
tempRect.height = 40;
block._NodeRect = tempRect;
Rect windowRect = new Rect(block._NodeRect);
windowRect.position += flowchart.ScrollPos;
//skip if outside of view
if (!scriptViewRect.Overlaps(windowRect))
return;
// Draw blocks
bool selected = block.IsSelected;
GUIStyle nodeStyleCopy = new GUIStyle(nodeStyle);
var graphics = GetBlockGraphics(block);
// Make sure node is wide enough to fit the node name text
float width = nodeStyleCopy.CalcSize(new GUIContent(block.BlockName)).x;
tempRect = block._NodeRect;
tempRect.width = Mathf.Max(block._NodeRect.width, width);
block._NodeRect = tempRect;
// Draw untinted highlight
if (selected)
{
GUI.backgroundColor = Color.white;
nodeStyleCopy.normal.background = graphics.onTexture;
GUI.Box(windowRect, "", nodeStyleCopy);
}
// Draw tinted block; ensure text is readable
var brightness = graphics.tint.r * 0.3 + graphics.tint.g * 0.59 + graphics.tint.b * 0.11;
nodeStyleCopy.normal.textColor = brightness >= 0.5 ? Color.black : Color.white;
if (GUI.GetNameOfFocusedControl() == searchFieldName && !block.IsFiltered)
{
graphics.tint.a *= 0.2f;
}
nodeStyleCopy.normal.background = graphics.offTexture;
GUI.backgroundColor = graphics.tint;
GUI.Box(windowRect, block.BlockName, nodeStyleCopy);
GUI.backgroundColor = Color.white;
if (block.Description.Length > 0)
{
GUIStyle descriptionStyle = new GUIStyle(EditorStyles.helpBox);
descriptionStyle.wordWrap = true;
var content = new GUIContent(block.Description);
windowRect.y += windowRect.height;
windowRect.height = descriptionStyle.CalcHeight(content, windowRect.width);
GUI.Label(windowRect, content, descriptionStyle);
}
GUI.backgroundColor = Color.white;
// Draw Event Handler labels
if (block._EventHandler != null)
{
string handlerLabel = "";
EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(block._EventHandler.GetType());
if (info != null)
{
handlerLabel = "<" + info.EventHandlerName + "> ";
}
GUIStyle handlerStyle = new GUIStyle(EditorStyles.whiteLabel);
handlerStyle.wordWrap = true;
handlerStyle.margin.top = 0;
handlerStyle.margin.bottom = 0;
handlerStyle.alignment = TextAnchor.MiddleCenter;
Rect rect = new Rect(block._NodeRect);
rect.height = handlerStyle.CalcHeight(new GUIContent(handlerLabel), block._NodeRect.width);
rect.x += flowchart.ScrollPos.x;
rect.y += flowchart.ScrollPos.y - rect.height;
GUI.Label(rect, handlerLabel, handlerStyle);
}
}
} }
} }

20382
Assets/Tests/Perf/ManyBlocks_700+.unity

File diff suppressed because it is too large Load Diff

7
Assets/Tests/Perf/ManyBlocks_700+.unity.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c4d1692b35ae833488af6eacb8029e26
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Loading…
Cancel
Save