浏览代码

* Hunt down code causing pixel cache dirtying every frame

* Put inline/port inputs in a shared pixel cache per node (rather than per input)
* Make preview and controls independent of node collapsed state
* Tidy up MaterialNodeView a bit
* Introduce PixelCacheProfiler for somewhat manually hunting down bad pixel caches
/main
Peter Bay Bastian 7 年前
当前提交
16db0624
共有 10 个文件被更改,包括 286 次插入123 次删除
  1. 8
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/GraphEditorView.cs
  2. 221
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/MaterialNodeView.cs
  3. 14
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PortInputView.cs
  4. 40
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/MaterialGraph.uss
  5. 59
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PixelCacheProfilerView.cs
  6. 3
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PixelCacheProfilerView.cs.meta
  7. 11
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/PixelCacheProfiler.uss
  8. 7
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/PixelCacheProfiler.uss.meta
  9. 39
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/UXML/PixelCacheProfiler.uxml
  10. 7
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/UXML/PixelCacheProfiler.uxml.meta

8
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/GraphEditorView.cs


m_GraphView.RegisterCallback<KeyDownEvent>(OnSpaceDown);
content.Add(m_GraphView);
// Uncomment to enable pixel caching profiler
// m_ProfilerView = new PixelCacheProfilerView(this);
// m_GraphView.Add(m_ProfilerView);
m_BlackboardProvider = new BlackboardProvider(assetName, graph);
m_GraphView.Add(m_BlackboardProvider.blackboard);
m_BlackboardProvider.blackboard.layout = new Rect(new Vector2(10f, 10f), m_BlackboardProvider.blackboard.layout.size);

node.UpdatePortInputVisibilities();
UpdateEdgeColors(nodesToUpdate);
if (m_ProfilerView != null)
m_ProfilerView.Profile();
}
void AddNode(INode node)

}
Stack<MaterialNodeView> m_NodeStack = new Stack<MaterialNodeView>();
PixelCacheProfilerView m_ProfilerView;
void UpdateEdgeColors(HashSet<MaterialNodeView> nodeViews)
{

221
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/MaterialNodeView.cs


using UnityEngine.Experimental.UIElements;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Drawing.Controls;
using UnityEngine.Experimental.UIElements.StyleEnums;
using Node = UnityEditor.Experimental.UIElements.GraphView.Node;
namespace UnityEditor.ShaderGraph.Drawing

List<VisualElement> m_ControlViews;
VisualElement m_PreviewElement;
VisualElement m_ControlsContainer;
List<Attacher> m_Attachers;
VisualElement m_ControlItems;
VisualElement m_PreviewFiller;
VisualElement m_PortInputContainer;
public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener)
{

return;
var contents = this.Q("contents");
m_ControlsContainer = new VisualElement
// Add controls container
var controlsContainer = new VisualElement { name = "controls" };
name = "controls"
};
extensionContainer.Add(m_ControlsContainer);
m_ControlsDivider = new VisualElement { name = "divider" };
m_ControlsDivider.AddToClassList("horizontal");
m_ControlsDivider = new VisualElement { name = "divider" };
m_ControlsDivider.AddToClassList("horizontal");
controlsContainer.Add(m_ControlsDivider);
m_ControlItems = new VisualElement { name = "items" };
controlsContainer.Add(m_ControlItems);
// Instantiate control views from node
foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false))
m_ControlItems.Add(attribute.InstantiateControl(node, propertyInfo));
}
if (m_ControlItems.childCount > 0)
contents.Add(controlsContainer);
m_PreviewContainer = new VisualElement { name = "previewContainer" };
m_PreviewContainer.AddToClassList("expanded");
// Add actual preview which floats on top of the node
m_PreviewContainer = new VisualElement
m_PreviewElement = new VisualElement
{
name = "previewElement",
clippingOptions = ClippingOptions.ClipAndCacheContents,
pickingMode = PickingMode.Ignore
};
m_PreviewImage = new Image
{
name = "preview",
pickingMode = PickingMode.Ignore,
image = Texture2D.whiteTexture,
};
m_PreviewElement.Add(m_PreviewImage);
m_PreviewRenderData = previewManager.GetPreview(inNode);
m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
UpdatePreviewTexture();
name = "previewContainer",
clippingOptions = ClippingOptions.ClipAndCacheContents,
pickingMode = PickingMode.Ignore
};
m_PreviewImage = new Image
{
name = "preview",
pickingMode = PickingMode.Ignore,
image = Texture2D.whiteTexture,
};
{
// Add preview collapse button on top of preview
var collapsePreviewButton = new VisualElement { name = "collapse" };
collapsePreviewButton.Add(new VisualElement { name = "icon" });
collapsePreviewButton.AddManipulator(new Clickable(() =>

}));
UpdatePreviewExpandedState(node.previewExpanded);
}
m_PreviewContainer.Add(m_PreviewImage);
// Hook up preview image to preview manager
m_PreviewRenderData = previewManager.GetPreview(inNode);
m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
UpdatePreviewTexture();
// Add fake preview which pads out the node to provide space for the floating preview
m_PreviewFiller = new VisualElement { name = "previewFiller" };
m_PreviewFiller.AddToClassList("expanded");
{
var previewDivider = new VisualElement { name = "divider" };
previewDivider.AddToClassList("horizontal");
m_PreviewFiller.Add(previewDivider);
var expandPreviewButton = new VisualElement { name = "expand" };
expandPreviewButton.Add(new VisualElement { name = "icon" });

UpdatePreviewExpandedState(true);
}));
m_PreviewContainer.Add(expandPreviewButton);
m_PreviewFiller.Add(expandPreviewButton);
contents.Add(m_PreviewFiller);
extensionContainer.Add(m_PreviewContainer);
UpdatePreviewExpandedState(node.previewExpanded);
m_ControlViews = new List<VisualElement>();
foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false))
m_ControlViews.Add(attribute.InstantiateControl(node, propertyInfo));
m_Attachers = new List<Attacher>(node.GetInputSlots<MaterialSlot>().Count());
// Add port input container, which acts as a pixel cache for all port inputs
m_PortInputContainer = new VisualElement
{
name = "portInputContainer",
clippingOptions = ClippingOptions.ClipAndCacheContents,
pickingMode = PickingMode.Ignore
};
Add(m_PortInputContainer);
UpdateSlotAttachers();
UpdatePortInputs();
UpdateControls();
if (node is PreviewNode)
{

UpdateSize();
}
m_PortInputContainer.SendToBack();
if (node.hasPreview)
m_PreviewFiller.BringToFront();
}
public AbstractMaterialNode node { get; private set; }

node.drawState = ds;
}
UpdateControls();
RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here
RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here
if (node.hasPreview)
m_PreviewFiller.BringToFront();
}
}

void UpdatePreviewExpandedState(bool expanded)
{
node.previewExpanded = expanded;
if (m_PreviewContainer == null)
if (m_PreviewFiller == null)
if (m_PreviewElement.parent != this)
if (m_PreviewContainer.parent != this)
Add(m_PreviewElement);
Add(m_PreviewContainer);
m_PreviewContainer.AddToClassList("expanded");
m_PreviewContainer.RemoveFromClassList("collapsed");
m_PreviewFiller.AddToClassList("expanded");
m_PreviewFiller.RemoveFromClassList("collapsed");
if (m_PreviewElement.parent == m_PreviewContainer)
if (m_PreviewContainer.parent == m_PreviewFiller)
m_PreviewElement.RemoveFromHierarchy();
m_PreviewContainer.RemoveFromHierarchy();
m_PreviewContainer.RemoveFromClassList("expanded");
m_PreviewContainer.AddToClassList("collapsed");
m_PreviewFiller.RemoveFromClassList("expanded");
m_PreviewFiller.AddToClassList("collapsed");
}
}

// Slot doesn't exist anymore, remove it
inputContainer.Remove(port);
// We also need to remove the attacher along with the element it's attaching
var attacher = m_Attachers.FirstOrDefault(a => a.target == port);
if (attacher != null)
{
attacher.Detach();
attacher.element.parent.Remove(attacher.element);
m_Attachers.Remove(attacher);
}
// We also need to remove the inline input
var portInputView = m_PortInputContainer.OfType<PortInputView>().FirstOrDefault(v => Equals(v.slot, port.slot));
if (portInputView != null)
portInputView.RemoveFromHierarchy();
}
else
{

outputContainer.Sort((x, y) => slots.IndexOf(((ShaderPort)x).slot) - slots.IndexOf(((ShaderPort)y).slot));
}
UpdateControls();
UpdateSlotAttachers();
UpdatePortInputs();
foreach (var control in m_ControlViews)
foreach (var control in m_ControlItems)
{
var listener = control as INodeModificationListener;
if (listener != null)

}
}
void UpdateSlotAttachers()
void UpdatePortInputs()
if (!m_Attachers.Any(a => a.target == port))
if (!m_PortInputContainer.OfType<PortInputView>().Any(a => Equals(a.slot, port.slot)))
var portInputView = new PortInputView(port.slot);
Add(portInputView);
portInputView.SendToBack();
m_Attachers.Add(new Attacher(portInputView, port, SpriteAlignment.LeftCenter) { distance = -8f });
var portInputView = new PortInputView(port.slot) { style = { positionType = PositionType.Absolute } };
m_PortInputContainer.Add(portInputView);
port.RegisterCallback<PostLayoutEvent>(evt => UpdatePortInput((ShaderPort)evt.target));
void UpdatePortInput(ShaderPort port)
{
var inputView = m_PortInputContainer.OfType<PortInputView>().First(x => Equals(x.slot, port.slot));
var currentRect = new Rect(inputView.style.positionLeft, inputView.style.positionTop, inputView.style.width, inputView.style.height);
var targetRect = new Rect(0.0f, 0.0f, port.layout.width, port.layout.height);
targetRect = port.ChangeCoordinatesTo(inputView.shadow.parent, targetRect);
var centerY = targetRect.center.y;
var centerX = targetRect.xMax - currentRect.width;
currentRect.center = new Vector2(centerX, centerY);
inputView.style.positionTop = currentRect.yMin;
var newHeight = inputView.parent.layout.height;
foreach (var element in inputView.parent.Children())
newHeight = Mathf.Max(newHeight, element.style.positionTop + element.layout.height);
if (Math.Abs(inputView.parent.style.height - newHeight) > 1e-3)
inputView.parent.style.height = newHeight;
}
foreach (var attacher in m_Attachers)
foreach (var portInputView in m_PortInputContainer.OfType<PortInputView>())
var slot = ((ShaderPort)attacher.target).slot;
attacher.element.visible = expanded && !node.owner.GetEdges(node.GetSlotReference(slot.id)).Any();
var slot = portInputView.slot;
var oldVisibility = portInputView.visible;
portInputView.visible = expanded && !node.owner.GetEdges(node.GetSlotReference(slot.id)).Any();
if (portInputView.visible != oldVisibility)
m_PortInputContainer.Dirty(ChangeType.Repaint);
}
}

anchor.visualClass = slot.concreteValueType.ToClassName();
}
foreach (var attacher in m_Attachers)
{
var portInputView = (PortInputView)attacher.element;
foreach (var portInputView in m_PortInputContainer.OfType<PortInputView>())
}
}
void OnResize(Vector2 deltaSize)

}
}
void UpdateControls()
{
if (!expanded)
{
m_ControlsContainer.Clear();
m_ControlsDivider.RemoveFromHierarchy();
}
else if (m_ControlsContainer.childCount != m_ControlViews.Count)
{
m_ControlsContainer.Clear();
foreach (var view in m_ControlViews)
m_ControlsContainer.Add(view);
extensionContainer.Add(m_ControlsDivider);
if (m_PreviewContainer != null)
m_ControlsDivider.PlaceBehind(m_PreviewContainer);
}
if (m_ControlsContainer.childCount == 0)
m_ControlsContainer.RemoveFromClassList("notEmpty");
else
m_ControlsContainer.AddToClassList("notEmpty");
}
void UpdateSize()
{
var previewNode = node as PreviewNode;

public void Dispose()
{
foreach (var attacher in m_Attachers)
{
((PortInputView)attacher.element).Dispose();
attacher.Detach();
attacher.element.parent.Remove(attacher.element);
}
m_Attachers.Clear();
foreach (var portInputView in m_PortInputContainer.OfType<PortInputView>())
portInputView.Dispose();
node = null;
if (m_PreviewRenderData != null)

14
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PortInputView.cs


get { return m_EdgeColor.GetSpecifiedValueOrDefault(Color.red); }
}
public MaterialSlot slot
{
get { return m_Slot; }
}
MaterialSlot m_Slot;
ConcreteSlotValueType m_SlotType;
VisualElement m_Control;

m_Container = new VisualElement { name = "container" };
{
m_Control = m_Slot.InstantiateControl();
m_Control = this.slot.InstantiateControl();
if (m_Control != null)
m_Container.Add(m_Control);

Add(m_Container);
m_Container.visible = m_EdgeControl.visible = m_Control != null;
m_Container.clippingOptions = ClippingOptions.ClipAndCacheContents;
}
protected override void OnStyleResolved(ICustomStyle styles)

public void UpdateSlotType()
{
if (m_Slot.concreteValueType != m_SlotType)
if (slot.concreteValueType != m_SlotType)
m_SlotType = m_Slot.concreteValueType;
m_SlotType = slot.concreteValueType;
AddToClassList("type" + m_SlotType);
if (m_Control != null)
{

m_Container.Remove(m_Control);
}
m_Control = m_Slot.InstantiateControl();
m_Control = slot.InstantiateControl();
if (m_Control != null)
m_Container.Insert(0, m_Control);

40
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/MaterialGraph.uss


margin-right: 0;
}
MaterialNodeView #previewContainer.expanded {
MaterialNodeView #collapsible-area {
width: 0;
height: 0;
}
MaterialNodeView #previewFiller.expanded {
height: 200;
padding-bottom: 200;
MaterialNodeView #previewContainer,
MaterialNodeView #previewFiller,
MaterialNodeView #controls.notEmpty {
MaterialNodeView #controls > #items {
MaterialNodeView > #previewElement {
MaterialNodeView > #previewContainer {
position-type: absolute;
position-bottom: 4;
position-left: 4;

MaterialNodeView > #previewElement > #preview {
MaterialNodeView > #previewContainer > #preview {
MaterialNodeView > #previewElement > #preview > #collapse {
MaterialNodeView > #previewContainer > #preview > #collapse {
background-color: #000;
border-color: #F0F0F0;
width: 0;

}
MaterialNodeView:hover > #previewElement > #preview > #collapse {
MaterialNodeView:hover > #previewContainer > #preview > #collapse {
MaterialNodeView > #previewElement > #preview > #collapse > #icon {
MaterialNodeView > #previewContainer > #preview > #collapse > #icon {
MaterialNodeView > #previewElement > #preview > #collapse:hover {
MaterialNodeView > #previewContainer > #preview > #collapse:hover {
MaterialNodeView #previewContainer > #expand {
MaterialNodeView #previewFiller > #expand {
align-self: center;
width: 56;
height: 16;

MaterialNodeView #previewContainer > #expand > #icon {
MaterialNodeView #previewFiller > #expand > #icon {
align-self: center;
background-image : resource("GraphView/Nodes/PreviewExpand.png");
width: 16;

MaterialNodeView #previewContainer.collapsed > #expand:hover {
MaterialNodeView #previewFiller.collapsed > #expand:hover {
MaterialNodeView #previewContainer.expanded > #expand {
MaterialNodeView #previewFiller.expanded > #expand {
height: 0;
}

width: 10;
height: 10;
cursor: resize-up-left;
}
MaterialNodeView > #portInputContainer {
position-type: absolute;
width: 212;
position-left: -200;
position-top: 46;
}
PortInputView {

59
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PixelCacheProfilerView.cs


using System.Linq;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
namespace UnityEditor.ShaderGraph.Drawing
{
public class PixelCacheProfilerView : VisualElement
{
readonly VisualElement m_Target;
Label m_TotalLabel;
Label m_DirtyLabel;
Label m_TotalNodeContentsLabel;
Label m_DirtyNodeContentsLabel;
Label m_TotalPreviewsLabel;
Label m_DirtyPreviewsLabel;
Label m_TotalInlinesLabel;
Label m_DirtyInlinesLabel;
public PixelCacheProfilerView(VisualElement target)
{
m_Target = target;
var tpl = Resources.Load<VisualTreeAsset>("UXML/PixelCacheProfiler");
tpl.CloneTree(this, null);
m_TotalLabel = this.Q<Label>("totalLabel");
m_DirtyLabel = this.Q<Label>("dirtyLabel");
m_TotalNodeContentsLabel = this.Q<Label>("totalNodeContentsLabel");
m_DirtyNodeContentsLabel = this.Q<Label>("dirtyNodeContentsLabel");
m_TotalPreviewsLabel = this.Q<Label>("totalPreviewsLabel");
m_DirtyPreviewsLabel = this.Q<Label>("dirtyPreviewsLabel");
m_TotalInlinesLabel = this.Q<Label>("totalInlinesLabel");
m_DirtyInlinesLabel = this.Q<Label>("dirtyInlinesLabel");
}
public void Profile()
{
var caches = m_Target.Query().Where(ve => ve.clippingOptions == ClippingOptions.ClipAndCacheContents).Build().ToList();
var dirtyCaches = caches.Where(ve => ve.IsDirty(ChangeType.Repaint)).ToList();
m_TotalLabel.text = caches.Count.ToString();
m_DirtyLabel.text = dirtyCaches.Count.ToString();
var nodeContentsCaches = caches.Where(ve => ve.name == "node-border").ToList();
var dirtyNodeContentsCaches = nodeContentsCaches.Where(ve => ve.IsDirty(ChangeType.Repaint)).ToList();
m_TotalNodeContentsLabel.text = nodeContentsCaches.Count.ToString();
m_DirtyNodeContentsLabel.text = dirtyNodeContentsCaches.Count.ToString();
var previewCaches = caches.Where(ve => ve.name == "previewContainer").ToList();
var dirtyPreviewCaches = previewCaches.Where(ve => ve.IsDirty(ChangeType.Repaint)).ToList();
m_TotalPreviewsLabel.text = previewCaches.Count.ToString();
m_DirtyPreviewsLabel.text = dirtyPreviewCaches.Count.ToString();
var inlineCaches = caches.Where(ve => ve.name == "portInputContainer").ToList();
var dirtyInlineCaches = inlineCaches.Where(ve => ve.IsDirty(ChangeType.Repaint)).ToList();
m_TotalInlinesLabel.text = inlineCaches.Count.ToString();
m_DirtyInlinesLabel.text = dirtyInlineCaches.Count.ToString();
}
}
}

3
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/PixelCacheProfilerView.cs.meta


fileFormatVersion: 2
guid: 7147f25b12dd4427b4c8afd44624f35b
timeCreated: 1517227822

11
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/PixelCacheProfiler.uss


PixelCacheProfilerView > #content > #title {
font-style: bold;
}
PixelCacheProfilerView > #content > .row {
flex-direction: row;
}
PixelCacheProfilerView > #content > .indented {
padding-left: 8;
}

7
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/Styles/PixelCacheProfiler.uss.meta


fileFormatVersion: 2
guid: 61cf67e9c1f54ceab08d2bd16ffd3c53
ScriptedImporter:
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}

39
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/UXML/PixelCacheProfiler.uxml


<UXML xmlns:ui="UnityEngine.Experimental.UIElements">
<ui:VisualElement name="content">
<Style path="Styles/PixelCacheProfiler"/>
<ui:Label name="title" text="Pixel Cache Profiler"/>
<ui:VisualElement class="row">
<ui:Label text="Total pixel caches: "/>
<ui:Label name="totalLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Node contents: "/>
<ui:Label name="totalNodeContentsLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Node previews: "/>
<ui:Label name="totalPreviewsLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Inline inputs: "/>
<ui:Label name="totalInlinesLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="row">
<ui:Label text="Dirty pixel caches: "/>
<ui:Label name="dirtyLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Node contents: "/>
<ui:Label name="dirtyNodeContentsLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Node previews: "/>
<ui:Label name="dirtyPreviewsLabel" text="-"/>
</ui:VisualElement>
<ui:VisualElement class="indented row">
<ui:Label text="Inline inputs: "/>
<ui:Label name="dirtyInlinesLabel" text="-"/>
</ui:VisualElement>
</ui:VisualElement>
</UXML>

7
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Resources/UXML/PixelCacheProfiler.uxml.meta


fileFormatVersion: 2
guid: 0fda1aff5a5744478f70542e95fd3d42
ScriptedImporter:
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
正在加载...
取消
保存