You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
605 lines
29 KiB
605 lines
29 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEditor.UIElements;
|
|
using UnityEditorInternal;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace UnityEditor.Rendering
|
|
{
|
|
public partial class RenderGraphViewer
|
|
{
|
|
static readonly string[] k_PassTypeNames =
|
|
{
|
|
"Legacy Render Pass",
|
|
"Unsafe Render Pass",
|
|
"Raster Render Pass",
|
|
"Compute Pass"
|
|
};
|
|
|
|
static readonly string[] k_PassTypeNamesNotMergedMessage =
|
|
{
|
|
"This is a Legacy Render Pass. Only Raster Render Passes can be merged.",
|
|
"This is an Unsafe Render Pass. Only Raster Render Passes can be merged.",
|
|
"Pass merging was disabled.",
|
|
"This is a Compute Pass. Only Raster Render Passes can be merged."
|
|
};
|
|
|
|
static partial class Names
|
|
{
|
|
public const string kPanelContainer = "panel-container";
|
|
public const string kResourceListFoldout = "panel-resource-list";
|
|
public const string kPassListFoldout = "panel-pass-list";
|
|
public const string kSearchField = "search-field";
|
|
}
|
|
static partial class Classes
|
|
{
|
|
public const string kPanelListLineBreak = "panel-list__line-break";
|
|
public const string kPanelListItem = "panel-list__item";
|
|
public const string kPanelListItemSelectionAnimation = "panel-list__item--selection-animation";
|
|
public const string kPanelResourceListItem = "panel-resource-list__item";
|
|
public const string kPanelPassListItem = "panel-pass-list__item";
|
|
public const string kSubHeaderText = "sub-header-text";
|
|
public const string kInfoFoldout = "info-foldout";
|
|
public const string kInfoFoldoutSecondaryText = "info-foldout__secondary-text";
|
|
public const string kCustomFoldoutArrow = "custom-foldout-arrow";
|
|
}
|
|
|
|
internal const string k_SelectionColorBeginTag = "<mark=#3169ACAB>";
|
|
internal const string k_SelectionColorEndTag = "</mark>";
|
|
|
|
TwoPaneSplitView m_SidePanelSplitView;
|
|
bool m_ResourceListExpanded = true;
|
|
bool m_PassListExpanded = true;
|
|
float m_SidePanelVerticalAspectRatio = 0.5f;
|
|
float m_SidePanelFixedPaneHeight = 0;
|
|
float m_ContentSplitViewFixedPaneWidth = 280;
|
|
|
|
// Lists of text elements that the search filters are able to highlight
|
|
Dictionary<VisualElement, List<TextElement>> m_SidePanelResourceTexts = new ();
|
|
Dictionary<VisualElement, List<TextElement>> m_SidePanelPassTexts = new ();
|
|
Dictionary<VisualElement, List<TextElement>> m_GridResourceListTexts = new ();
|
|
Dictionary<VisualElement, List<TextElement>> m_GridPassListTexts = new ();
|
|
|
|
void InitializeSidePanel()
|
|
{
|
|
m_SidePanelSplitView = rootVisualElement.Q<TwoPaneSplitView>(Names.kPanelContainer);
|
|
rootVisualElement.RegisterCallback<GeometryChangedEvent>(_ =>
|
|
{
|
|
SaveSplitViewFixedPaneHeight(); // Window resized - save the current pane height
|
|
|
|
// TwoPaneSplitView also updates draglineanchor offset using the same event, conflicting with what we
|
|
// do here. Deferring our panel height update to next frame solves a bug with dragline "jumping" when
|
|
// window is resized down vertically and the lower panel is already at minimum height.
|
|
rootVisualElement.schedule.Execute(UpdatePanelHeights);
|
|
});
|
|
|
|
var contentSplitView = rootVisualElement.Q<TwoPaneSplitView>(Names.kContentContainer);
|
|
contentSplitView.fixedPaneInitialDimension = m_ContentSplitViewFixedPaneWidth;
|
|
contentSplitView.fixedPaneIndex = 1;
|
|
contentSplitView.fixedPane?.RegisterCallback<GeometryChangedEvent>(_ =>
|
|
{
|
|
float? w = contentSplitView.fixedPane?.resolvedStyle?.width;
|
|
if (w.HasValue)
|
|
m_ContentSplitViewFixedPaneWidth = w.Value;
|
|
});
|
|
|
|
// Callbacks for dynamic height allocation between resource & pass lists
|
|
HeaderFoldout resourceListFoldout = rootVisualElement.Q<HeaderFoldout>(Names.kResourceListFoldout);
|
|
resourceListFoldout.value = m_ResourceListExpanded;
|
|
resourceListFoldout.RegisterValueChangedCallback(evt =>
|
|
{
|
|
if (m_ResourceListExpanded)
|
|
SaveSplitViewFixedPaneHeight(); // Closing the foldout - save the current pane height
|
|
|
|
m_ResourceListExpanded = resourceListFoldout.value;
|
|
UpdatePanelHeights();
|
|
});
|
|
resourceListFoldout.icon = m_ResourceListIcon;
|
|
resourceListFoldout.contextMenuGenerator = () => CreateContextMenu(resourceListFoldout.Q<ScrollView>());
|
|
|
|
HeaderFoldout passListFoldout = rootVisualElement.Q<HeaderFoldout>(Names.kPassListFoldout);
|
|
passListFoldout.value = m_PassListExpanded;
|
|
passListFoldout.RegisterValueChangedCallback(evt =>
|
|
{
|
|
if (m_PassListExpanded)
|
|
SaveSplitViewFixedPaneHeight(); // Closing the foldout - save the current pane height
|
|
|
|
m_PassListExpanded = passListFoldout.value;
|
|
UpdatePanelHeights();
|
|
});
|
|
passListFoldout.icon = m_PassListIcon;
|
|
passListFoldout.contextMenuGenerator = () => CreateContextMenu(passListFoldout.Q<ScrollView>());
|
|
|
|
var searchField = rootVisualElement.Q<ToolbarSearchField>(Names.kSearchField);
|
|
searchField.placeholderText = L10n.Tr("Search");
|
|
searchField.RegisterValueChangedCallback(evt => OnSearchFilterChanged(evt.newValue));
|
|
}
|
|
|
|
static bool IsInsideTag(string input, int index)
|
|
{
|
|
int openTagIndex = input.LastIndexOf('<', index);
|
|
int closeTagIndex = input.LastIndexOf('>', index);
|
|
return openTagIndex > closeTagIndex;
|
|
}
|
|
|
|
static bool IsSearchFilterMatch(string str, string searchString, out int startIndex, out int endIndex)
|
|
{
|
|
startIndex = -1;
|
|
endIndex = -1;
|
|
|
|
if (searchString.Length == 0)
|
|
return true;
|
|
|
|
int searchStartIndex = 0;
|
|
for (;;)
|
|
{
|
|
startIndex = str.IndexOf(searchString, searchStartIndex, StringComparison.CurrentCultureIgnoreCase);
|
|
|
|
// If we found a match but it is inside another tag, ignore it and continue
|
|
if (startIndex != -1 && IsInsideTag(str, startIndex))
|
|
{
|
|
searchStartIndex = startIndex + 1;
|
|
continue;
|
|
}
|
|
|
|
// Either valid match (not inside another tag) or no match
|
|
break;
|
|
}
|
|
|
|
if (startIndex == -1)
|
|
return false;
|
|
|
|
endIndex = startIndex + searchString.Length - 1;
|
|
return true;
|
|
}
|
|
|
|
private IVisualElementScheduledItem m_PreviousSearch;
|
|
private string m_PendingSearchString = string.Empty;
|
|
private const int k_SearchStringLimit = 15;
|
|
void OnSearchFilterChanged(string searchString)
|
|
{
|
|
// Ensure the search string is within the allowed length limit (15 chars max)
|
|
if (searchString.Length > k_SearchStringLimit)
|
|
{
|
|
searchString = searchString[..k_SearchStringLimit]; // Trim to max 15 chars
|
|
Debug.LogWarning("[Render Graph Viewer] Search string limit exceeded: " + k_SearchStringLimit);
|
|
}
|
|
|
|
// Sanitize to not match rich text tags
|
|
searchString = searchString.Replace("<", string.Empty).Replace(">", string.Empty);
|
|
|
|
// If the search string hasn't changed, avoid repeating the same search
|
|
if (m_PendingSearchString == searchString)
|
|
return;
|
|
|
|
m_PendingSearchString = searchString;
|
|
|
|
if (m_PreviousSearch != null && m_PreviousSearch.isActive)
|
|
m_PreviousSearch.Pause();
|
|
|
|
m_PreviousSearch = rootVisualElement
|
|
.schedule
|
|
.Execute(() =>
|
|
{
|
|
PerformSearch(m_SidePanelResourceTexts, searchString, hideRootElementIfNoMatch: true);
|
|
PerformSearch(m_SidePanelPassTexts, searchString, hideRootElementIfNoMatch: true);
|
|
PerformSearch(m_GridResourceListTexts, searchString);
|
|
PerformSearch(m_GridPassListTexts, searchString);
|
|
})
|
|
.StartingIn(5); // Avoid spamming multiple search if the user types really fast
|
|
}
|
|
|
|
internal static void PerformSearch(Dictionary<VisualElement, List<TextElement>> elementCache, string searchString, bool hideRootElementIfNoMatch = false)
|
|
{
|
|
// Display filter
|
|
foreach (var (rootElement, textElements) in elementCache)
|
|
{
|
|
bool anyDescendantMatchesSearch = false;
|
|
foreach (var elem in textElements)
|
|
{
|
|
var text = elem.text;
|
|
|
|
// Remove existing match highlight tags
|
|
var hasHighlight = text.IndexOf(k_SelectionColorBeginTag, StringComparison.Ordinal) >= 0;
|
|
if (hasHighlight)
|
|
{
|
|
text = text.Replace(k_SelectionColorBeginTag, string.Empty);
|
|
text = text.Replace(k_SelectionColorEndTag, string.Empty);
|
|
}
|
|
|
|
if (!IsSearchFilterMatch(text, searchString, out int startHighlight, out int endHighlight))
|
|
{
|
|
// Reset original text
|
|
elem.text = text;
|
|
continue;
|
|
}
|
|
|
|
if (startHighlight >= 0 && endHighlight >= 0)
|
|
{
|
|
text = text.Insert(startHighlight, k_SelectionColorBeginTag);
|
|
text = text.Insert(endHighlight + k_SelectionColorBeginTag.Length + 1, k_SelectionColorEndTag);
|
|
}
|
|
elem.text = text;
|
|
anyDescendantMatchesSearch = true;
|
|
}
|
|
|
|
if (hideRootElementIfNoMatch)
|
|
rootElement.style.display = anyDescendantMatchesSearch ? DisplayStyle.Flex : DisplayStyle.None;
|
|
}
|
|
}
|
|
|
|
void SetChildFoldoutsExpanded(VisualElement elem, bool expanded)
|
|
{
|
|
elem.Query<Foldout>().ForEach(f => f.value = expanded);
|
|
}
|
|
|
|
GenericMenu CreateContextMenu(VisualElement content)
|
|
{
|
|
var menu = new GenericMenu();
|
|
menu.AddItem(new GUIContent("Collapse All"), false, () => SetChildFoldoutsExpanded(content, false));
|
|
menu.AddItem(new GUIContent("Expand All"), false, () => SetChildFoldoutsExpanded(content, true));
|
|
return menu;
|
|
}
|
|
|
|
void PopulateResourceList()
|
|
{
|
|
ScrollView content = rootVisualElement.Q<HeaderFoldout>(Names.kResourceListFoldout).Q<ScrollView>();
|
|
content.Clear();
|
|
|
|
UpdatePanelHeights();
|
|
|
|
m_SidePanelResourceTexts.Clear();
|
|
|
|
int visibleResourceIndex = 0;
|
|
foreach (var visibleResourceElement in m_ResourceElementsInfo)
|
|
{
|
|
var resourceData = m_CurrentDebugData.resourceLists[(int)visibleResourceElement.type][visibleResourceElement.index];
|
|
|
|
var resourceItem = new Foldout();
|
|
resourceItem.text = resourceData.name;
|
|
resourceItem.value = false;
|
|
resourceItem.userData = visibleResourceIndex;
|
|
resourceItem.AddToClassList(Classes.kPanelListItem);
|
|
resourceItem.AddToClassList(Classes.kPanelResourceListItem);
|
|
resourceItem.AddToClassList(Classes.kCustomFoldoutArrow);
|
|
visibleResourceIndex++;
|
|
|
|
var iconContainer = new VisualElement();
|
|
iconContainer.AddToClassList(Classes.kResourceIconContainer);
|
|
|
|
var importedIcon = new VisualElement();
|
|
importedIcon.AddToClassList(Classes.kResourceIconImported);
|
|
importedIcon.tooltip = "Imported resource";
|
|
importedIcon.style.display = resourceData.imported ? DisplayStyle.Flex : DisplayStyle.None;
|
|
iconContainer.Add(importedIcon);
|
|
|
|
var foldoutCheckmark = resourceItem.Q("unity-checkmark");
|
|
// Add resource type icon before the label
|
|
foldoutCheckmark.parent.Insert(1, CreateResourceTypeIcon(visibleResourceElement.type, resourceData.memoryless));
|
|
foldoutCheckmark.parent.Add(iconContainer);
|
|
foldoutCheckmark.BringToFront(); // Move foldout checkmark to the right
|
|
|
|
// Add imported icon to the right of the foldout checkmark
|
|
var toggleContainer = resourceItem.Q<Toggle>();
|
|
toggleContainer.tooltip = resourceData.name;
|
|
|
|
RenderGraphResourceType type = visibleResourceElement.type;
|
|
if (type == RenderGraphResourceType.Texture && resourceData.textureData != null)
|
|
{
|
|
var lineBreak = new VisualElement();
|
|
lineBreak.AddToClassList(Classes.kPanelListLineBreak);
|
|
resourceItem.Add(lineBreak);
|
|
resourceItem.Add(new Label($"Size: {resourceData.textureData.width}x{resourceData.textureData.height}x{resourceData.textureData.depth}"));
|
|
resourceItem.Add(new Label($"Format: {resourceData.textureData.format.ToString()}"));
|
|
resourceItem.Add(new Label($"Clear: {resourceData.textureData.clearBuffer}"));
|
|
resourceItem.Add(new Label($"BindMS: {resourceData.textureData.bindMS}"));
|
|
resourceItem.Add(new Label($"Samples: {resourceData.textureData.samples}"));
|
|
if (m_CurrentDebugData.isNRPCompiler)
|
|
resourceItem.Add(new Label($"Memoryless: {resourceData.memoryless}"));
|
|
}
|
|
else if (type == RenderGraphResourceType.Buffer && resourceData.bufferData != null)
|
|
{
|
|
var lineBreak = new VisualElement();
|
|
lineBreak.AddToClassList(Classes.kPanelListLineBreak);
|
|
resourceItem.Add(lineBreak);
|
|
resourceItem.Add(new Label($"Count: {resourceData.bufferData.count}"));
|
|
resourceItem.Add(new Label($"Stride: {resourceData.bufferData.stride}"));
|
|
resourceItem.Add(new Label($"Target: {resourceData.bufferData.target.ToString()}"));
|
|
resourceItem.Add(new Label($"Usage: {resourceData.bufferData.usage.ToString()}"));
|
|
}
|
|
|
|
content.Add(resourceItem);
|
|
|
|
m_SidePanelResourceTexts[resourceItem] = resourceItem.Query().Descendents<TextElement>().ToList();
|
|
}
|
|
}
|
|
|
|
void PopulatePassList()
|
|
{
|
|
HeaderFoldout headerFoldout = rootVisualElement.Q<HeaderFoldout>(Names.kPassListFoldout);
|
|
if (!m_CurrentDebugData.isNRPCompiler)
|
|
{
|
|
headerFoldout.style.display = DisplayStyle.None;
|
|
return;
|
|
}
|
|
headerFoldout.style.display = DisplayStyle.Flex;
|
|
|
|
ScrollView content = headerFoldout.Q<ScrollView>();
|
|
content.Clear();
|
|
|
|
UpdatePanelHeights();
|
|
|
|
m_SidePanelPassTexts.Clear();
|
|
|
|
void CreateTextElement(VisualElement parent, string text, string className = null)
|
|
{
|
|
var textElement = new TextElement();
|
|
textElement.text = text;
|
|
if (className != null)
|
|
textElement.AddToClassList(className);
|
|
parent.Add(textElement);
|
|
}
|
|
|
|
HashSet<int> addedPasses = new HashSet<int>();
|
|
|
|
foreach (var visiblePassElement in m_PassElementsInfo)
|
|
{
|
|
if (addedPasses.Contains(visiblePassElement.passId))
|
|
continue; // Add only one item per merged pass group
|
|
|
|
List<RenderGraph.DebugData.PassData> passDatas = new();
|
|
List<string> passNames = new();
|
|
var groupedPassIds = GetGroupedPassIds(visiblePassElement.passId);
|
|
foreach (int groupedId in groupedPassIds) {
|
|
addedPasses.Add(groupedId);
|
|
passDatas.Add(m_CurrentDebugData.passList[groupedId]);
|
|
passNames.Add(m_CurrentDebugData.passList[groupedId].name);
|
|
}
|
|
|
|
var passItem = new Foldout();
|
|
var passesText = string.Join(", ", passNames);
|
|
passItem.text = $"<b>{passesText}</b>";
|
|
passItem.Q<Toggle>().tooltip = passesText;
|
|
passItem.value = false;
|
|
passItem.userData = m_PassIdToVisiblePassIndex[visiblePassElement.passId];
|
|
passItem.AddToClassList(Classes.kPanelListItem);
|
|
passItem.AddToClassList(Classes.kPanelPassListItem);
|
|
|
|
//Native pass info (duplicated for each pass group so just look at the first)
|
|
var firstPassData = passDatas[0];
|
|
var nativePassInfo = firstPassData.nrpInfo?.nativePassInfo;
|
|
|
|
if (nativePassInfo != null)
|
|
{
|
|
if (nativePassInfo.mergedPassIds.Count == 1)
|
|
CreateTextElement(passItem, "Native Pass was created from Raster Render Pass.");
|
|
else if (nativePassInfo.mergedPassIds.Count > 1)
|
|
CreateTextElement(passItem, $"Native Pass was created by merging {nativePassInfo.mergedPassIds.Count} Raster Render Passes.");
|
|
|
|
CreateTextElement(passItem, "Pass break reasoning", Classes.kSubHeaderText);
|
|
CreateTextElement(passItem, nativePassInfo.passBreakReasoning);
|
|
}
|
|
else
|
|
{
|
|
CreateTextElement(passItem, "Pass break reasoning", Classes.kSubHeaderText);
|
|
string msg = k_PassTypeNamesNotMergedMessage[(int)firstPassData.type];
|
|
CreateTextElement(passItem, msg);
|
|
}
|
|
|
|
if (nativePassInfo != null)
|
|
{
|
|
CreateTextElement(passItem, "Render Graph Pass Info", Classes.kSubHeaderText);
|
|
foreach (int passId in groupedPassIds)
|
|
{
|
|
var pass = m_CurrentDebugData.passList[passId];
|
|
Debug.Assert(pass.nrpInfo != null); // This overlay currently assumes NRP compiler
|
|
|
|
var passFoldout = new Foldout();
|
|
passFoldout.text = $"<b>{pass.name}</b> ({k_PassTypeNames[(int) pass.type]})";
|
|
|
|
var foldoutTextElement = passFoldout.Q<TextElement>(className: Foldout.textUssClassName);
|
|
foldoutTextElement.displayTooltipWhenElided = false; // no tooltip override when ellipsis is active
|
|
|
|
bool hasSubpassIndex = pass.nativeSubPassIndex != -1;
|
|
if (hasSubpassIndex)
|
|
{
|
|
// Abuse Foldout to allow two-line header: add line break <br> at the end of the actual foldout text to increase height,
|
|
// then inject a second label into the hierarchy starting with a line break to offset it to the second line.
|
|
passFoldout.text += "<br>";
|
|
Label subpassIndexLabel = new Label($"<br>Subpass #{pass.nativeSubPassIndex}");
|
|
subpassIndexLabel.AddToClassList(Classes.kInfoFoldoutSecondaryText);
|
|
foldoutTextElement.Add(subpassIndexLabel);
|
|
}
|
|
|
|
passFoldout.AddToClassList(Classes.kInfoFoldout);
|
|
passFoldout.AddToClassList(Classes.kCustomFoldoutArrow);
|
|
passFoldout.Q<Toggle>().tooltip = $"The {k_PassTypeNames[(int) pass.type]} <b>{pass.name}</b> belongs to native subpass {pass.nativeSubPassIndex}.";
|
|
|
|
var foldoutCheckmark = passFoldout.Q("unity-checkmark");
|
|
foldoutCheckmark.BringToFront(); // Move foldout checkmark to the right
|
|
|
|
var lineBreak = new VisualElement();
|
|
lineBreak.AddToClassList(Classes.kPanelListLineBreak);
|
|
passFoldout.Add(lineBreak);
|
|
|
|
CreateTextElement(passFoldout,
|
|
$"Attachment dimensions: {pass.nrpInfo.width}x{pass.nrpInfo.height}x{pass.nrpInfo.volumeDepth}");
|
|
CreateTextElement(passFoldout, $"Has depth attachment: {pass.nrpInfo.hasDepth}");
|
|
CreateTextElement(passFoldout, $"MSAA samples: {pass.nrpInfo.samples}");
|
|
CreateTextElement(passFoldout, $"Async compute: {pass.async}");
|
|
|
|
passItem.Add(passFoldout);
|
|
}
|
|
|
|
CreateTextElement(passItem, "Attachment Load/Store Actions", Classes.kSubHeaderText);
|
|
if (nativePassInfo != null && nativePassInfo.attachmentInfos.Count > 0)
|
|
{
|
|
foreach (var attachmentInfo in nativePassInfo.attachmentInfos)
|
|
{
|
|
var attachmentFoldout = new Foldout();
|
|
|
|
string subResourceText = string.Empty;
|
|
if (attachmentInfo.attachment.mipLevel > 0) subResourceText += $" Mip:{attachmentInfo.attachment.mipLevel}";
|
|
if (attachmentInfo.attachment.depthSlice > 0) subResourceText += $" Slice:{attachmentInfo.attachment.depthSlice}";
|
|
|
|
// Abuse Foldout to allow two-line header (same as above)
|
|
attachmentFoldout.text = $"<b>{attachmentInfo.resourceName + subResourceText}</b><br>";
|
|
Label attachmentIndexLabel = new Label($"<br>Attachment #{attachmentInfo.attachmentIndex}");
|
|
attachmentIndexLabel.AddToClassList(Classes.kInfoFoldoutSecondaryText);
|
|
|
|
var foldoutTextElement = attachmentFoldout.Q<TextElement>(className: Foldout.textUssClassName);
|
|
foldoutTextElement.displayTooltipWhenElided = false; // no tooltip override when ellipsis is active
|
|
foldoutTextElement.Add(attachmentIndexLabel);
|
|
|
|
attachmentFoldout.AddToClassList(Classes.kInfoFoldout);
|
|
attachmentFoldout.AddToClassList(Classes.kCustomFoldoutArrow);
|
|
attachmentFoldout.Q<Toggle>().tooltip = $"Texture <b>{attachmentInfo.resourceName}</b> is bound at attachment index {attachmentInfo.attachmentIndex}.";
|
|
|
|
var foldoutCheckmark = attachmentFoldout.Q("unity-checkmark");
|
|
foldoutCheckmark.BringToFront(); // Move foldout checkmark to the right
|
|
|
|
var lineBreak = new VisualElement();
|
|
lineBreak.AddToClassList(Classes.kPanelListLineBreak);
|
|
attachmentFoldout.Add(lineBreak);
|
|
|
|
attachmentFoldout.Add(new TextElement
|
|
{
|
|
text = $"<b>Load action:</b> {attachmentInfo.attachment.loadAction}\n- {attachmentInfo.loadReason}"
|
|
});
|
|
|
|
bool addMsaaInfo = !string.IsNullOrEmpty(attachmentInfo.storeMsaaReason);
|
|
string resolvedTexturePrefix = addMsaaInfo ? "Resolved surface: " : "";
|
|
|
|
string storeActionText = $"<b>Store action:</b> {attachmentInfo.attachment.storeAction}" +
|
|
$"\n - {resolvedTexturePrefix}{attachmentInfo.storeReason}";
|
|
|
|
if (addMsaaInfo)
|
|
{
|
|
string msaaTexturePrefix = "MSAA surface: ";
|
|
storeActionText += $"\n - {msaaTexturePrefix}{attachmentInfo.storeMsaaReason}";
|
|
}
|
|
|
|
attachmentFoldout.Add(new TextElement { text = storeActionText });
|
|
|
|
passItem.Add(attachmentFoldout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreateTextElement(passItem, "No attachments.");
|
|
}
|
|
}
|
|
|
|
content.Add(passItem);
|
|
|
|
m_SidePanelPassTexts[passItem] = passItem.Query().Descendents<TextElement>().ToList();
|
|
}
|
|
}
|
|
|
|
void SaveSplitViewFixedPaneHeight()
|
|
{
|
|
m_SidePanelFixedPaneHeight = m_SidePanelSplitView.fixedPane?.resolvedStyle?.height ?? 0;
|
|
}
|
|
|
|
void UpdatePanelHeights()
|
|
{
|
|
bool passListExpanded = m_PassListExpanded && HasValidDebugData && m_CurrentDebugData.isNRPCompiler;
|
|
const int kFoldoutHeaderHeightPx = 18;
|
|
const int kFoldoutHeaderExpandedMinHeightPx = 50;
|
|
const int kWindowExtraMarginPx = 6;
|
|
|
|
var resourceList = rootVisualElement.Q<HeaderFoldout>(Names.kResourceListFoldout);
|
|
var passList = rootVisualElement.Q<HeaderFoldout>(Names.kPassListFoldout);
|
|
|
|
resourceList.style.minHeight = kFoldoutHeaderHeightPx;
|
|
passList.style.minHeight = kFoldoutHeaderHeightPx;
|
|
|
|
float panelHeightPx = position.height - kHeaderContainerHeightPx - kWindowExtraMarginPx;
|
|
if (!m_ResourceListExpanded)
|
|
{
|
|
m_SidePanelSplitView.fixedPaneInitialDimension = kFoldoutHeaderHeightPx;
|
|
}
|
|
else if (!passListExpanded)
|
|
{
|
|
m_SidePanelSplitView.fixedPaneInitialDimension = panelHeightPx - kFoldoutHeaderHeightPx;
|
|
}
|
|
else
|
|
{
|
|
// Update aspect ratio in case user has dragged the split view
|
|
if (m_SidePanelFixedPaneHeight > kFoldoutHeaderHeightPx && m_SidePanelFixedPaneHeight < panelHeightPx - kFoldoutHeaderHeightPx)
|
|
{
|
|
m_SidePanelVerticalAspectRatio = m_SidePanelFixedPaneHeight / panelHeightPx;
|
|
}
|
|
m_SidePanelSplitView.fixedPaneInitialDimension = panelHeightPx * m_SidePanelVerticalAspectRatio;
|
|
|
|
resourceList.style.minHeight = kFoldoutHeaderExpandedMinHeightPx;
|
|
passList.style.minHeight = kFoldoutHeaderExpandedMinHeightPx;
|
|
}
|
|
|
|
// Ensure fixed pane initial dimension gets applied in case it has already been set
|
|
m_SidePanelSplitView.fixedPane.style.height = m_SidePanelSplitView.fixedPaneInitialDimension;
|
|
|
|
// Disable drag line when one of the foldouts is collapsed
|
|
var dragLine = m_SidePanelSplitView.Q("unity-dragline");
|
|
var dragLineAnchor = m_SidePanelSplitView.Q("unity-dragline-anchor");
|
|
if (!m_ResourceListExpanded || !passListExpanded)
|
|
{
|
|
dragLine.pickingMode = PickingMode.Ignore;
|
|
dragLineAnchor.pickingMode = PickingMode.Ignore;
|
|
}
|
|
else
|
|
{
|
|
dragLine.pickingMode = PickingMode.Position;
|
|
dragLineAnchor.pickingMode = PickingMode.Position;
|
|
}
|
|
}
|
|
|
|
void ScrollToPass(int visiblePassIndex)
|
|
{
|
|
var passFoldout = rootVisualElement.Q<HeaderFoldout>(Names.kPassListFoldout);
|
|
ScrollToFoldout(passFoldout, visiblePassIndex);
|
|
}
|
|
|
|
void ScrollToResource(int visibleResourceIndex)
|
|
{
|
|
var resourceFoldout = rootVisualElement.Q<HeaderFoldout>(Names.kResourceListFoldout);
|
|
ScrollToFoldout(resourceFoldout, visibleResourceIndex);
|
|
}
|
|
|
|
void ScrollToFoldout(VisualElement parent, int index)
|
|
{
|
|
ScrollView scrollView = parent.Q<ScrollView>();
|
|
scrollView.Query<Foldout>(classes: Classes.kPanelListItem).ForEach(foldout =>
|
|
{
|
|
if (index == (int) foldout.userData)
|
|
{
|
|
// Trigger animation
|
|
foldout.AddToClassList(Classes.kPanelListItemSelectionAnimation);
|
|
|
|
// This repaint hack is needed because transition animations have poor framerate. So we are hooking to editor update
|
|
// loop for the duration of the animation to force repaints and have a smooth highlight animation.
|
|
// See https://jira.unity3d.com/browse/UIE-1326
|
|
EditorApplication.update += Repaint;
|
|
|
|
foldout.RegisterCallbackOnce<TransitionEndEvent>(_ =>
|
|
{
|
|
// "Highlight in" animation finished
|
|
foldout.RemoveFromClassList(Classes.kPanelListItemSelectionAnimation);
|
|
foldout.RegisterCallbackOnce<TransitionEndEvent>(_ =>
|
|
{
|
|
// "Highlight out" animation finished
|
|
EditorApplication.update -= Repaint;
|
|
});
|
|
});
|
|
|
|
// Open foldout
|
|
foldout.value = true;
|
|
// Defer scrolling to allow foldout to be expanded first
|
|
scrollView.schedule.Execute(() => scrollView.ScrollTo(foldout)).StartingIn(50);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|