This commit is contained in:
2024-09-20 20:30:10 +02:00
commit 4fabf1a6fd
29169 changed files with 1706941 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// Templated class for <see cref="IDebugDisplaySettings"/>
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class DebugDisplaySettings<T> : IDebugDisplaySettings
where T : IDebugDisplaySettings, new()
{
/// <summary>
/// The set of <see cref="IDebugDisplaySettingsData"/> containing the settings for this debug display
/// </summary>
protected readonly HashSet<IDebugDisplaySettingsData> m_Settings = new HashSet<IDebugDisplaySettingsData>();
private static readonly Lazy<T> s_Instance = new Lazy<T>(() =>
{
var instance = new T();
instance.Reset();
return instance;
});
/// <summary>
/// The singleton instance that contains the current settings of Rendering Debugger.
/// </summary>
public static T Instance => s_Instance.Value;
#region IDebugDisplaySettingsQuery
/// <summary>
/// Returns true if any of the debug settings are currently active.
/// </summary>
public virtual bool AreAnySettingsActive
{
get
{
foreach (IDebugDisplaySettingsData setting in m_Settings)
{
if (setting.AreAnySettingsActive)
return true;
}
return false;
}
}
/// <summary>
/// Checks whether the current state of these settings allows post-processing.
/// </summary>
public virtual bool IsPostProcessingAllowed
{
get
{
// Only enable post-processing if we aren't using certain debug-views.
bool postProcessingAllowed = true;
foreach (IDebugDisplaySettingsData setting in m_Settings)
postProcessingAllowed &= setting.IsPostProcessingAllowed;
return postProcessingAllowed;
}
}
/// <summary>
/// Returns true if lighting is active for current state of debug settings.
/// </summary>
public virtual bool IsLightingActive
{
get
{
bool lightingActive = true;
foreach (IDebugDisplaySettingsData setting in m_Settings)
lightingActive &= setting.IsLightingActive;
return lightingActive;
}
}
#endregion
/// <summary>
/// Adds a new <see cref="TData"/> to this settings
/// </summary>
/// <typeparam name="TData">The type of <see cref="TData"/> to be added</typeparam>
/// <param name="newData">The <see cref="TData"/> to be added</param>
/// <returns>The type of <see cref="TData"/> that has been added</returns>
protected TData Add<TData>(TData newData) where TData : IDebugDisplaySettingsData
{
m_Settings.Add(newData);
return newData;
}
/// <summary>
/// Executes an action for each element
/// </summary>
/// <param name="onExecute"></param>
public void ForEach(Action<IDebugDisplaySettingsData> onExecute)
{
foreach (IDebugDisplaySettingsData setting in m_Settings)
{
onExecute(setting);
}
}
/// <summary>
/// Reset the stored debug settings
/// </summary>
public virtual void Reset()
{
m_Settings.Clear();
}
/// <summary>
/// Attempts to get the color that should be used to clear the screen according to current debug settings.
/// </summary>
/// <param name="color">A reference to the screen clear color to use.</param>
/// <returns>True if the color reference was updated, and false otherwise.</returns>
public virtual bool TryGetScreenClearColor(ref Color color)
{
foreach (IDebugDisplaySettingsData setting in m_Settings)
{
if (setting.TryGetScreenClearColor(ref color))
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4e8d2f4fab62224f8d693e15696410e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEngine.Rendering
{
/// <summary>
/// The abstract common implementation of the <see cref="IDebugDisplaySettingsPanelDisposable"/>
/// </summary>
public abstract class DebugDisplaySettingsPanel : IDebugDisplaySettingsPanelDisposable
{
private readonly List<DebugUI.Widget> m_Widgets = new List<DebugUI.Widget>();
private readonly DisplayInfoAttribute m_DisplayInfo;
/// <summary>
/// The Panel name
/// </summary>
public virtual string PanelName => m_DisplayInfo?.name ?? string.Empty;
/// <summary>
/// The order where this panel should be shown
/// </summary>
public virtual int Order => m_DisplayInfo?.order ?? 0;
/// <summary>
/// The collection of widgets that are in this panel
/// </summary>
public DebugUI.Widget[] Widgets => m_Widgets.ToArray();
/// <summary>
/// The <see cref="DebugUI.Flags"/> for this panel
/// </summary>
public virtual DebugUI.Flags Flags => DebugUI.Flags.None;
/// <summary>
/// Adds a widget to the panel
/// </summary>
/// <param name="widget">The <see cref="DebugUI.Widget"/> to be added.</param>
protected void AddWidget(DebugUI.Widget widget)
{
if (widget == null)
throw new ArgumentNullException(nameof(widget));
m_Widgets.Add(widget);
}
/// <summary>
/// Clears the widgets list
/// </summary>
protected void Clear()
{
m_Widgets.Clear();
}
/// <summary>
/// Disposes the panel
/// </summary>
public void Dispose()
{
Clear();
}
/// <summary>
/// Default constructor
/// </summary>
protected DebugDisplaySettingsPanel()
{
m_DisplayInfo = GetType().GetCustomAttribute<DisplayInfoAttribute>();
if (m_DisplayInfo == null)
Debug.Log($"Type {GetType()} should specify the attribute {nameof(DisplayInfoAttribute)}");
}
}
/// <summary>
/// Class to help declare rendering debugger panels
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class DebugDisplaySettingsPanel<T> : DebugDisplaySettingsPanel
where T : IDebugDisplaySettingsData
{
internal T m_Data;
/// <summary>
/// Access to the data stored
/// </summary>
public T data
{
get => m_Data;
internal set => m_Data = value;
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="data">The data that the panel holds</param>
protected DebugDisplaySettingsPanel(T data)
: base()
{
m_Data = data;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5ee786a2f4e04b5bbc5c7b2e645272e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// The UI implementation for a debug settings panel
/// </summary>
public class DebugDisplaySettingsUI : IDebugData
{
private IEnumerable<IDebugDisplaySettingsPanelDisposable> m_DisposablePanels;
private IDebugDisplaySettings m_Settings;
private void Reset()
{
if (m_Settings != null)
{
m_Settings.Reset();
// TODO: Tear the UI down and re-create it for now - this is horrible, so reset it instead.
UnregisterDebug();
RegisterDebug(m_Settings);
DebugManager.instance.RefreshEditor();
}
}
/// <summary>
/// Register a display for the UI
/// </summary>
/// <param name="settings"><see cref="IDebugDisplaySettings"/> to be registered</param>
public void RegisterDebug(IDebugDisplaySettings settings)
{
DebugManager debugManager = DebugManager.instance;
List<IDebugDisplaySettingsPanelDisposable> panels = new List<IDebugDisplaySettingsPanelDisposable>();
debugManager.RegisterData(this);
m_Settings = settings;
m_DisposablePanels = panels;
Action<IDebugDisplaySettingsData> onExecute = (data) =>
{
IDebugDisplaySettingsPanelDisposable disposableSettingsPanel = data.CreatePanel();
DebugUI.Widget[] panelWidgets = disposableSettingsPanel.Widgets;
DebugUI.Panel panel = debugManager.GetPanel(
displayName: disposableSettingsPanel.PanelName,
createIfNull: true,
groupIndex: (disposableSettingsPanel is DebugDisplaySettingsPanel debugDisplaySettingsPanel) ? debugDisplaySettingsPanel.Order : 0);
ObservableList<DebugUI.Widget> panelChildren = panel.children;
panel.flags = disposableSettingsPanel.Flags;
panels.Add(disposableSettingsPanel);
panelChildren.Add(panelWidgets);
};
m_Settings.ForEach(onExecute);
}
/// <summary>
/// Unregister the debug panels
/// </summary>
public void UnregisterDebug()
{
DebugManager debugManager = DebugManager.instance;
foreach (IDebugDisplaySettingsPanelDisposable disposableSettingsPanel in m_DisposablePanels)
{
DebugUI.Widget[] panelWidgets = disposableSettingsPanel.Widgets;
string panelId = disposableSettingsPanel.PanelName;
DebugUI.Panel panel = debugManager.GetPanel(panelId, true);
ObservableList<DebugUI.Widget> panelChildren = panel.children;
disposableSettingsPanel.Dispose();
panelChildren.Remove(panelWidgets);
}
m_DisposablePanels = null;
debugManager.UnregisterData(this);
}
#region IDebugData
/// <summary>
/// The reset action to be executed when a Reset of the rendering debugger is need
/// </summary>
/// <returns>A <see cref="Action"/> with the restet callback</returns>
public Action GetReset()
{
return Reset;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 267b889b8e81a45d2a5f5bc4121bc7b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,416 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
namespace UnityEngine.Rendering
{
/// <summary>
/// Debug Dispaly Settings Volume
/// </summary>
public class DebugDisplaySettingsVolume : IDebugDisplaySettingsData
{
/// <summary>Current volume debug settings.</summary>
public IVolumeDebugSettings2 volumeDebugSettings { get; }
/// <summary>
/// Constructor with the settings
/// </summary>
/// <param name="volumeDebugSettings"></param>
public DebugDisplaySettingsVolume(IVolumeDebugSettings2 volumeDebugSettings)
{
this.volumeDebugSettings = volumeDebugSettings;
}
internal int volumeComponentEnumIndex;
static class Styles
{
public static readonly GUIContent none = new GUIContent("None");
public static readonly GUIContent editorCamera = new GUIContent("Editor Camera");
}
static class Strings
{
public static readonly string none = "None";
public static readonly string camera = "Camera";
public static readonly string parameter = "Parameter";
public static readonly string component = "Component";
public static readonly string debugViewNotSupported = "Debug view not supported";
public static readonly string volumeInfo = "Volume Info";
public static readonly string interpolatedValue = "Interpolated Value";
public static readonly string defaultValue = "Default Value";
public static readonly string global = "Global";
public static readonly string local = "Local";
}
internal static class WidgetFactory
{
public static DebugUI.EnumField CreateComponentSelector(SettingsPanel panel, Action<DebugUI.Field<int>, int> refresh)
{
int componentIndex = 0;
var componentNames = new List<GUIContent>() { Styles.none };
var componentValues = new List<int>() { componentIndex++ };
foreach (var type in panel.data.volumeDebugSettings.volumeComponentsPathAndType)
{
componentNames.Add(new GUIContent() { text = type.Item1 });
componentValues.Add(componentIndex++);
}
return new DebugUI.EnumField
{
displayName = Strings.component,
getter = () => panel.data.volumeDebugSettings.selectedComponent,
setter = value => panel.data.volumeDebugSettings.selectedComponent = value,
enumNames = componentNames.ToArray(),
enumValues = componentValues.ToArray(),
getIndex = () => panel.data.volumeComponentEnumIndex,
setIndex = value => { panel.data.volumeComponentEnumIndex = value; },
onValueChanged = refresh
};
}
public static DebugUI.ObjectPopupField CreateCameraSelector(SettingsPanel panel, Action<DebugUI.Field<Object>, Object> refresh)
{
return new DebugUI.ObjectPopupField
{
displayName = Strings.camera,
getter = () => panel.data.volumeDebugSettings.selectedCamera,
setter = value =>
{
var c = panel.data.volumeDebugSettings.cameras.ToArray();
panel.data.volumeDebugSettings.selectedCameraIndex = Array.IndexOf(c, value as Camera);
},
getObjects = () => panel.data.volumeDebugSettings.cameras,
onValueChanged = refresh
};
}
static DebugUI.Widget CreateVolumeParameterWidget(string name, VolumeParameter param, Func<bool> isHiddenCallback = null)
{
if (param == null)
return new DebugUI.Value() { displayName = name, getter = () => "-" };
var parameterType = param.GetType();
// Special overrides
if (parameterType == typeof(ColorParameter))
{
var p = (ColorParameter)param;
return new DebugUI.ColorField()
{
displayName = name,
hdr = p.hdr,
showAlpha = p.showAlpha,
getter = () => p.value,
setter = value => p.value = value,
isHiddenCallback = isHiddenCallback
};
}
else if (parameterType == typeof(BoolParameter))
{
var p = (BoolParameter)param;
return new DebugUI.BoolField()
{
displayName = name,
getter = () => p.value,
setter = value => p.value = value,
isHiddenCallback = isHiddenCallback
};
}
else
{
var typeInfo = parameterType.GetTypeInfo();
var genericArguments = typeInfo.BaseType.GenericTypeArguments;
if (genericArguments.Length > 0 && genericArguments[0].IsArray)
{
return new DebugUI.ObjectListField()
{
displayName = name,
getter = () => (Object[])parameterType.GetProperty("value").GetValue(param, null),
type = parameterType
};
}
}
// For parameters that do not override `ToString`
var property = param.GetType().GetProperty("value");
var toString = property.PropertyType.GetMethod("ToString", Type.EmptyTypes);
if ((toString == null) || (toString.DeclaringType == typeof(object)) || (toString.DeclaringType == typeof(UnityEngine.Object)))
{
// Check if the parameter has a name
var nameProp = property.PropertyType.GetProperty("name");
if (nameProp == null)
return new DebugUI.Value() { displayName = name, getter = () => Strings.debugViewNotSupported };
// Return the parameter name
return new DebugUI.Value()
{
displayName = name,
getter = () =>
{
var value = property.GetValue(param);
if (value == null || value.Equals(null))
return Strings.none;
var valueString = nameProp.GetValue(value);
return valueString ?? Strings.none;
},
isHiddenCallback = isHiddenCallback
};
}
// Call the ToString method
return new DebugUI.Value()
{
displayName = name,
getter = () =>
{
var value = property.GetValue(param);
return value == null ? Strings.none : value.ToString();
},
isHiddenCallback = isHiddenCallback
};
}
public static DebugUI.Table CreateVolumeTable(DebugDisplaySettingsVolume data)
{
var table = new DebugUI.Table()
{
displayName = Strings.parameter,
isReadOnly = true
};
Type selectedType = data.volumeDebugSettings.selectedComponentType;
if (selectedType == null)
return table;
var stack = data.volumeDebugSettings.selectedCameraVolumeStack ?? VolumeManager.instance.stack;
var stackComponent = stack.GetComponent(selectedType);
if (stackComponent == null)
return table;
var volumes = data.volumeDebugSettings.GetVolumes();
var inst = (VolumeComponent)ScriptableObject.CreateInstance(selectedType);
// First row for volume info
var row = new DebugUI.Table.Row()
{
displayName = Strings.volumeInfo,
opened = true, // Open by default for the in-game view
children =
{
new DebugUI.Value()
{
displayName = Strings.interpolatedValue,
getter = () => string.Empty
}
}
};
// Second row, links to volume gameobjects
var row2 = new DebugUI.Table.Row()
{
displayName = "GameObject",
children = { new DebugUI.Value() { getter = () => string.Empty } }
};
foreach (var volume in volumes)
{
var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile;
row.children.Add(new DebugUI.Value()
{
displayName = profile.name,
getter = () =>
{
var scope = volume.isGlobal ? Strings.global : Strings.local;
var weight = data.volumeDebugSettings.GetVolumeWeight(volume);
return scope + " (" + (weight * 100f) + "%)";
}
});
row2.children.Add(new DebugUI.ObjectField()
{
displayName = profile.name,
getter = () => volume,
});
}
row.children.Add(new DebugUI.Value() { displayName = Strings.defaultValue, getter = () => string.Empty });
table.children.Add(row);
row2.children.Add(new DebugUI.Value() { getter = () => string.Empty });
table.children.Add(row2);
// Build rows - recursively handles nested parameters
var rows = new List<DebugUI.Table.Row>();
int AddParameterRows(Type type, string baseName = null, int skip = 0)
{
void AddRow(FieldInfo f, string prefix, int skip)
{
var fieldName = prefix + f.Name;
var attr = (DisplayInfoAttribute[])f.GetCustomAttributes(typeof(DisplayInfoAttribute), true);
if (attr.Length != 0)
fieldName = prefix + attr[0].name;
#if UNITY_EDITOR
// Would be nice to have the equivalent for the runtime debug.
else
fieldName = UnityEditor.ObjectNames.NicifyVariableName(fieldName);
#endif
int currentParam = rows.Count + skip;
row = new DebugUI.Table.Row()
{
displayName = fieldName,
children = { CreateVolumeParameterWidget(Strings.interpolatedValue, stackComponent.parameterList[currentParam]) },
};
foreach (var volume in volumes)
{
VolumeParameter param = null;
var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile;
if (profile.TryGet(selectedType, out VolumeComponent component))
param = component.parameterList[currentParam];
row.children.Add(CreateVolumeParameterWidget(volume.name + " (" + profile.name + ")", param, () => !component.parameterList[currentParam].overrideState));
}
row.children.Add(CreateVolumeParameterWidget(Strings.defaultValue, inst.parameterList[currentParam]));
rows.Add(row);
}
var fields = type
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.OrderBy(t => t.MetadataToken);
foreach (var field in fields)
{
if (field.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length != 0)
{
skip++;
continue;
}
var fieldType = field.FieldType;
if (fieldType.IsSubclassOf(typeof(VolumeParameter)))
AddRow(field, baseName ?? string.Empty, skip);
else if (!fieldType.IsArray && fieldType.IsClass)
skip += AddParameterRows(fieldType, baseName ?? (field.Name + " "), skip);
}
return skip;
}
AddParameterRows(selectedType);
foreach (var r in rows.OrderBy(t => t.displayName))
table.children.Add(r);
data.volumeDebugSettings.RefreshVolumes(volumes);
for (int i = 0; i < volumes.Length; i++)
table.SetColumnVisibility(i + 1, data.volumeDebugSettings.VolumeHasInfluence(volumes[i]));
float timer = 0.0f, refreshRate = 0.2f;
table.isHiddenCallback = () =>
{
timer += Time.deltaTime;
if (timer >= refreshRate)
{
if (data.volumeDebugSettings.selectedCamera != null)
{
var newVolumes = data.volumeDebugSettings.GetVolumes();
if (!data.volumeDebugSettings.RefreshVolumes(newVolumes))
{
for (int i = 0; i < newVolumes.Length; i++)
{
var visible = data.volumeDebugSettings.VolumeHasInfluence(newVolumes[i]);
table.SetColumnVisibility(i + 1, visible);
}
}
if (!volumes.SequenceEqual(newVolumes))
{
volumes = newVolumes;
DebugManager.instance.ReDrawOnScreenDebug();
}
}
timer = 0.0f;
}
return false;
};
return table;
}
}
[DisplayInfo(name = "Volume", order = int.MaxValue)]
internal class SettingsPanel : DebugDisplaySettingsPanel<DebugDisplaySettingsVolume>
{
public SettingsPanel(DebugDisplaySettingsVolume data)
: base(data)
{
AddWidget(WidgetFactory.CreateComponentSelector(this, (_, __) => Refresh()));
AddWidget(WidgetFactory.CreateCameraSelector(this, (_, __) => Refresh()));
}
DebugUI.Table m_VolumeTable = null;
void Refresh()
{
var panel = DebugManager.instance.GetPanel(PanelName);
if (panel == null)
return;
bool needsRefresh = false;
if (m_VolumeTable != null)
{
needsRefresh = true;
panel.children.Remove(m_VolumeTable);
}
if (m_Data.volumeDebugSettings.selectedComponent > 0 && m_Data.volumeDebugSettings.selectedCamera != null)
{
needsRefresh = true;
m_VolumeTable = WidgetFactory.CreateVolumeTable(m_Data);
AddWidget(m_VolumeTable);
panel.children.Add(m_VolumeTable);
}
if (needsRefresh)
DebugManager.instance.ReDrawOnScreenDebug();
}
}
#region IDebugDisplaySettingsData
/// <summary>
/// Checks whether ANY of the debug settings are currently active.
/// </summary>
public bool AreAnySettingsActive => false; // Volume Debug Panel doesn't need to modify the renderer data, therefore this property returns false
/// <summary>
/// Checks whether the current state of these settings allows post-processing.
/// </summary>
public bool IsPostProcessingAllowed => true;
/// <summary>
/// Checks whether lighting is active for these settings.
/// </summary>
public bool IsLightingActive => true;
/// <summary>
/// Attempts to get the color used to clear the screen for this debug setting.
/// </summary>
/// <param name="color">A reference to the screen clear color to use.</param>
/// <returns>"true" if we updated the color, "false" if we didn't change anything.</returns>
public bool TryGetScreenClearColor(ref Color color)
{
return false;
}
/// <summary>
/// Creates the panel
/// </summary>
/// <returns>The panel</returns>
public IDebugDisplaySettingsPanelDisposable CreatePanel()
{
return new SettingsPanel(this);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 987617381d0b4ad4fafa9f3ab3ae5fb4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,213 @@
//#define RTPROFILER_DEBUG
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEngine.Rendering
{
/// <summary>
/// Debug frame timings class
/// </summary>
public class DebugFrameTiming
{
const string k_FpsFormatString = "{0:F1}";
const string k_MsFormatString = "{0:F2}ms";
const float k_RefreshRate = 1f / 5f;
internal FrameTimeSampleHistory m_FrameHistory;
internal BottleneckHistory m_BottleneckHistory;
/// <summary>
/// Size of the Bottleneck History Window in number of samples.
/// </summary>
public int bottleneckHistorySize { get; set; } = 60;
/// <summary>
/// Size of the Sample History Window in number of samples.
/// </summary>
public int sampleHistorySize { get; set; } = 30;
FrameTiming[] m_Timing = new FrameTiming[1];
FrameTimeSample m_Sample = new FrameTimeSample();
/// <summary>
/// Constructs the debug frame timing
/// </summary>
public DebugFrameTiming()
{
m_FrameHistory = new FrameTimeSampleHistory(sampleHistorySize);
m_BottleneckHistory = new BottleneckHistory(bottleneckHistorySize);
}
/// <summary>
/// Update timing data from profiling counters.
/// </summary>
public void UpdateFrameTiming()
{
m_Timing[0] = default;
m_Sample = default;
FrameTimingManager.CaptureFrameTimings();
FrameTimingManager.GetLatestTimings(1, m_Timing);
if (m_Timing.Length > 0)
{
m_Sample.FullFrameTime = (float)m_Timing.First().cpuFrameTime;
m_Sample.FramesPerSecond = m_Sample.FullFrameTime > 0f ? 1000f / m_Sample.FullFrameTime : 0f;
m_Sample.MainThreadCPUFrameTime = (float)m_Timing.First().cpuMainThreadFrameTime;
m_Sample.MainThreadCPUPresentWaitTime = (float)m_Timing.First().cpuMainThreadPresentWaitTime;
m_Sample.RenderThreadCPUFrameTime = (float)m_Timing.First().cpuRenderThreadFrameTime;
m_Sample.GPUFrameTime = (float)m_Timing.First().gpuFrameTime;
}
m_FrameHistory.DiscardOldSamples(sampleHistorySize);
m_FrameHistory.Add(m_Sample);
m_FrameHistory.ComputeAggregateValues();
m_BottleneckHistory.DiscardOldSamples(bottleneckHistorySize);
m_BottleneckHistory.AddBottleneckFromAveragedSample(m_FrameHistory.SampleAverage);
m_BottleneckHistory.ComputeHistogram();
}
/// <summary>
/// Add frame timing data widgets to debug UI.
/// </summary>
/// <param name="list">List of widgets to add the stats.</param>
public void RegisterDebugUI(List<DebugUI.Widget> list)
{
list.Add(new DebugUI.Foldout()
{
displayName = "Frame Stats",
opened = true,
columnLabels = new string[] { "Avg", "Min", "Max" },
children =
{
new DebugUI.ValueTuple
{
displayName = "Frame Rate (FPS)",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleAverage.FramesPerSecond },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleMin.FramesPerSecond },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleMax.FramesPerSecond },
}
},
new DebugUI.ValueTuple
{
displayName = "Frame Time",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.FullFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.FullFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.FullFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Main Thread Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.MainThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.MainThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.MainThreadCPUFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Render Thread Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.RenderThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.RenderThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.RenderThreadCPUFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Present Wait",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.MainThreadCPUPresentWaitTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.MainThreadCPUPresentWaitTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.MainThreadCPUPresentWaitTime },
}
},
new DebugUI.ValueTuple
{
displayName = "GPU Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.GPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.GPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.GPUFrameTime },
}
}
}
});
list.Add(new DebugUI.Foldout
{
displayName = "Bottlenecks",
children =
{
#if UNITY_EDITOR
new DebugUI.Container { displayName = "Not supported in Editor" }
#else
new DebugUI.ProgressBarValue { displayName = "CPU", getter = () => m_BottleneckHistory.Histogram.CPU },
new DebugUI.ProgressBarValue { displayName = "GPU", getter = () => m_BottleneckHistory.Histogram.GPU },
new DebugUI.ProgressBarValue { displayName = "Present limited", getter = () => m_BottleneckHistory.Histogram.PresentLimited },
new DebugUI.ProgressBarValue { displayName = "Balanced", getter = () => m_BottleneckHistory.Histogram.Balanced },
#endif
}
});
#if RTPROFILER_DEBUG
list.Add(new DebugUI.Foldout
{
displayName = "Realtime Profiler Debug",
children =
{
new DebugUI.IntField
{
displayName = "Frame Time Sample History Size",
getter = () => sampleHistorySize,
setter = (value) => { sampleHistorySize = value; },
min = () => 1,
max = () => 100
},
new DebugUI.IntField
{
displayName = "Bottleneck History Size",
getter = () => bottleneckHistorySize,
setter = (value) => { bottleneckHistorySize = value; },
min = () => 1,
max = () => 100
},
new DebugUI.IntField
{
displayName = "Force VSyncCount",
min = () => - 1,
max = () => 4,
getter = () => QualitySettings.vSyncCount,
setter = (value) => { QualitySettings.vSyncCount = value; }
},
new DebugUI.IntField
{
displayName = "Force TargetFrameRate",
min = () => - 1,
max = () => 1000,
getter = () => Application.targetFrameRate,
setter = (value) => { Application.targetFrameRate = value; }
},
}
});
#endif
}
internal void Reset()
{
m_BottleneckHistory.Clear();
m_FrameHistory.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b966c0b6fb41a743ad89f19c17430eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,517 @@
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
#define USE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
#endif
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
internal enum DebugAction
{
EnableDebugMenu,
PreviousDebugPanel,
NextDebugPanel,
Action,
MakePersistent,
MoveVertical,
MoveHorizontal,
Multiplier,
ResetAll,
DebugActionCount
}
enum DebugActionRepeatMode
{
Never,
Delay
}
public sealed partial class DebugManager
{
const string kEnableDebugBtn1 = "Enable Debug Button 1";
const string kEnableDebugBtn2 = "Enable Debug Button 2";
const string kDebugPreviousBtn = "Debug Previous";
const string kDebugNextBtn = "Debug Next";
const string kValidateBtn = "Debug Validate";
const string kPersistentBtn = "Debug Persistent";
const string kDPadVertical = "Debug Vertical";
const string kDPadHorizontal = "Debug Horizontal";
const string kMultiplierBtn = "Debug Multiplier";
const string kResetBtn = "Debug Reset";
const string kEnableDebug = "Enable Debug";
DebugActionDesc[] m_DebugActions;
DebugActionState[] m_DebugActionStates;
#if USE_INPUT_SYSTEM
InputActionMap debugActionMap = new InputActionMap("Debug Menu");
#endif
void RegisterActions()
{
m_DebugActions = new DebugActionDesc[(int)DebugAction.DebugActionCount];
m_DebugActionStates = new DebugActionState[(int)DebugAction.DebugActionCount];
var enableDebugMenu = new DebugActionDesc();
#if USE_INPUT_SYSTEM
enableDebugMenu.buttonAction = debugActionMap.FindAction(kEnableDebug);
#else
enableDebugMenu.buttonTriggerList.Add(new[] { kEnableDebugBtn1, kEnableDebugBtn2 });
enableDebugMenu.keyTriggerList.Add(new[] { KeyCode.LeftControl, KeyCode.Backspace });
#endif
enableDebugMenu.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.EnableDebugMenu, enableDebugMenu);
var resetDebugMenu = new DebugActionDesc();
#if USE_INPUT_SYSTEM
resetDebugMenu.buttonAction = debugActionMap.FindAction(kResetBtn);
#else
resetDebugMenu.keyTriggerList.Add(new[] { KeyCode.LeftAlt, KeyCode.Backspace });
resetDebugMenu.buttonTriggerList.Add(new[] { kResetBtn, kEnableDebugBtn2 });
#endif
resetDebugMenu.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.ResetAll, resetDebugMenu);
var nextDebugPanel = new DebugActionDesc();
#if USE_INPUT_SYSTEM
nextDebugPanel.buttonAction = debugActionMap.FindAction(kDebugNextBtn);
#else
nextDebugPanel.buttonTriggerList.Add(new[] { kDebugNextBtn });
#endif
nextDebugPanel.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.NextDebugPanel, nextDebugPanel);
var previousDebugPanel = new DebugActionDesc();
#if USE_INPUT_SYSTEM
previousDebugPanel.buttonAction = debugActionMap.FindAction(kDebugPreviousBtn);
#else
previousDebugPanel.buttonTriggerList.Add(new[] { kDebugPreviousBtn });
#endif
previousDebugPanel.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.PreviousDebugPanel, previousDebugPanel);
var validate = new DebugActionDesc();
#if USE_INPUT_SYSTEM
validate.buttonAction = debugActionMap.FindAction(kValidateBtn);
#else
validate.buttonTriggerList.Add(new[] { kValidateBtn });
#endif
validate.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.Action, validate);
var persistent = new DebugActionDesc();
#if USE_INPUT_SYSTEM
persistent.buttonAction = debugActionMap.FindAction(kPersistentBtn);
#else
persistent.buttonTriggerList.Add(new[] { kPersistentBtn });
#endif
persistent.repeatMode = DebugActionRepeatMode.Never;
AddAction(DebugAction.MakePersistent, persistent);
var multiplier = new DebugActionDesc();
#if USE_INPUT_SYSTEM
multiplier.buttonAction = debugActionMap.FindAction(kMultiplierBtn);
#else
multiplier.buttonTriggerList.Add(new[] { kMultiplierBtn });
#endif
multiplier.repeatMode = DebugActionRepeatMode.Delay;
validate.repeatDelay = 0f;
AddAction(DebugAction.Multiplier, multiplier);
var moveVertical = new DebugActionDesc();
#if USE_INPUT_SYSTEM
moveVertical.buttonAction = debugActionMap.FindAction(kDPadVertical);
#else
moveVertical.axisTrigger = kDPadVertical;
#endif
moveVertical.repeatMode = DebugActionRepeatMode.Delay;
moveVertical.repeatDelay = 0.16f;
AddAction(DebugAction.MoveVertical, moveVertical);
var moveHorizontal = new DebugActionDesc();
#if USE_INPUT_SYSTEM
moveHorizontal.buttonAction = debugActionMap.FindAction(kDPadHorizontal);
#else
moveHorizontal.axisTrigger = kDPadHorizontal;
#endif
moveHorizontal.repeatMode = DebugActionRepeatMode.Delay;
moveHorizontal.repeatDelay = 0.16f;
AddAction(DebugAction.MoveHorizontal, moveHorizontal);
}
internal void EnableInputActions()
{
#if USE_INPUT_SYSTEM
foreach (var action in debugActionMap)
action.Enable();
#endif
}
void AddAction(DebugAction action, DebugActionDesc desc)
{
int index = (int)action;
m_DebugActions[index] = desc;
m_DebugActionStates[index] = new DebugActionState();
}
void SampleAction(int actionIndex)
{
var desc = m_DebugActions[actionIndex];
var state = m_DebugActionStates[actionIndex];
// Disable all input events if we're using the new input system
#if USE_INPUT_SYSTEM
if (state.runningAction == false)
{
if (desc.buttonAction != null)
{
var value = desc.buttonAction.ReadValue<float>();
if (!Mathf.Approximately(value, 0))
state.TriggerWithButton(desc.buttonAction, value);
}
}
#elif ENABLE_LEGACY_INPUT_MANAGER
//bool canSampleAction = (state.actionTriggered == false) || (desc.repeatMode == DebugActionRepeatMode.Delay && state.timer > desc.repeatDelay);
if (state.runningAction == false)
{
// Check button triggers
for (int buttonListIndex = 0; buttonListIndex < desc.buttonTriggerList.Count; ++buttonListIndex)
{
var buttons = desc.buttonTriggerList[buttonListIndex];
bool allButtonPressed = true;
try
{
foreach (var button in buttons)
{
allButtonPressed = Input.GetButton(button);
if (!allButtonPressed)
break;
}
}
catch (ArgumentException)
{
// Exception thrown if the input mapping gets removed while in play mode (UUM-37148)
allButtonPressed = false;
}
if (allButtonPressed)
{
state.TriggerWithButton(buttons, 1f);
break;
}
}
// Check axis triggers
if (desc.axisTrigger != "")
{
try
{
float axisValue = Input.GetAxis(desc.axisTrigger);
if (axisValue != 0f)
state.TriggerWithAxis(desc.axisTrigger, axisValue);
}
catch (ArgumentException)
{
// Exception thrown if the input mapping gets removed while in play mode (UUM-37148)
}
}
// Check key triggers
for (int keyListIndex = 0; keyListIndex < desc.keyTriggerList.Count; ++keyListIndex)
{
bool allKeyPressed = true;
var keys = desc.keyTriggerList[keyListIndex];
try
{
foreach (var key in keys)
{
allKeyPressed = Input.GetKey(key);
if (!allKeyPressed)
break;
}
}
catch (ArgumentException)
{
// Exception thrown if the input mapping gets removed while in play mode (UUM-37148)
allKeyPressed = false;
}
if (allKeyPressed)
{
state.TriggerWithKey(keys, 1f);
break;
}
}
}
#endif
}
void UpdateAction(int actionIndex)
{
var desc = m_DebugActions[actionIndex];
var state = m_DebugActionStates[actionIndex];
if (state.runningAction)
state.Update(desc);
}
internal void UpdateActions()
{
for (int actionIndex = 0; actionIndex < m_DebugActions.Length; ++actionIndex)
{
UpdateAction(actionIndex);
SampleAction(actionIndex);
}
}
internal float GetAction(DebugAction action)
{
return m_DebugActionStates[(int)action].actionState;
}
internal bool GetActionToggleDebugMenuWithTouch()
{
#if USE_INPUT_SYSTEM
if (!EnhancedTouchSupport.enabled)
return false;
var touches = InputSystem.EnhancedTouch.Touch.activeTouches;
var touchCount = touches.Count;
InputSystem.TouchPhase? expectedTouchPhase = null;
#else
var touchCount = Input.touchCount;
TouchPhase? expectedTouchPhase = TouchPhase.Began;
#endif
if (touchCount == 3)
{
#if !USE_INPUT_SYSTEM
var touches = Input.touches; // Causes an allocation, which is why this is inside the condition
#endif
foreach (var touch in touches)
{
// Gesture: 3-finger double-tap
if ((!expectedTouchPhase.HasValue || touch.phase == expectedTouchPhase.Value) && touch.tapCount == 2)
return true;
}
}
return false;
}
internal bool GetActionReleaseScrollTarget()
{
#if USE_INPUT_SYSTEM
bool mouseWheelActive = Mouse.current != null && Mouse.current.scroll.ReadValue() != Vector2.zero;
bool touchSupported = Touchscreen.current != null;
#else
bool mouseWheelActive = Input.mouseScrollDelta != Vector2.zero;
bool touchSupported = Input.touchSupported;
#endif
return mouseWheelActive || touchSupported; // Touchscreens have general problems with scrolling, so it's disabled.
}
void RegisterInputs()
{
#if UNITY_EDITOR && !USE_INPUT_SYSTEM
var inputEntries = new List<InputManagerEntry>
{
new InputManagerEntry { name = kEnableDebugBtn1, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left ctrl", altBtnPositive = "joystick button 8" },
new InputManagerEntry { name = kEnableDebugBtn2, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "backspace", altBtnPositive = "joystick button 9" },
new InputManagerEntry { name = kResetBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left alt", altBtnPositive = "joystick button 1" },
new InputManagerEntry { name = kDebugNextBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page down", altBtnPositive = "joystick button 5" },
new InputManagerEntry { name = kDebugPreviousBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page up", altBtnPositive = "joystick button 4" },
new InputManagerEntry { name = kValidateBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "return", altBtnPositive = "joystick button 0" },
new InputManagerEntry { name = kPersistentBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right shift", altBtnPositive = "joystick button 2" },
new InputManagerEntry { name = kMultiplierBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left shift", altBtnPositive = "joystick button 3" },
new InputManagerEntry { name = kDPadHorizontal, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right", btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
new InputManagerEntry { name = kDPadVertical, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "up", btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
new InputManagerEntry { name = kDPadVertical, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Seventh, btnPositive = "up", btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
new InputManagerEntry { name = kDPadHorizontal, kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Sixth, btnPositive = "right", btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
};
InputRegistering.RegisterInputs(inputEntries);
#endif
#if USE_INPUT_SYSTEM
// Register input system actions
var enableAction = debugActionMap.AddAction(kEnableDebug, type: InputActionType.Button);
enableAction.AddCompositeBinding("ButtonWithOneModifier")
.With("Modifier", "<Gamepad>/rightStickPress")
.With("Button", "<Gamepad>/leftStickPress")
.With("Modifier", "<Keyboard>/leftCtrl")
.With("Button", "<Keyboard>/backspace");
var resetAction = debugActionMap.AddAction(kResetBtn, type: InputActionType.Button);
resetAction.AddCompositeBinding("ButtonWithOneModifier")
.With("Modifier", "<Gamepad>/rightStickPress")
.With("Button", "<Gamepad>/b")
.With("Modifier", "<Keyboard>/leftAlt")
.With("Button", "<Keyboard>/backspace");
var next = debugActionMap.AddAction(kDebugNextBtn, type: InputActionType.Button);
next.AddBinding("<Keyboard>/pageDown");
next.AddBinding("<Gamepad>/rightShoulder");
var previous = debugActionMap.AddAction(kDebugPreviousBtn, type: InputActionType.Button);
previous.AddBinding("<Keyboard>/pageUp");
previous.AddBinding("<Gamepad>/leftShoulder");
var validateAction = debugActionMap.AddAction(kValidateBtn, type: InputActionType.Button);
validateAction.AddBinding("<Keyboard>/enter");
validateAction.AddBinding("<Gamepad>/a");
var persistentAction = debugActionMap.AddAction(kPersistentBtn, type: InputActionType.Button);
persistentAction.AddBinding("<Keyboard>/rightShift");
persistentAction.AddBinding("<Gamepad>/x");
var multiplierAction = debugActionMap.AddAction(kMultiplierBtn, type: InputActionType.Value);
multiplierAction.AddBinding("<Keyboard>/leftShift");
multiplierAction.AddBinding("<Gamepad>/y");
var moveVerticalAction = debugActionMap.AddAction(kDPadVertical);
moveVerticalAction.AddCompositeBinding("1DAxis")
.With("Positive", "<Gamepad>/dpad/up")
.With("Negative", "<Gamepad>/dpad/down")
.With("Positive", "<Keyboard>/upArrow")
.With("Negative", "<Keyboard>/downArrow");
var moveHorizontalAction = debugActionMap.AddAction(kDPadHorizontal);
moveHorizontalAction.AddCompositeBinding("1DAxis")
.With("Positive", "<Gamepad>/dpad/right")
.With("Negative", "<Gamepad>/dpad/left")
.With("Positive", "<Keyboard>/rightArrow")
.With("Negative", "<Keyboard>/leftArrow");
#endif
}
}
class DebugActionDesc
{
#if USE_INPUT_SYSTEM
public InputAction buttonAction = null;
#else
public string axisTrigger = "";
public List<string[]> buttonTriggerList = new List<string[]>();
public List<KeyCode[]> keyTriggerList = new List<KeyCode[]>();
#endif
public DebugActionRepeatMode repeatMode = DebugActionRepeatMode.Never;
public float repeatDelay;
}
class DebugActionState
{
enum DebugActionKeyType
{
Button,
Axis,
Key
}
DebugActionKeyType m_Type;
#if USE_INPUT_SYSTEM
InputAction inputAction;
#else
string[] m_PressedButtons;
string m_PressedAxis = "";
KeyCode[] m_PressedKeys;
#endif
bool[] m_TriggerPressedUp;
float m_Timer;
internal bool runningAction { get; private set; }
internal float actionState { get; private set; }
void Trigger(int triggerCount, float state)
{
actionState = state;
runningAction = true;
m_Timer = 0f;
m_TriggerPressedUp = new bool[triggerCount];
for (int i = 0; i < m_TriggerPressedUp.Length; ++i)
m_TriggerPressedUp[i] = false;
}
#if USE_INPUT_SYSTEM
public void TriggerWithButton(InputAction action, float state)
{
inputAction = action;
Trigger(action.bindings.Count, state);
}
#else
public void TriggerWithButton(string[] buttons, float state)
{
m_Type = DebugActionKeyType.Button;
m_PressedButtons = buttons;
m_PressedAxis = "";
Trigger(buttons.Length, state);
}
public void TriggerWithAxis(string axis, float state)
{
m_Type = DebugActionKeyType.Axis;
m_PressedAxis = axis;
Trigger(1, state);
}
public void TriggerWithKey(KeyCode[] keys, float state)
{
m_Type = DebugActionKeyType.Key;
m_PressedKeys = keys;
m_PressedAxis = "";
Trigger(keys.Length, state);
}
#endif
void Reset()
{
runningAction = false;
m_Timer = 0f;
m_TriggerPressedUp = null;
}
public void Update(DebugActionDesc desc)
{
// Always reset this so that the action can only be caught once until repeat/reset
actionState = 0f;
if (m_TriggerPressedUp != null)
{
m_Timer += Time.deltaTime;
for (int i = 0; i < m_TriggerPressedUp.Length; ++i)
{
#if USE_INPUT_SYSTEM
if (inputAction != null)
m_TriggerPressedUp[i] |= Mathf.Approximately(inputAction.ReadValue<float>(), 0f);
#else
if (m_Type == DebugActionKeyType.Button)
m_TriggerPressedUp[i] |= Input.GetButtonUp(m_PressedButtons[i]);
else if (m_Type == DebugActionKeyType.Axis)
m_TriggerPressedUp[i] |= Mathf.Approximately(Input.GetAxis(m_PressedAxis), 0f);
else
m_TriggerPressedUp[i] |= Input.GetKeyUp(m_PressedKeys[i]);
#endif
}
bool allTriggerUp = true;
foreach (bool value in m_TriggerPressedUp)
allTriggerUp &= value;
if (allTriggerUp || (m_Timer > desc.repeatDelay && desc.repeatMode == DebugActionRepeatMode.Delay))
Reset();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b9ea0a15a47dead4ebc52bd3f7a926c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,150 @@
using System;
using System.Diagnostics;
using UnityEngine.Rendering.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH
using UnityEngine.UI;
#endif
namespace UnityEngine.Rendering
{
using UnityObject = UnityEngine.Object;
public sealed partial class DebugManager
{
/// <summary>
/// The modes of the UI of the Rendering Debugger
/// </summary>
public enum UIMode : int
{
/// <summary>
/// Editor Window
/// </summary>
EditorMode,
/// <summary>
/// In Game view
/// </summary>
RuntimeMode
}
/// <summary>
/// Event that is raised when a window state is changed
/// </summary>
public static event Action<UIMode, bool> windowStateChanged;
class UIState
{
public UIMode mode;
[SerializeField]
private bool m_Open;
public bool open
{
get => m_Open;
set
{
if (m_Open == value)
return;
m_Open = value;
windowStateChanged?.Invoke(mode, m_Open);
}
}
}
private UIState editorUIState = new UIState() { mode = UIMode.EditorMode };
/// <summary>
/// Is the debug editor window open.
/// </summary>
public bool displayEditorUI
{
get => editorUIState.open;
set => editorUIState.open = value;
}
private bool m_EnableRuntimeUI = true;
/// <summary>
/// Controls whether runtime UI can be enabled. When this is set to false, there will be no overhead
/// from debug GameObjects or runtime initialization.
/// </summary>
public bool enableRuntimeUI
{
get => m_EnableRuntimeUI;
set
{
if (value != m_EnableRuntimeUI)
{
m_EnableRuntimeUI = value;
DebugUpdater.SetEnabled(value);
}
}
}
private UIState runtimeUIState = new UIState() { mode = UIMode.RuntimeMode };
/// <summary>
/// Displays the runtime version of the debug window.
/// </summary>
public bool displayRuntimeUI
{
get => m_Root != null && m_Root.activeInHierarchy;
set
{
if (value)
{
m_Root = UnityObject.Instantiate(Resources.Load<Transform>("DebugUICanvas")).gameObject;
m_Root.name = "[Debug Canvas]";
m_Root.transform.localPosition = Vector3.zero;
m_RootUICanvas = m_Root.GetComponent<DebugUIHandlerCanvas>();
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH
var canvasScaler = m_Root.GetComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
#endif
m_Root.SetActive(true);
}
else
{
CoreUtils.Destroy(m_Root);
m_Root = null;
m_RootUICanvas = null;
}
onDisplayRuntimeUIChanged(value);
DebugUpdater.HandleInternalEventSystemComponents(value);
runtimeUIState.open = m_Root != null && m_Root.activeInHierarchy;
}
}
/// <summary>
/// Displays the persistent runtime debug window.
/// </summary>
public bool displayPersistentRuntimeUI
{
get => m_RootUIPersistentCanvas != null && m_PersistentRoot.activeInHierarchy;
set
{
if (value)
{
EnsurePersistentCanvas();
}
else
{
CoreUtils.Destroy(m_PersistentRoot);
m_PersistentRoot = null;
m_RootUIPersistentCanvas = null;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77aa0fe63e0ce9b428385908d4cd2663
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,431 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using UnityEngine.Assertions;
using UnityEngine.Rendering.UI;
namespace UnityEngine.Rendering
{
using UnityObject = UnityEngine.Object;
/// <summary>
/// IDebugData interface.
/// </summary>
public interface IDebugData
{
/// <summary>Get the reset callback for this DebugData</summary>
/// <returns>The reset callback</returns>
Action GetReset();
//Action GetLoad();
//Action GetSave();
}
/// <summary>
/// Manager class for the Debug Window.
/// </summary>
public sealed partial class DebugManager
{
static readonly Lazy<DebugManager> s_Instance = new Lazy<DebugManager>(() => new DebugManager());
/// <summary>
/// Global instance of the DebugManager.
/// </summary>
public static DebugManager instance => s_Instance.Value;
ReadOnlyCollection<DebugUI.Panel> m_ReadOnlyPanels;
readonly List<DebugUI.Panel> m_Panels = new List<DebugUI.Panel>();
void UpdateReadOnlyCollection()
{
m_Panels.Sort();
m_ReadOnlyPanels = m_Panels.AsReadOnly();
}
/// <summary>
/// List of currently registered debug panels.
/// </summary>
public ReadOnlyCollection<DebugUI.Panel> panels
{
get
{
if (m_ReadOnlyPanels == null)
UpdateReadOnlyCollection();
return m_ReadOnlyPanels;
}
}
/// <summary>
/// Callback called when the runtime UI changed.
/// </summary>
public event Action<bool> onDisplayRuntimeUIChanged = delegate { };
/// <summary>
/// Callback called when the debug window is dirty.
/// </summary>
public event Action onSetDirty = delegate { };
event Action resetData;
/// <summary>
/// Force an editor request.
/// </summary>
public bool refreshEditorRequested;
int? m_RequestedPanelIndex;
GameObject m_Root;
DebugUIHandlerCanvas m_RootUICanvas;
GameObject m_PersistentRoot;
DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas;
/// <summary>
/// Is any debug window or UI currently active.
/// </summary>
public bool isAnyDebugUIActive
{
get
{
return
displayRuntimeUI || displayPersistentRuntimeUI
#if UNITY_EDITOR
|| displayEditorUI
#endif
;
}
}
DebugManager()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
RegisterInputs();
RegisterActions();
#endif
}
/// <summary>
/// Refresh the debug window.
/// </summary>
public void RefreshEditor()
{
refreshEditorRequested = true;
}
/// <summary>
/// Reset the debug window.
/// </summary>
public void Reset()
{
resetData?.Invoke();
ReDrawOnScreenDebug();
}
/// <summary>
/// Request the runtime debug UI be redrawn on the next update.
/// </summary>
public void ReDrawOnScreenDebug()
{
if (displayRuntimeUI)
m_RootUICanvas?.RequestHierarchyReset();
}
/// <summary>
/// Register debug data.
/// </summary>
/// <param name="data">Data to be registered.</param>
public void RegisterData(IDebugData data) => resetData += data.GetReset();
/// <summary>
/// Register debug data.
/// </summary>
/// <param name="data">Data to be registered.</param>
public void UnregisterData(IDebugData data) => resetData -= data.GetReset();
/// <summary>
/// Get hashcode state of the Debug Window.
/// </summary>
/// <returns></returns>
public int GetState()
{
int hash = 17;
foreach (var panel in m_Panels)
hash = hash * 23 + panel.GetHashCode();
return hash;
}
internal void RegisterRootCanvas(DebugUIHandlerCanvas root)
{
Assert.IsNotNull(root);
m_Root = root.gameObject;
m_RootUICanvas = root;
}
internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
{
m_RootUICanvas.ChangeSelection(widget, fromNext);
}
internal void SetScrollTarget(DebugUIHandlerWidget widget)
{
if (m_RootUICanvas != null)
m_RootUICanvas.SetScrollTarget(widget);
}
void EnsurePersistentCanvas()
{
if (m_RootUIPersistentCanvas == null)
{
var uiManager = UnityObject.FindObjectOfType<DebugUIHandlerPersistentCanvas>();
if (uiManager == null)
{
m_PersistentRoot = UnityObject.Instantiate(Resources.Load<Transform>("DebugUIPersistentCanvas")).gameObject;
m_PersistentRoot.name = "[Debug Canvas - Persistent]";
m_PersistentRoot.transform.localPosition = Vector3.zero;
}
else
{
m_PersistentRoot = uiManager.gameObject;
}
m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent<DebugUIHandlerPersistentCanvas>();
}
}
internal void TogglePersistent(DebugUI.Widget widget, int? forceTupleIndex = null)
{
if (widget == null)
return;
EnsurePersistentCanvas();
switch (widget)
{
case DebugUI.Value value:
m_RootUIPersistentCanvas.Toggle(value);
break;
case DebugUI.ValueTuple valueTuple:
m_RootUIPersistentCanvas.Toggle(valueTuple, forceTupleIndex);
break;
case DebugUI.Container container:
// When container is toggled, we make sure that if there are ValueTuples, they all get the same element index.
int pinnedIndex = container.children.Max(w => (w as DebugUI.ValueTuple)?.pinnedElementIndex ?? -1);
foreach (var child in container.children)
{
if (child is DebugUI.Value || child is DebugUI.ValueTuple)
TogglePersistent(child, pinnedIndex);
}
break;
default:
Debug.Log("Only readonly items can be made persistent.");
break;
}
}
void OnPanelDirty(DebugUI.Panel panel)
{
onSetDirty();
}
/// <summary>
/// Returns the panel index
/// </summary>
/// <param name="displayName">The displayname for the panel</param>
/// <returns>The index for the panel or -1 if not found.</returns>
public int PanelIndex([DisallowNull] string displayName)
{
displayName ??= string.Empty;
for (int i = 0; i < m_Panels.Count; ++i)
{
if (displayName.Equals(m_Panels[i].displayName, StringComparison.InvariantCultureIgnoreCase))
return i;
}
return -1;
}
/// <summary>
/// Request DebugWindow to open the specified panel.
/// </summary>
/// <param name="index">Index of the debug window panel to activate.</param>
public void RequestEditorWindowPanelIndex(int index)
{
// Similar to RefreshEditor(), this function is required to bypass a dependency problem where DebugWindow
// cannot be accessed from the Core.Runtime assembly. Should there be a better way to allow editor-dependent
// features in DebugUI?
m_RequestedPanelIndex = index;
}
internal int? GetRequestedEditorWindowPanelIndex()
{
int? requestedIndex = m_RequestedPanelIndex;
m_RequestedPanelIndex = null;
return requestedIndex;
}
// TODO: Optimally we should use a query path here instead of a display name
/// <summary>
/// Returns a debug panel.
/// </summary>
/// <param name="displayName">Name of the debug panel.</param>
/// <param name="createIfNull">Create the panel if it does not exists.</param>
/// <param name="groupIndex">Group index.</param>
/// <param name="overrideIfExist">Replace an existing panel.</param>
/// <returns></returns>
public DebugUI.Panel GetPanel(string displayName, bool createIfNull = false, int groupIndex = 0, bool overrideIfExist = false)
{
int panelIndex = PanelIndex(displayName);
DebugUI.Panel p = panelIndex >= 0 ? m_Panels[panelIndex] : null;
if (p != null)
{
if (overrideIfExist)
{
p.onSetDirty -= OnPanelDirty;
RemovePanel(p);
p = null;
}
else
return p;
}
if (createIfNull)
{
p = new DebugUI.Panel { displayName = displayName, groupIndex = groupIndex };
p.onSetDirty += OnPanelDirty;
m_Panels.Add(p);
UpdateReadOnlyCollection();
}
return p;
}
/// <summary>
/// Find the index of the panel from it's display name.
/// </summary>
/// <param name="displayName">The display name of the panel to find.</param>
/// <returns>The index of the panel in the list. -1 if not found.</returns>
public int FindPanelIndex(string displayName)
=> m_Panels.FindIndex(p => p.displayName == displayName);
// TODO: Use a query path here as well instead of a display name
/// <summary>
/// Remove a debug panel.
/// </summary>
/// <param name="displayName">Name of the debug panel to remove.</param>
public void RemovePanel(string displayName)
{
DebugUI.Panel panel = null;
foreach (var p in m_Panels)
{
if (p.displayName == displayName)
{
p.onSetDirty -= OnPanelDirty;
panel = p;
break;
}
}
RemovePanel(panel);
}
/// <summary>
/// Remove a debug panel.
/// </summary>
/// <param name="panel">Reference to the debug panel to remove.</param>
public void RemovePanel(DebugUI.Panel panel)
{
if (panel == null)
return;
m_Panels.Remove(panel);
UpdateReadOnlyCollection();
}
/// <summary>
/// Gets an <see cref="DebugUI.Widget[]"/> matching the given <see cref="DebugUI.Flags"/>
/// </summary>
/// <param name="flags">The flags of the widget</param>
/// <returns>Reference to the requested debug item.</returns>
public DebugUI.Widget[] GetItems(DebugUI.Flags flags)
{
using (ListPool<DebugUI.Widget>.Get(out var temp))
{
foreach (var panel in m_Panels)
{
var widgets = GetItemsFromContainer(flags, panel);
temp.AddRange(widgets);
}
return temp.ToArray();
}
}
internal DebugUI.Widget[] GetItemsFromContainer(DebugUI.Flags flags, DebugUI.IContainer container)
{
using (ListPool<DebugUI.Widget>.Get(out var temp))
{
foreach (var child in container.children)
{
if (child.flags.HasFlag(flags))
{
temp.Add(child);
continue;
}
if (child is DebugUI.IContainer containerChild)
{
temp.AddRange(GetItemsFromContainer(flags, containerChild));
}
}
return temp.ToArray();
}
}
/// <summary>
/// Get a Debug Item.
/// </summary>
/// <param name="queryPath">Path of the debug item.</param>
/// <returns>Reference to the requested debug item.</returns>
public DebugUI.Widget GetItem(string queryPath)
{
foreach (var panel in m_Panels)
{
var w = GetItem(queryPath, panel);
if (w != null)
return w;
}
return null;
}
/// <summary>
/// Get a debug item from a specific container.
/// </summary>
/// <param name="queryPath">Path of the debug item.</param>
/// <param name="container">Container to query.</param>
/// <returns>Reference to the requested debug item.</returns>
DebugUI.Widget GetItem(string queryPath, DebugUI.IContainer container)
{
foreach (var child in container.children)
{
if (child.queryPath == queryPath)
return child;
if (child is DebugUI.IContainer containerChild)
{
var w = GetItem(queryPath, containerChild);
if (w != null)
return w;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a09c907f481d4894a96697283a87c352
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,516 @@
namespace UnityEngine.Rendering
{
/// <summary>Debug class containing several debug shapes for debugging</summary>
public partial class DebugShapes
{
// Singleton
static DebugShapes s_Instance = null;
/// <summary>Singleton instance</summary>
static public DebugShapes instance
{
get
{
if (s_Instance == null)
{
s_Instance = new DebugShapes();
}
return s_Instance;
}
}
Mesh m_sphereMesh = null;
Mesh m_boxMesh = null;
Mesh m_coneMesh = null;
Mesh m_pyramidMesh = null;
// This code has been grabbed from http://wiki.unity3d.com/index.php/ProceduralPrimitives
void BuildSphere(ref Mesh outputMesh, float radius, uint longSubdiv, uint latSubdiv)
{
// Make sure it is empty before pushing anything to it
outputMesh.Clear();
// Build the vertices array
Vector3[] vertices = new Vector3[(longSubdiv + 1) * latSubdiv + 2];
float _pi = Mathf.PI;
float _2pi = _pi * 2f;
vertices[0] = Vector3.up * radius;
for (int lat = 0; lat < latSubdiv; lat++)
{
float a1 = _pi * (float)(lat + 1) / (latSubdiv + 1);
float sin1 = Mathf.Sin(a1);
float cos1 = Mathf.Cos(a1);
for (int lon = 0; lon <= longSubdiv; lon++)
{
float a2 = _2pi * (float)(lon == longSubdiv ? 0 : lon) / longSubdiv;
float sin2 = Mathf.Sin(a2);
float cos2 = Mathf.Cos(a2);
vertices[lon + lat * (longSubdiv + 1) + 1] = new Vector3(sin1 * cos2, cos1, sin1 * sin2) * radius;
}
}
vertices[vertices.Length - 1] = Vector3.up * -radius;
// Build the normals array
Vector3[] normals = new Vector3[vertices.Length];
for (int n = 0; n < vertices.Length; n++)
{
normals[n] = vertices[n].normalized;
}
// Build the UV array
Vector2[] uvs = new Vector2[vertices.Length];
uvs[0] = Vector2.up;
uvs[uvs.Length - 1] = Vector2.zero;
for (int lat = 0; lat < latSubdiv; lat++)
{
for (int lon = 0; lon <= longSubdiv; lon++)
{
uvs[lon + lat * (longSubdiv + 1) + 1] = new Vector2((float)lon / longSubdiv, 1f - (float)(lat + 1) / (latSubdiv + 1));
}
}
// Build the index array
int nbFaces = vertices.Length;
int nbTriangles = nbFaces * 2;
int nbIndexes = nbTriangles * 3;
int[] triangles = new int[nbIndexes];
// Top Cap
int i = 0;
for (int lon = 0; lon < longSubdiv; lon++)
{
triangles[i++] = lon + 2;
triangles[i++] = lon + 1;
triangles[i++] = 0;
}
//Middle
for (uint lat = 0; lat < latSubdiv - 1; lat++)
{
for (uint lon = 0; lon < longSubdiv; lon++)
{
uint current = lon + lat * (longSubdiv + 1) + 1;
uint next = current + longSubdiv + 1;
triangles[i++] = (int)current;
triangles[i++] = (int)current + 1;
triangles[i++] = (int)next + 1;
triangles[i++] = (int)current;
triangles[i++] = (int)next + 1;
triangles[i++] = (int)next;
}
}
// Bottom Cap
for (int lon = 0; lon < longSubdiv; lon++)
{
triangles[i++] = vertices.Length - 1;
triangles[i++] = vertices.Length - (lon + 2) - 1;
triangles[i++] = vertices.Length - (lon + 1) - 1;
}
// Assign them to
outputMesh.vertices = vertices;
outputMesh.normals = normals;
outputMesh.uv = uvs;
outputMesh.triangles = triangles;
outputMesh.RecalculateBounds();
}
void BuildBox(ref Mesh outputMesh, float length, float width, float height)
{
outputMesh.Clear();
Vector3 p0 = new Vector3(-length * .5f, -width * .5f, height * .5f);
Vector3 p1 = new Vector3(length * .5f, -width * .5f, height * .5f);
Vector3 p2 = new Vector3(length * .5f, -width * .5f, -height * .5f);
Vector3 p3 = new Vector3(-length * .5f, -width * .5f, -height * .5f);
Vector3 p4 = new Vector3(-length * .5f, width * .5f, height * .5f);
Vector3 p5 = new Vector3(length * .5f, width * .5f, height * .5f);
Vector3 p6 = new Vector3(length * .5f, width * .5f, -height * .5f);
Vector3 p7 = new Vector3(-length * .5f, width * .5f, -height * .5f);
Vector3[] vertices = new Vector3[]
{
// Bottom
p0, p1, p2, p3,
// Left
p7, p4, p0, p3,
// Front
p4, p5, p1, p0,
// Back
p6, p7, p3, p2,
// Right
p5, p6, p2, p1,
// Top
p7, p6, p5, p4
};
Vector3 up = Vector3.up;
Vector3 down = Vector3.down;
Vector3 front = Vector3.forward;
Vector3 back = Vector3.back;
Vector3 left = Vector3.left;
Vector3 right = Vector3.right;
Vector3[] normales = new Vector3[]
{
// Bottom
down, down, down, down,
// Left
left, left, left, left,
// Front
front, front, front, front,
// Back
back, back, back, back,
// Right
right, right, right, right,
// Top
up, up, up, up
};
Vector2 _00 = new Vector2(0f, 0f);
Vector2 _10 = new Vector2(1f, 0f);
Vector2 _01 = new Vector2(0f, 1f);
Vector2 _11 = new Vector2(1f, 1f);
Vector2[] uvs = new Vector2[]
{
// Bottom
_11, _01, _00, _10,
// Left
_11, _01, _00, _10,
// Front
_11, _01, _00, _10,
// Back
_11, _01, _00, _10,
// Right
_11, _01, _00, _10,
// Top
_11, _01, _00, _10,
};
int[] triangles = new int[]
{
// Bottom
3, 1, 0,
3, 2, 1,
// Left
3 + 4 * 1, 1 + 4 * 1, 0 + 4 * 1,
3 + 4 * 1, 2 + 4 * 1, 1 + 4 * 1,
// Front
3 + 4 * 2, 1 + 4 * 2, 0 + 4 * 2,
3 + 4 * 2, 2 + 4 * 2, 1 + 4 * 2,
// Back
3 + 4 * 3, 1 + 4 * 3, 0 + 4 * 3,
3 + 4 * 3, 2 + 4 * 3, 1 + 4 * 3,
// Right
3 + 4 * 4, 1 + 4 * 4, 0 + 4 * 4,
3 + 4 * 4, 2 + 4 * 4, 1 + 4 * 4,
// Top
3 + 4 * 5, 1 + 4 * 5, 0 + 4 * 5,
3 + 4 * 5, 2 + 4 * 5, 1 + 4 * 5,
};
outputMesh.vertices = vertices;
outputMesh.normals = normales;
outputMesh.uv = uvs;
outputMesh.triangles = triangles;
outputMesh.RecalculateBounds();
}
void BuildCone(ref Mesh outputMesh, float height, float topRadius, float bottomRadius, int nbSides)
{
outputMesh.Clear();
int nbVerticesCap = nbSides + 1;
// bottom + top + sides
Vector3[] vertices = new Vector3[nbVerticesCap + nbVerticesCap + nbSides * 2 + 2];
int vert = 0;
float _2pi = Mathf.PI * 2f;
// Bottom cap
vertices[vert++] = new Vector3(0f, 0f, 0f);
while (vert <= nbSides)
{
float rad = (float)vert / nbSides * _2pi;
vertices[vert] = new Vector3(Mathf.Sin(rad) * bottomRadius, Mathf.Cos(rad) * bottomRadius, 0f);
vert++;
}
// Top cap
vertices[vert++] = new Vector3(0f, 0f, height);
while (vert <= nbSides * 2 + 1)
{
float rad = (float)(vert - nbSides - 1) / nbSides * _2pi;
vertices[vert] = new Vector3(Mathf.Sin(rad) * topRadius, Mathf.Cos(rad) * topRadius, height);
vert++;
}
// Sides
int v = 0;
while (vert <= vertices.Length - 4)
{
float rad = (float)v / nbSides * _2pi;
vertices[vert] = new Vector3(Mathf.Sin(rad) * topRadius, Mathf.Cos(rad) * topRadius, height);
vertices[vert + 1] = new Vector3(Mathf.Sin(rad) * bottomRadius, Mathf.Cos(rad) * bottomRadius, 0);
vert += 2;
v++;
}
vertices[vert] = vertices[nbSides * 2 + 2];
vertices[vert + 1] = vertices[nbSides * 2 + 3];
// bottom + top + sides
Vector3[] normales = new Vector3[vertices.Length];
vert = 0;
// Bottom cap
while (vert <= nbSides)
{
normales[vert++] = new Vector3(0, 0, -1);
}
// Top cap
while (vert <= nbSides * 2 + 1)
{
normales[vert++] = new Vector3(0, 0, 1);
}
// Sides
v = 0;
while (vert <= vertices.Length - 4)
{
float rad = (float)v / nbSides * _2pi;
float cos = Mathf.Cos(rad);
float sin = Mathf.Sin(rad);
normales[vert] = new Vector3(sin, cos, 0f);
normales[vert + 1] = normales[vert];
vert += 2;
v++;
}
normales[vert] = normales[nbSides * 2 + 2];
normales[vert + 1] = normales[nbSides * 2 + 3];
Vector2[] uvs = new Vector2[vertices.Length];
// Bottom cap
int u = 0;
uvs[u++] = new Vector2(0.5f, 0.5f);
while (u <= nbSides)
{
float rad = (float)u / nbSides * _2pi;
uvs[u] = new Vector2(Mathf.Cos(rad) * .5f + .5f, Mathf.Sin(rad) * .5f + .5f);
u++;
}
// Top cap
uvs[u++] = new Vector2(0.5f, 0.5f);
while (u <= nbSides * 2 + 1)
{
float rad = (float)u / nbSides * _2pi;
uvs[u] = new Vector2(Mathf.Cos(rad) * .5f + .5f, Mathf.Sin(rad) * .5f + .5f);
u++;
}
// Sides
int u_sides = 0;
while (u <= uvs.Length - 4)
{
float t = (float)u_sides / nbSides;
uvs[u] = new Vector3(t, 1f);
uvs[u + 1] = new Vector3(t, 0f);
u += 2;
u_sides++;
}
uvs[u] = new Vector2(1f, 1f);
uvs[u + 1] = new Vector2(1f, 0f);
int nbTriangles = nbSides + nbSides + nbSides * 2;
int[] triangles = new int[nbTriangles * 3 + 3];
// Bottom cap
int tri = 0;
int i = 0;
while (tri < nbSides - 1)
{
triangles[i] = 0;
triangles[i + 1] = tri + 1;
triangles[i + 2] = tri + 2;
tri++;
i += 3;
}
triangles[i] = 0;
triangles[i + 1] = tri + 1;
triangles[i + 2] = 1;
tri++;
i += 3;
// Top cap
//tri++;
while (tri < nbSides * 2)
{
triangles[i] = tri + 2;
triangles[i + 1] = tri + 1;
triangles[i + 2] = nbVerticesCap;
tri++;
i += 3;
}
triangles[i] = nbVerticesCap + 1;
triangles[i + 1] = tri + 1;
triangles[i + 2] = nbVerticesCap;
tri++;
i += 3;
tri++;
// Sides
while (tri <= nbTriangles)
{
triangles[i] = tri + 2;
triangles[i + 1] = tri + 1;
triangles[i + 2] = tri + 0;
tri++;
i += 3;
triangles[i] = tri + 1;
triangles[i + 1] = tri + 2;
triangles[i + 2] = tri + 0;
tri++;
i += 3;
}
outputMesh.vertices = vertices;
outputMesh.normals = normales;
outputMesh.uv = uvs;
outputMesh.triangles = triangles;
outputMesh.RecalculateBounds();
}
void BuildPyramid(ref Mesh outputMesh, float width, float height, float depth)
{
outputMesh.Clear();
// Allocate the buffer
Vector3[] vertices = new Vector3[16];
// Top Face
vertices[0] = new Vector3(0f, 0f, 0f);
vertices[1] = new Vector3(-width / 2.0f, height / 2.0f, depth);
vertices[2] = new Vector3(width / 2.0f, height / 2.0f, depth);
// Left Face
vertices[3] = new Vector3(0f, 0f, 0f);
vertices[4] = new Vector3(width / 2.0f, height / 2.0f, depth);
vertices[5] = new Vector3(width / 2.0f, -height / 2.0f, depth);
// Bottom Face
vertices[6] = new Vector3(0f, 0f, 0f);
vertices[7] = new Vector3(width / 2.0f, -height / 2.0f, depth);
vertices[8] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
// Right Face
vertices[9] = new Vector3(0f, 0f, 0f);
vertices[10] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
vertices[11] = new Vector3(-width / 2.0f, height / 2.0f, depth);
// Cap
vertices[12] = new Vector3(-width / 2.0f, height / 2.0f, depth);
vertices[13] = new Vector3(-width / 2.0f, -height / 2.0f, depth);
vertices[14] = new Vector3(width / 2.0f, -height / 2.0f, depth);
vertices[15] = new Vector3(width / 2.0f, height / 2.0f, depth);
// TODO: support the uv/normals
Vector3[] normals = new Vector3[vertices.Length];
Vector2[] uvs = new Vector2[vertices.Length];
// The indexes for the side part is simple
int[] triangles = new int[18];
for (int idx = 0; idx < 12; ++idx)
{
triangles[idx] = idx;
}
// Cap indexes
triangles[12] = 12;
triangles[13] = 13;
triangles[14] = 14;
triangles[15] = 12;
triangles[16] = 14;
triangles[17] = 15;
outputMesh.vertices = vertices;
outputMesh.normals = normals;
outputMesh.uv = uvs;
outputMesh.triangles = triangles;
outputMesh.RecalculateBounds();
}
void BuildShapes()
{
m_sphereMesh = new Mesh();
BuildSphere(ref m_sphereMesh, 1.0f, 24, 16);
m_boxMesh = new Mesh();
BuildBox(ref m_boxMesh, 1.0f, 1.0f, 1.0f);
m_coneMesh = new Mesh();
BuildCone(ref m_coneMesh, 1.0f, 1.0f, 0.0f, 16);
m_pyramidMesh = new Mesh();
BuildPyramid(ref m_pyramidMesh, 1.0f, 1.0f, 1.0f);
}
void RebuildResources()
{
if (m_sphereMesh == null || m_boxMesh == null || m_coneMesh == null || m_pyramidMesh == null)
{
BuildShapes();
}
}
/// <summary>Get a Sphere Mesh</summary>
/// <returns>A Sphere Mesh</returns>
public Mesh RequestSphereMesh()
{
RebuildResources();
return m_sphereMesh;
}
/// <summary>Get a Box Mesh</summary>
/// <returns>A Box Mesh</returns>
public Mesh RequestBoxMesh()
{
RebuildResources();
return m_boxMesh;
}
/// <summary>Get a Cone Mesh</summary>
/// <returns>A Cone Mesh</returns>
public Mesh RequestConeMesh()
{
RebuildResources();
return m_coneMesh;
}
/// <summary>Get a Pyramid Mesh</summary>
/// <returns>A Pyramid Mesh</returns>
public Mesh RequestPyramidMesh()
{
RebuildResources();
return m_pyramidMesh;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a164ae4d75dc0074b966a2efdf0a5700
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,468 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace UnityEngine.Rendering
{
public partial class DebugUI
{
/// <summary>
/// Base class for "container" type widgets, although it can be used on its own (if a display name is set then it'll behave as a group with a header)
/// </summary>
public class Container : Widget, IContainer
{
const string k_IDToken = "#";
internal bool hideDisplayName => string.IsNullOrEmpty(displayName) || displayName.StartsWith(k_IDToken);
/// <summary>
/// List of children.
/// </summary>
public ObservableList<Widget> children { get; private set; }
/// <summary>
/// Panel the container is attached to.
/// </summary>
public override Panel panel
{
get { return m_Panel; }
internal set
{
/// Frequenlty used panels do now own widgets
if (value != null && value.flags.HasFlag(DebugUI.Flags.FrequentlyUsed))
return;
m_Panel = value;
// Bubble down
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
children[i].panel = value;
}
}
/// <summary>
/// Constructor
/// </summary>
public Container()
: this(string.Empty, new ObservableList<Widget>())
{
}
/// <summary>
/// Constructor for a container without header
/// </summary>
/// <param name="id">The id of the container</param>
public Container(string id)
: this($"{k_IDToken}{id}", new ObservableList<Widget>())
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="displayName">Display name of the container.</param>
/// <param name="children">List of attached children.</param>
public Container(string displayName, ObservableList<Widget> children)
{
this.displayName = displayName;
this.children = children;
children.ItemAdded += OnItemAdded;
children.ItemRemoved += OnItemRemoved;
// Call OnAdded callback for already existing items to ensure their panel & parent are set
for (int i = 0; i < this.children.Count; i++)
OnItemAdded(this.children, new ListChangedEventArgs<Widget>(i, this.children[i]));
}
internal override void GenerateQueryPath()
{
base.GenerateQueryPath();
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
children[i].GenerateQueryPath();
}
/// <summary>
/// Method called when a children is added.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of added children.</param>
protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
if (e.item != null)
{
e.item.panel = m_Panel;
e.item.parent = this;
}
if (m_Panel != null)
m_Panel.SetDirty();
}
/// <summary>
/// Method called when a children is removed.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of removed children.</param>
protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
if (e.item != null)
{
e.item.panel = null;
e.item.parent = null;
}
if (m_Panel != null)
m_Panel.SetDirty();
}
/// <summary>
/// Returns the hash code of the widget.
/// </summary>
/// <returns>Hash code of the widget.</returns>
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + queryPath.GetHashCode();
hash = hash * 23 + isHidden.GetHashCode();
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
hash = hash * 23 + children[i].GetHashCode();
return hash;
}
}
/// <summary>
/// Unity-like foldout that can be collapsed.
/// </summary>
public class Foldout : Container, IValueField
{
/// <summary>
/// Context menu item.
/// </summary>
public struct ContextMenuItem
{
/// <summary>
/// Name of the item displayed in context menu dropdown.
/// </summary>
public string displayName;
/// <summary>
/// Callback when context menu item is selected.
/// </summary>
public Action action;
}
/// <summary>
/// Always false.
/// </summary>
public bool isReadOnly { get { return false; } }
/// <summary>
/// Opened state of the foldout.
/// </summary>
public bool opened;
/// <summary>
/// Draw the foldout in full width using a header style.
/// </summary>
public bool isHeader;
/// <summary>
/// Optional list of context menu items. If the list is not provided, no context menu button will be displayed.
/// </summary>
public List<ContextMenuItem> contextMenuItems = null;
/// <summary>
/// List of columns labels.
/// </summary>
public string[] columnLabels { get; set; } = null;
/// <summary>
/// List of columns label tooltips.
/// </summary>
public string[] columnTooltips { get; set; } = null;
/// <summary>
/// Constructor.
/// </summary>
public Foldout() : base() { }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="displayName">Display name of the foldout.</param>
/// <param name="children">List of attached children.</param>
/// <param name="columnLabels">Optional list of column names.</param>
/// <param name="columnTooltips">Optional list of tooltips for column name labels.</param>
public Foldout(string displayName, ObservableList<Widget> children, string[] columnLabels = null, string[] columnTooltips = null)
: base(displayName, children)
{
this.columnLabels = columnLabels;
this.columnTooltips = columnTooltips;
}
/// <summary>
/// Get the opened state of the foldout.
/// </summary>
/// <returns>True if the foldout is opened.</returns>
public bool GetValue() => opened;
/// <summary>
/// Get the opened state of the foldout.
/// </summary>
/// <returns>True if the foldout is opened.</returns>
object IValueField.GetValue() => GetValue();
/// <summary>
/// Set the opened state of the foldout.
/// </summary>
/// <param name="value">True to open the foldout, false to close it.</param>
public void SetValue(object value) => SetValue((bool)value);
/// <summary>
/// Validates the value of the widget before setting it.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>The validated value.</returns>
public object ValidateValue(object value) => value;
/// <summary>
/// Set the value of the widget.
/// </summary>
/// <param name="value">Input value.</param>
public void SetValue(bool value) => opened = value;
}
/// <summary>
/// Horizontal Layout Container.
/// </summary>
public class HBox : Container
{
/// <summary>
/// Constructor.
/// </summary>
public HBox()
{
displayName = "HBox";
}
}
/// <summary>
/// Vertical Layout Container.
/// </summary>
public class VBox : Container
{
/// <summary>
/// Constructor.
/// </summary>
public VBox()
{
displayName = "VBox";
}
}
/// <summary>
/// Array Container.
/// </summary>
public class Table : Container
{
/// <summary>Row Container.</summary>
public class Row : Foldout
{
/// <summary>Constructor.</summary>
public Row() { displayName = "Row"; }
}
/// <summary>
/// True if the table is read only.
/// </summary>
public bool isReadOnly = false;
/// <summary>Constructor.</summary>
public Table() { displayName = "Array"; }
/// <summary>
/// Set column visibility.
/// </summary>
/// <param name="index">Index of the column.</param>
/// <param name="visible">True if the column should be visible.</param>
public void SetColumnVisibility(int index, bool visible)
{
#if UNITY_EDITOR
var header = Header;
if (index < 0 || index >= m_ColumnCount)
return;
index++;
if (header.IsColumnVisible(index) != visible)
{
var newVisibleColumns = new System.Collections.Generic.List<int>(header.state.visibleColumns);
if (newVisibleColumns.Contains(index))
{
newVisibleColumns.Remove(index);
}
else
{
newVisibleColumns.Add(index);
newVisibleColumns.Sort();
}
header.state.visibleColumns = newVisibleColumns.ToArray();
var cols = header.state.columns;
for (int i = 0; i < cols.Length; i++)
cols[i].width = 50f;
header.ResizeToFit();
}
#else
var columns = VisibleColumns;
if (index < 0 || index > columns.Length)
return;
columns[index] = visible;
#endif
}
/// <summary>
/// Get column visibility.
/// </summary>
/// <param name="index">Index of the column.</param>
/// <returns>True if the column is visible.</returns>
public bool GetColumnVisibility(int index)
{
#if UNITY_EDITOR
var header = Header;
if (index < 0 || index >= m_ColumnCount)
return false;
return header.IsColumnVisible(index + 1);
#else
var columns = VisibleColumns;
if (index < 0 || index > columns.Length)
return false;
return columns[index];
#endif
}
#if UNITY_EDITOR
/// <summary>
/// The scroll position of the table.
/// </summary>
public Vector2 scroll = Vector2.zero;
int m_ColumnCount;
UnityEditor.IMGUI.Controls.MultiColumnHeader m_Header = null;
/// <summary>
/// The table header for drawing
/// </summary>
public UnityEditor.IMGUI.Controls.MultiColumnHeader Header
{
get
{
if (m_Header != null)
return m_Header;
if (children.Count != 0)
{
m_ColumnCount = ((Container)children[0]).children.Count;
for (int i = 1; i < children.Count; i++)
{
if (((Container)children[i]).children.Count != m_ColumnCount)
{
Debug.LogError("All rows must have the same number of children.");
return null;
}
}
}
UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column CreateColumn(string name)
{
var col = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column()
{
canSort = false,
headerTextAlignment = TextAlignment.Center,
headerContent = new GUIContent(name),
};
GUIStyle style = UnityEditor.IMGUI.Controls.MultiColumnHeader.DefaultStyles.columnHeaderCenterAligned;
style.CalcMinMaxWidth(col.headerContent, out col.width, out float _);
col.width = Mathf.Min(col.width, 50f);
return col;
}
var cols = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column[m_ColumnCount + 1];
cols[0] = CreateColumn(displayName);
cols[0].allowToggleVisibility = false;
for (int i = 0; i < m_ColumnCount; i++)
cols[i + 1] = CreateColumn(((Container)children[0]).children[i].displayName);
var state = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState(cols);
m_Header = new UnityEditor.IMGUI.Controls.MultiColumnHeader(state) { height = 23 };
m_Header.ResizeToFit();
return m_Header;
}
}
#else
bool[] m_Header = null;
/// <summary>
/// The visible columns
/// </summary>
public bool[] VisibleColumns
{
get
{
if (m_Header != null)
return m_Header;
int columnCount = 0;
if (children.Count != 0)
{
columnCount = ((Container)children[0]).children.Count;
for (int i = 1; i < children.Count; i++)
{
if (((Container)children[i]).children.Count != columnCount)
{
Debug.LogError("All rows must have the same number of children.");
return null;
}
}
}
m_Header = new bool[columnCount];
for (int i = 0; i < columnCount; i++)
m_Header[i] = true;
return m_Header;
}
}
#endif
/// <summary>
/// Method called when a children is added.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of added children.</param>
protected override void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
base.OnItemAdded(sender, e);
m_Header = null;
}
/// <summary>
/// Method called when a children is removed.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of removed children.</param>
protected override void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
base.OnItemRemoved(sender, e);
m_Header = null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02feae31429679949ab4a4a40c8ad34e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,614 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
public partial class DebugUI
{
/// <summary>
/// Generic field - will be serialized in the editor if it's not read-only
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class Field<T> : Widget, IValueField
{
/// <summary>
/// Getter for this field.
/// </summary>
public Func<T> getter { get; set; }
/// <summary>
/// Setter for this field.
/// </summary>
public Action<T> setter { get; set; }
// This should be an `event` but they don't play nice with object initializers in the
// version of C# we use.
/// <summary>
/// Callback used when the value of the field changes.
/// </summary>
public Action<Field<T>, T> onValueChanged;
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
object IValueField.ValidateValue(object value)
{
return ValidateValue((T)value);
}
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
public virtual T ValidateValue(T value)
{
return value;
}
/// <summary>
/// Get the value of the field.
/// </summary>
/// <returns>Value of the field.</returns>
object IValueField.GetValue()
{
return GetValue();
}
/// <summary>
/// Get the value of the field.
/// </summary>
/// <returns>Value of the field.</returns>
public T GetValue()
{
Assert.IsNotNull(getter);
return getter();
}
/// <summary>
/// Set the value of the field.
/// </summary>
/// <param name="value">Input value.</param>
public void SetValue(object value)
{
SetValue((T)value);
}
/// <summary>
/// Set the value of the field.
/// </summary>
/// <param name="value">Input value.</param>
public virtual void SetValue(T value)
{
Assert.IsNotNull(setter);
var v = ValidateValue(value);
if (v == null || !v.Equals(getter()))
{
setter(v);
onValueChanged?.Invoke(this, v);
}
}
}
/// <summary>
/// Boolean field.
/// </summary>
public class BoolField : Field<bool> { }
/// <summary>
/// Boolean field with history.
/// </summary>
public class HistoryBoolField : BoolField
{
/// <summary>
/// History getter for this field.
/// </summary>
public Func<bool>[] historyGetter { get; set; }
/// <summary>
/// Depth of the field's history.
/// </summary>
public int historyDepth => historyGetter?.Length ?? 0;
/// <summary>
/// Get the value of the field at a certain history index.
/// </summary>
/// <param name="historyIndex">Index of the history to query.</param>
/// <returns>Value of the field at the provided history index.</returns>
public bool GetHistoryValue(int historyIndex)
{
Assert.IsNotNull(historyGetter);
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyGetter.Length, "out of range historyIndex");
Assert.IsNotNull(historyGetter[historyIndex]);
return historyGetter[historyIndex]();
}
}
/// <summary>
/// Integer field.
/// </summary>
public class IntField : Field<int>
{
/// <summary>
/// Minimum value function.
/// </summary>
public Func<int> min;
/// <summary>
/// Maximum value function.
/// </summary>
public Func<int> max;
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public int incStep = 1;
/// <summary>
/// Step increment multiplier.
/// </summary>
public int intStepMult = 10;
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
public override int ValidateValue(int value)
{
if (min != null) value = Mathf.Max(value, min());
if (max != null) value = Mathf.Min(value, max());
return value;
}
}
/// <summary>
/// Unsigned integer field.
/// </summary>
public class UIntField : Field<uint>
{
/// <summary>
/// Minimum value function.
/// </summary>
public Func<uint> min;
/// <summary>
/// Maximum value function.
/// </summary>
public Func<uint> max;
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public uint incStep = 1u;
/// <summary>
/// Step increment multiplier.
/// </summary>
public uint intStepMult = 10u;
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
public override uint ValidateValue(uint value)
{
if (min != null) value = (uint)Mathf.Max((int)value, (int)min());
if (max != null) value = (uint)Mathf.Min((int)value, (int)max());
return value;
}
}
/// <summary>
/// Float field.
/// </summary>
public class FloatField : Field<float>
{
/// <summary>
/// Minimum value function.
/// </summary>
public Func<float> min;
/// <summary>
/// Maximum value function.
/// </summary>
public Func<float> max;
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public float incStep = 0.1f;
/// <summary>
/// Step increment multiplier.
/// </summary>
public float incStepMult = 10f;
/// <summary>
/// Number of decimals.
/// </summary>
public int decimals = 3;
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
public override float ValidateValue(float value)
{
if (min != null) value = Mathf.Max(value, min());
if (max != null) value = Mathf.Min(value, max());
return value;
}
}
/// <summary>
/// Generic <see cref="EnumField"/> that stores enumNames and enumValues
/// </summary>
/// <typeparam name="T">The inner type of the field</typeparam>
public abstract class EnumField<T> : Field<T>
{
/// <summary>
/// List of names of the enumerator entries.
/// </summary>
public GUIContent[] enumNames;
private int[] m_EnumValues;
/// <summary>
/// List of values of the enumerator entries.
/// </summary>
public int[] enumValues
{
get => m_EnumValues;
set
{
if (value?.Distinct().Count() != value?.Count())
Debug.LogWarning($"{displayName} - The values of the enum are duplicated, this might lead to a errors displaying the enum");
m_EnumValues = value;
}
}
// Space-delimit PascalCase (https://stackoverflow.com/questions/155303/net-how-can-you-split-a-caps-delimited-string-into-an-array)
static Regex s_NicifyRegEx = new("([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", RegexOptions.Compiled);
/// <summary>
/// Automatically fills the enum names with a given <see cref="Type"/>
/// </summary>
/// <param name="enumType">The enum type</param>
protected void AutoFillFromType(Type enumType)
{
if (enumType == null || !enumType.IsEnum)
throw new ArgumentException($"{nameof(enumType)} must not be null and it must be an Enum type");
using (ListPool<GUIContent>.Get(out var tmpNames))
using (ListPool<int>.Get(out var tmpValues))
{
var enumEntries = enumType.GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(fieldInfo => !fieldInfo.IsDefined(typeof(ObsoleteAttribute)) && !fieldInfo.IsDefined(typeof(HideInInspector)));
foreach (var fieldInfo in enumEntries)
{
var description = fieldInfo.GetCustomAttribute<InspectorNameAttribute>();
var displayName = new GUIContent(description == null ? s_NicifyRegEx.Replace(fieldInfo.Name, "$1 ") : description.displayName);
tmpNames.Add(displayName);
tmpValues.Add((int)Enum.Parse(enumType, fieldInfo.Name));
}
enumNames = tmpNames.ToArray();
enumValues = tmpValues.ToArray();
}
}
}
/// <summary>
/// Enumerator field.
/// </summary>
public class EnumField : EnumField<int>
{
internal int[] quickSeparators;
private int[] m_Indexes;
internal int[] indexes => m_Indexes ??= Enumerable.Range(0, enumNames?.Length ?? 0).ToArray();
/// <summary>
/// Get the enumeration value index.
/// </summary>
public Func<int> getIndex { get; set; }
/// <summary>
/// Set the enumeration value index.
/// </summary>
public Action<int> setIndex { get; set; }
/// <summary>
/// Current enumeration value index.
/// </summary>
public int currentIndex
{
get => getIndex();
set => setIndex(value);
}
/// <summary>
/// Generates enumerator values and names automatically based on the provided type.
/// </summary>
public Type autoEnum
{
set
{
AutoFillFromType(value);
InitQuickSeparators();
}
}
internal void InitQuickSeparators()
{
var enumNamesPrefix = enumNames.Select(x =>
{
string[] splitted = x.text.Split('/');
if (splitted.Length == 1)
return "";
else
return splitted[0];
});
quickSeparators = new int[enumNamesPrefix.Distinct().Count()];
string lastPrefix = null;
for (int i = 0, wholeNameIndex = 0; i < quickSeparators.Length; ++i)
{
var currentTestedPrefix = enumNamesPrefix.ElementAt(wholeNameIndex);
while (lastPrefix == currentTestedPrefix)
{
currentTestedPrefix = enumNamesPrefix.ElementAt(++wholeNameIndex);
}
lastPrefix = currentTestedPrefix;
quickSeparators[i] = wholeNameIndex++;
}
}
/// <summary>
/// Set the value of the field.
/// </summary>
/// <param name="value">Input value.</param>
public override void SetValue(int value)
{
Assert.IsNotNull(setter);
var validValue = ValidateValue(value);
// There might be cases that the value does not map the index, look for the correct index
var newCurrentIndex = Array.IndexOf(enumValues, validValue);
if (currentIndex != newCurrentIndex && !validValue.Equals(getter()))
{
setter(validValue);
onValueChanged?.Invoke(this, validValue);
if (newCurrentIndex > -1)
currentIndex = newCurrentIndex;
}
}
}
/// <summary>
/// Object PopupField
/// </summary>
public class ObjectPopupField : Field<Object>
{
/// <summary>
/// Callback to obtain the elemtents of the pop up
/// </summary>
public Func<IEnumerable<Object>> getObjects { get; set; }
}
/// <summary>
/// Enumerator field with history.
/// </summary>
public class HistoryEnumField : EnumField
{
/// <summary>
/// History getter for this field.
/// </summary>
public Func<int>[] historyIndexGetter { get; set; }
/// <summary>
/// Depth of the field's history.
/// </summary>
public int historyDepth => historyIndexGetter?.Length ?? 0;
/// <summary>
/// Get the value of the field at a certain history index.
/// </summary>
/// <param name="historyIndex">Index of the history to query.</param>
/// <returns>Value of the field at the provided history index.</returns>
public int GetHistoryValue(int historyIndex)
{
Assert.IsNotNull(historyIndexGetter);
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyIndexGetter.Length, "out of range historyIndex");
Assert.IsNotNull(historyIndexGetter[historyIndex]);
return historyIndexGetter[historyIndex]();
}
}
/// <summary>
/// Bitfield enumeration field.
/// </summary>
public class BitField : EnumField<Enum>
{
Type m_EnumType;
/// <summary>
/// Generates bitfield values and names automatically based on the provided type.
/// </summary>
public Type enumType
{
get => m_EnumType;
set
{
m_EnumType = value;
AutoFillFromType(value);
}
}
}
/// <summary>
/// Color field.
/// </summary>
public class ColorField : Field<Color>
{
/// <summary>
/// HDR color.
/// </summary>
public bool hdr = false;
/// <summary>
/// Show alpha of the color field.
/// </summary>
public bool showAlpha = true;
// Editor-only
/// <summary>
/// Show the color picker.
/// </summary>
public bool showPicker = true;
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public float incStep = 0.025f;
/// <summary>
/// Step increment multiplier.
/// </summary>
public float incStepMult = 5f;
/// <summary>
/// Number of decimals.
/// </summary>
public int decimals = 3;
/// <summary>
/// Function used to validate the value when updating the field.
/// </summary>
/// <param name="value">Input value.</param>
/// <returns>Validated value.</returns>
public override Color ValidateValue(Color value)
{
if (!hdr)
{
value.r = Mathf.Clamp01(value.r);
value.g = Mathf.Clamp01(value.g);
value.b = Mathf.Clamp01(value.b);
value.a = Mathf.Clamp01(value.a);
}
return value;
}
}
/// <summary>
/// Vector2 field.
/// </summary>
public class Vector2Field : Field<Vector2>
{
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public float incStep = 0.025f;
/// <summary>
/// Step increment multiplier.
/// </summary>
public float incStepMult = 10f;
/// <summary>
/// Number of decimals.
/// </summary>
public int decimals = 3;
}
/// <summary>
/// Vector3 field.
/// </summary>
public class Vector3Field : Field<Vector3>
{
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public float incStep = 0.025f;
/// <summary>
/// Step increment multiplier.
/// </summary>
public float incStepMult = 10f;
/// <summary>
/// Number of decimals.
/// </summary>
public int decimals = 3;
}
/// <summary>
/// Vector4 field.
/// </summary>
public class Vector4Field : Field<Vector4>
{
// Runtime-only
/// <summary>
/// Step increment.
/// </summary>
public float incStep = 0.025f;
/// <summary>
/// Step increment multiplier.
/// </summary>
public float incStepMult = 10f;
/// <summary>
/// Number of decimals.
/// </summary>
public int decimals = 3;
}
/// <summary>
/// Object field.
/// </summary>
public class ObjectField : Field<Object>
{
/// <summary>
/// Object type.
/// </summary>
public Type type = typeof(Object);
}
/// <summary>
/// Object list field.
/// </summary>
public class ObjectListField : Field<Object[]>
{
/// <summary>
/// Objects type.
/// </summary>
public Type type = typeof(Object);
}
/// <summary>
/// Simple message box widget, providing a couple of different styles.
/// </summary>
public class MessageBox : Widget
{
/// <summary>
/// Label style defines text color and background.
/// </summary>
public enum Style
{
/// <summary>
/// Info category
/// </summary>
Info,
/// <summary>
/// Warning category
/// </summary>
Warning,
/// <summary>
/// Error category
/// </summary>
Error
}
/// <summary>
/// Style used to render displayName.
/// </summary>
public Style style = Style.Info;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 332920c028664644e9e4e1bf5a1631d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using System;
namespace UnityEngine.Rendering
{
public partial class DebugUI
{
// Root panel class - we don't want to extend Container here because we need a clear
// separation between debug panels and actual widgets
/// <summary>
/// Root panel class.
/// </summary>
public class Panel : IContainer, IComparable<Panel>
{
/// <summary>
/// Widget flags for this panel.
/// </summary>
public Flags flags { get; set; }
/// <summary>
/// Display name of the panel.
/// </summary>
public string displayName { get; set; }
/// <summary>
/// Group index of the panel.
/// </summary>
public int groupIndex { get; set; }
/// <summary>
/// Path of the panel.
/// </summary>
public string queryPath { get { return displayName; } }
/// <summary>
/// Specify if the panel is editor only.
/// </summary>
public bool isEditorOnly { get { return (flags & Flags.EditorOnly) != 0; } }
/// <summary>
/// Specify if the panel is runtime only.
/// </summary>
public bool isRuntimeOnly { get { return (flags & Flags.RuntimeOnly) != 0; } }
/// <summary>
/// Returns true if the panel is inactive in the editor.
/// </summary>
public bool isInactiveInEditor { get { return (isRuntimeOnly && !Application.isPlaying); } }
/// <summary>
/// Returns true if the panel should always be updated.
/// </summary>
public bool editorForceUpdate { get { return (flags & Flags.EditorForceUpdate) != 0; } }
/// <summary>
/// List of children.
/// </summary>
public ObservableList<Widget> children { get; private set; }
/// <summary>
/// Callback used when the panel is set dirty.
/// </summary>
public event Action<Panel> onSetDirty = delegate { };
/// <summary>
/// Constructor.
/// </summary>
public Panel()
{
children = new ObservableList<Widget>();
children.ItemAdded += OnItemAdded;
children.ItemRemoved += OnItemRemoved;
}
/// <summary>
/// Callback used when a child is added.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of added children.</param>
protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
if (e.item != null)
{
e.item.panel = this;
e.item.parent = this;
}
SetDirty();
}
/// <summary>
/// Callback used when a child is removed.
/// </summary>
/// <param name="sender">Sender widget.</param>
/// <param name="e">List of removed children.</param>
protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
{
if (e.item != null)
{
e.item.panel = null;
e.item.parent = null;
}
SetDirty();
}
/// <summary>
/// Set the panel dirty.
/// </summary>
public void SetDirty()
{
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
children[i].GenerateQueryPath();
onSetDirty(this);
}
/// <summary>
/// Returns the hash code of the panel.
/// </summary>
/// <returns>Hash code of the panel.</returns>
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + displayName.GetHashCode();
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
hash = hash * 23 + children[i].GetHashCode();
return hash;
}
/// <summary>
/// Comparison function.
/// </summary>
/// <param name="other">Panel to compare to.</param>
/// <returns>True if the panels share the same group index.</returns>
int IComparable<Panel>.CompareTo(Panel other) => other == null ? 1 : groupIndex.CompareTo(other.groupIndex);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a33c6b643214f847b2e0cf1721a64db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,337 @@
using System;
using System.Linq;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
/// <summary>
/// Debug UI Class
/// </summary>
public partial class DebugUI
{
/// <summary>
/// Flags for Debug UI widgets.
/// </summary>
[Flags]
public enum Flags
{
/// <summary>
/// None.
/// </summary>
None = 0,
/// <summary>
/// This widget is Editor only.
/// </summary>
EditorOnly = 1 << 1,
/// <summary>
/// This widget is Runtime only.
/// </summary>
RuntimeOnly = 1 << 2,
/// <summary>
/// This widget will force the Debug Editor Window refresh.
/// </summary>
EditorForceUpdate = 1 << 3,
/// <summary>
/// This widget will appear in the section "Frequently Used"
/// </summary>
FrequentlyUsed = 1 << 4
}
/// <summary>
/// Base class for all debug UI widgets.
/// </summary>
public abstract class Widget
{
// Set to null until it's added to a panel, be careful
/// <summary>
/// Panels containing the widget.
/// </summary>
protected Panel m_Panel;
/// <summary>
/// Panels containing the widget.
/// </summary>
public virtual Panel panel
{
get { return m_Panel; }
internal set { m_Panel = value; }
}
/// <summary>
/// Parent container.
/// </summary>
protected IContainer m_Parent;
/// <summary>
/// Parent container.
/// </summary>
public virtual IContainer parent
{
get { return m_Parent; }
internal set { m_Parent = value; }
}
/// <summary>
/// Flags for the widget.
/// </summary>
public Flags flags { get; set; }
/// <summary>
/// Display name.
/// </summary>
public string displayName { get; set; }
/// <summary>
/// Tooltip.
/// </summary>
public string tooltip { get; set; }
/// <summary>
/// Path of the widget.
/// </summary>
public string queryPath { get; private set; }
/// <summary>
/// True if the widget is Editor only.
/// </summary>
public bool isEditorOnly => flags.HasFlag(Flags.EditorOnly);
/// <summary>
/// True if the widget is Runtime only.
/// </summary>
public bool isRuntimeOnly => flags.HasFlag(Flags.RuntimeOnly);
/// <summary>
/// True if the widget is inactive in the editor (i.e. widget is runtime only and the application is not 'Playing').
/// </summary>
public bool isInactiveInEditor => (isRuntimeOnly && !Application.isPlaying);
/// <summary>
/// Optional delegate that can be used to conditionally hide widgets at runtime (e.g. due to state of other widgets).
/// </summary>
public Func<bool> isHiddenCallback;
/// <summary>
/// If <see cref="isHiddenCallback">shouldHideDelegate</see> has been set and returns true, the widget is hidden from the UI.
/// </summary>
public bool isHidden => isHiddenCallback?.Invoke() ?? false;
internal virtual void GenerateQueryPath()
{
queryPath = displayName.Trim();
if (m_Parent != null)
queryPath = m_Parent.queryPath + " -> " + queryPath;
}
/// <summary>
/// Returns the hash code of the widget.
/// </summary>
/// <returns>The hash code of the widget.</returns>
public override int GetHashCode()
{
return queryPath.GetHashCode() ^ isHidden.GetHashCode();
}
/// <summary>
/// Helper struct to allow more compact initialization of widgets.
/// </summary>
public struct NameAndTooltip
{
/// <summary>
/// The name
/// </summary>
public string name;
/// <summary>
/// The tooltip
/// </summary>
public string tooltip;
}
/// <summary>
/// Helper setter to allow more compact initialization of widgets.
/// </summary>
public NameAndTooltip nameAndTooltip
{
set
{
displayName = value.name;
tooltip = value.tooltip;
}
}
}
/// <summary>
/// Interface for widgets that can contain other widgets.
/// </summary>
public interface IContainer
{
/// <summary>
/// List of children of the container.
/// </summary>
ObservableList<Widget> children { get; }
/// <summary>
/// Display name of the container.
/// </summary>
string displayName { get; set; }
/// <summary>
/// Path of the container.
/// </summary>
string queryPath { get; }
}
/// <summary>
/// Any widget that implements this will be considered for serialization (only if the setter is set and thus is not read-only)
/// </summary>
public interface IValueField
{
/// <summary>
/// Return the value of the field.
/// </summary>
/// <returns>Value of the field.</returns>
object GetValue();
/// <summary>
/// Set the value of the field.
/// </summary>
/// <param name="value">Input value.</param>
void SetValue(object value);
/// <summary>
/// Function used to validate the value when setting it.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
object ValidateValue(object value);
}
// Miscellaneous
/// <summary>
/// Button widget.
/// </summary>
public class Button : Widget
{
/// <summary>
/// Action performed by the button.
/// </summary>
public Action action { get; set; }
}
/// <summary>
/// Read only Value widget.
/// </summary>
public class Value : Widget
{
/// <summary>
/// Getter for the Value.
/// </summary>
public Func<object> getter { get; set; }
/// <summary>
/// Refresh rate for the read-only value (runtime only)
/// </summary>
public float refreshRate = 0.1f;
/// <summary>
/// Optional C# numeric format string, using following syntax: "{0[:numericFormatString]}"
/// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings
/// and https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting
/// Example: 123.45678 with formatString "{0:F2} ms" --> "123.45 ms".
/// </summary>
public string formatString = null;
/// <summary>
/// Constructor.
/// </summary>
public Value()
{
displayName = "";
}
/// <summary>
/// Returns the value of the widget.
/// </summary>
/// <returns>The value of the widget.</returns>
public virtual object GetValue()
{
Assert.IsNotNull(getter);
return getter();
}
/// <summary>
/// Returns the formatted value string for display purposes.
/// </summary>
/// <param name="value">Value to be formatted.</param>
/// <returns>The formatted value string.</returns>
public virtual string FormatString(object value)
{
return string.IsNullOrEmpty(formatString) ? $"{value}" : string.Format(formatString, value);
}
}
/// <summary>
/// Progress bar value.
/// </summary>
public class ProgressBarValue : Value
{
/// <summary>
/// Minimum value.
/// </summary>
public float min = 0f;
/// <summary>
/// Maximum value.
/// </summary>
public float max = 1f;
/// <summary>
/// Get the current progress string, remapped to [0, 1] range, representing the progress between min and max.
/// </summary>
/// <param name="value">Value to be formatted.</param>
/// <returns>Formatted progress percentage string between 0% and 100%.</returns>
public override string FormatString(object value)
{
static float Remap01(float v, float x0, float y0) => (v - x0) / (y0 - x0);
float clamped = Mathf.Clamp((float)value, min, max);
float percentage = Remap01(clamped, min, max);
return $"{percentage:P1}";
}
}
/// <summary>
/// Tuple of Value widgets for creating tabular UI.
/// </summary>
public class ValueTuple : Widget
{
/// <summary>
/// Number of elements in the tuple.
/// </summary>
public int numElements
{
get
{
Assert.IsTrue(values.Length > 0);
return values.Length;
}
}
/// <summary>
/// Value widgets.
/// </summary>
public Value[] values;
/// <summary>
/// Refresh rate for the read-only values (runtime only)
/// </summary>
public float refreshRate => values.FirstOrDefault()?.refreshRate ?? 0.1f;
/// <summary>
/// The currently pinned element index, or -1 if none are pinned.
/// </summary>
public int pinnedElementIndex = -1;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4cce618ad8a6d934dbdc631f979b3aa6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,213 @@
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
#define USE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
using UnityEngine.InputSystem.EnhancedTouch;
#endif
using System;
using System.Collections;
using System.Diagnostics;
using UnityEngine.EventSystems;
namespace UnityEngine.Rendering
{
class DebugUpdater : MonoBehaviour
{
static DebugUpdater s_Instance = null;
ScreenOrientation m_Orientation;
bool m_RuntimeUiWasVisibleLastFrame = false;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void RuntimeInit()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (DebugManager.instance.enableRuntimeUI)
EnableRuntime();
#endif
}
internal static void SetEnabled(bool enabled)
{
if (enabled)
EnableRuntime();
else
DisableRuntime();
}
static void EnableRuntime()
{
if (s_Instance != null)
return;
var go = new GameObject { name = "[Debug Updater]" };
s_Instance = go.AddComponent<DebugUpdater>();
s_Instance.m_Orientation = Screen.orientation;
DontDestroyOnLoad(go);
DebugManager.instance.EnableInputActions();
#if USE_INPUT_SYSTEM
EnhancedTouchSupport.Enable();
#endif
}
static void DisableRuntime()
{
DebugManager debugManager = DebugManager.instance;
debugManager.displayRuntimeUI = false;
debugManager.displayPersistentRuntimeUI = false;
if (s_Instance != null)
{
CoreUtils.Destroy(s_Instance.gameObject);
s_Instance = null;
}
}
internal static void HandleInternalEventSystemComponents(bool uiEnabled)
{
if (s_Instance == null)
return;
if (uiEnabled)
s_Instance.EnsureExactlyOneEventSystem();
else
s_Instance.DestroyDebugEventSystem();
}
void EnsureExactlyOneEventSystem()
{
var eventSystems = FindObjectsOfType<EventSystem>();
var debugEventSystem = GetComponent<EventSystem>();
if (eventSystems.Length > 1 && debugEventSystem != null)
{
Debug.Log($"More than one EventSystem detected in scene. Destroying EventSystem owned by DebugUpdater.");
DestroyDebugEventSystem();
}
else if (eventSystems.Length == 0)
{
Debug.Log($"No EventSystem available. Creating a new EventSystem to enable Rendering Debugger runtime UI.");
CreateDebugEventSystem();
}
else
{
StartCoroutine(DoAfterInputModuleUpdated(CheckInputModuleExists));
}
}
IEnumerator DoAfterInputModuleUpdated(Action action)
{
// EventSystem.current.currentInputModule is not updated immediately when EventSystem.current changes. It happens
// with a delay in EventSystem.Update(), so wait a couple of frames to ensure that has happened.
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
action.Invoke();
}
void CheckInputModuleExists()
{
if (EventSystem.current != null && EventSystem.current.currentInputModule == null)
{
Debug.LogWarning("Found a game object with EventSystem component but no corresponding BaseInputModule component - Debug UI input might not work correctly.");
}
}
#if USE_INPUT_SYSTEM
void AssignDefaultActions()
{
if (EventSystem.current != null && EventSystem.current.currentInputModule is InputSystemUIInputModule inputSystemModule)
{
// FIXME: In order to activate default input actions in player builds (required for touch input to work),
// we need to call InputSystemUIInputModule.AssignDefaultActions() which was added in com.unity.inputsystem@1.1.0-pre.5.
// However, there is a problem in InputSystem package version ordering, where it sorts this version as an
// older version than it should be. Hence we cannot write a version define to conditionally compile this function call.
// Instead, we use reflection to see if the function is there and can be invoked.
//
// Once com.unity.inputsystem@1.1.0 is available, create an INPUTSYSTEM_1_1_0_OR_GREATER version define and use it
// to conditionally call AssignDefaultActions().
System.Reflection.MethodInfo assignDefaultActionsMethod = inputSystemModule.GetType().GetMethod("AssignDefaultActions");
if (assignDefaultActionsMethod != null)
{
assignDefaultActionsMethod.Invoke(inputSystemModule, null);
}
}
CheckInputModuleExists();
}
#endif
void CreateDebugEventSystem()
{
gameObject.AddComponent<EventSystem>();
#if USE_INPUT_SYSTEM
gameObject.AddComponent<InputSystemUIInputModule>();
StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions));
#else
gameObject.AddComponent<StandaloneInputModule>();
#endif
}
void DestroyDebugEventSystem()
{
var eventSystem = GetComponent<EventSystem>();
#if USE_INPUT_SYSTEM
var inputModule = GetComponent<InputSystemUIInputModule>();
if (inputModule)
{
CoreUtils.Destroy(inputModule);
StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions));
}
#else
CoreUtils.Destroy(GetComponent<StandaloneInputModule>());
CoreUtils.Destroy(GetComponent<BaseInput>());
#endif
CoreUtils.Destroy(eventSystem);
}
void Update()
{
DebugManager debugManager = DebugManager.instance;
// Runtime UI visibility can change i.e. due to scene unload - allow component cleanup in this case.
if (m_RuntimeUiWasVisibleLastFrame != debugManager.displayRuntimeUI)
{
HandleInternalEventSystemComponents(debugManager.displayRuntimeUI);
}
debugManager.UpdateActions();
if (debugManager.GetAction(DebugAction.EnableDebugMenu) != 0.0f ||
debugManager.GetActionToggleDebugMenuWithTouch())
{
debugManager.displayRuntimeUI = !debugManager.displayRuntimeUI;
}
if (debugManager.displayRuntimeUI)
{
if (debugManager.GetAction(DebugAction.ResetAll) != 0.0f)
debugManager.Reset();
if (debugManager.GetActionReleaseScrollTarget())
debugManager.SetScrollTarget(null); // Allow mouse wheel scroll without causing auto-scroll
}
if (m_Orientation != Screen.orientation)
{
StartCoroutine(RefreshRuntimeUINextFrame());
m_Orientation = Screen.orientation;
}
m_RuntimeUiWasVisibleLastFrame = debugManager.displayRuntimeUI;
}
static IEnumerator RefreshRuntimeUINextFrame()
{
yield return null; // Defer runtime UI refresh to next frame to allow canvas to update first.
DebugManager.instance.ReDrawOnScreenDebug();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fa749f6d8ed4b441af9aa005fe0f895
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f240cc177d08124a9694021c19c0dc4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// Represents a system bottleneck, meaning the factor that is most dominant in determining
/// the total frame time.
/// </summary>
internal enum PerformanceBottleneck
{
Indeterminate, // Cannot be determined
PresentLimited, // Limited by presentation (vsync or framerate cap)
CPU, // Limited by CPU (main and/or render thread)
GPU, // Limited by GPU
Balanced, // Limited by both CPU and GPU, i.e. well balanced
}
/// <summary>
/// BottleneckHistogram represents the distribution of bottlenecks over the Bottleneck History Window,
/// the size of which is determined by <see cref="DebugFrameTiming.bottleneckHistorySize"/>.
/// </summary>
internal struct BottleneckHistogram
{
internal float PresentLimited;
internal float CPU;
internal float GPU;
internal float Balanced;
};
/// <summary>
/// Container class for bottleneck history with helper to calculate histogram.
/// </summary>
internal class BottleneckHistory
{
public BottleneckHistory(int initialCapacity)
{
m_Bottlenecks.Capacity = initialCapacity;
}
List<PerformanceBottleneck> m_Bottlenecks = new();
internal BottleneckHistogram Histogram;
internal void DiscardOldSamples(int historySize)
{
Debug.Assert(historySize > 0, "Invalid sampleHistorySize");
while (m_Bottlenecks.Count >= historySize)
m_Bottlenecks.RemoveAt(0);
m_Bottlenecks.Capacity = historySize;
}
internal void AddBottleneckFromAveragedSample(FrameTimeSample frameHistorySampleAverage)
{
var bottleneck = DetermineBottleneck(frameHistorySampleAverage);
m_Bottlenecks.Add(bottleneck);
}
internal void ComputeHistogram()
{
var stats = new BottleneckHistogram();
for (int i = 0; i < m_Bottlenecks.Count; i++)
{
switch (m_Bottlenecks[i])
{
case PerformanceBottleneck.Balanced:
stats.Balanced++;
break;
case PerformanceBottleneck.CPU:
stats.CPU++;
break;
case PerformanceBottleneck.GPU:
stats.GPU++;
break;
case PerformanceBottleneck.PresentLimited:
stats.PresentLimited++;
break;
}
}
stats.Balanced /= m_Bottlenecks.Count;
stats.CPU /= m_Bottlenecks.Count;
stats.GPU /= m_Bottlenecks.Count;
stats.PresentLimited /= m_Bottlenecks.Count;
Histogram = stats;
}
static PerformanceBottleneck DetermineBottleneck(FrameTimeSample s)
{
const float kNearFullFrameTimeThresholdPercent = 0.2f;
const float kNonZeroPresentWaitTimeMs = 0.5f;
if (s.GPUFrameTime == 0 || s.MainThreadCPUFrameTime == 0) // In direct mode, render thread doesn't exist
return PerformanceBottleneck.Indeterminate; // Missing data
float fullFrameTimeWithMargin = (1f - kNearFullFrameTimeThresholdPercent) * s.FullFrameTime;
// GPU time is close to frame time, CPU times are not
if (s.GPUFrameTime > fullFrameTimeWithMargin &&
s.MainThreadCPUFrameTime < fullFrameTimeWithMargin &&
s.RenderThreadCPUFrameTime < fullFrameTimeWithMargin)
return PerformanceBottleneck.GPU;
// One of the CPU times is close to frame time, GPU is not
if (s.GPUFrameTime < fullFrameTimeWithMargin &&
(s.MainThreadCPUFrameTime > fullFrameTimeWithMargin ||
s.RenderThreadCPUFrameTime > fullFrameTimeWithMargin))
return PerformanceBottleneck.CPU;
// Main thread waited due to Vsync or target frame rate
if (s.MainThreadCPUPresentWaitTime > kNonZeroPresentWaitTimeMs)
{
// None of the times are close to frame time
if (s.GPUFrameTime < fullFrameTimeWithMargin &&
s.MainThreadCPUFrameTime < fullFrameTimeWithMargin &&
s.RenderThreadCPUFrameTime < fullFrameTimeWithMargin)
return PerformanceBottleneck.PresentLimited;
}
return PerformanceBottleneck.Balanced;
}
internal void Clear()
{
m_Bottlenecks.Clear();
Histogram = new BottleneckHistogram();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 331aa10d229f4c2fb9a9240812fb45d6
timeCreated: 1629185832

View File

@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// Represents timing data captured from a single frame.
/// </summary>
internal struct FrameTimeSample
{
internal float FramesPerSecond;
internal float FullFrameTime;
internal float MainThreadCPUFrameTime;
internal float MainThreadCPUPresentWaitTime;
internal float RenderThreadCPUFrameTime;
internal float GPUFrameTime;
internal FrameTimeSample(float initValue)
{
FramesPerSecond = initValue;
FullFrameTime = initValue;
MainThreadCPUFrameTime = initValue;
MainThreadCPUPresentWaitTime = initValue;
RenderThreadCPUFrameTime = initValue;
GPUFrameTime = initValue;
}
};
/// <summary>
/// Container class for sample history with helpers to calculate min, max and average in one pass.
/// </summary>
class FrameTimeSampleHistory
{
public FrameTimeSampleHistory(int initialCapacity)
{
m_Samples.Capacity = initialCapacity;
}
List<FrameTimeSample> m_Samples = new();
internal FrameTimeSample SampleAverage;
internal FrameTimeSample SampleMin;
internal FrameTimeSample SampleMax;
internal void Add(FrameTimeSample sample)
{
m_Samples.Add(sample);
}
// Helper functions
static Func<float, float, float> s_SampleValueAdd = (float value, float other) =>
{
return value + other;
};
static Func<float, float, float> s_SampleValueMin = (float value, float other) =>
{
return other > 0 ? Mathf.Min(value, other) : value;
};
static Func<float, float, float> s_SampleValueMax = (float value, float other) =>
{
return Mathf.Max(value, other);
};
static Func<float, float, float> s_SampleValueCountValid = (float value, float other) =>
{
return other > 0 ? value + 1 : value;
};
static Func<float, float, float> s_SampleValueEnsureValid = (float value, float other) =>
{
return other > 0 ? value : 0;
};
static Func<float, float, float> s_SampleValueDivide = (float value, float other) =>
{
return other > 0 ? value / other : 0;
};
internal void ComputeAggregateValues()
{
void ForEachSampleMember(ref FrameTimeSample aggregate, FrameTimeSample sample, Func<float, float, float> func)
{
aggregate.FramesPerSecond = func(aggregate.FramesPerSecond, sample.FramesPerSecond);
aggregate.FullFrameTime = func(aggregate.FullFrameTime, sample.FullFrameTime);
aggregate.MainThreadCPUFrameTime = func(aggregate.MainThreadCPUFrameTime, sample.MainThreadCPUFrameTime);
aggregate.MainThreadCPUPresentWaitTime = func(aggregate.MainThreadCPUPresentWaitTime, sample.MainThreadCPUPresentWaitTime);
aggregate.RenderThreadCPUFrameTime = func(aggregate.RenderThreadCPUFrameTime, sample.RenderThreadCPUFrameTime);
aggregate.GPUFrameTime = func(aggregate.GPUFrameTime, sample.GPUFrameTime);
};
FrameTimeSample average = new();
FrameTimeSample min = new(float.MaxValue);
FrameTimeSample max = new(float.MinValue);
FrameTimeSample numValidSamples = new(); // Using the struct to record how many valid samples each field has
for (int i = 0; i < m_Samples.Count; i++)
{
var s = m_Samples[i];
ForEachSampleMember(ref min, s, s_SampleValueMin);
ForEachSampleMember(ref max, s, s_SampleValueMax);
ForEachSampleMember(ref average, s, s_SampleValueAdd);
ForEachSampleMember(ref numValidSamples, s, s_SampleValueCountValid);
}
ForEachSampleMember(ref min, numValidSamples, s_SampleValueEnsureValid);
ForEachSampleMember(ref max, numValidSamples, s_SampleValueEnsureValid);
ForEachSampleMember(ref average, numValidSamples, s_SampleValueDivide);
SampleAverage = average;
SampleMin = min;
SampleMax = max;
}
internal void DiscardOldSamples(int sampleHistorySize)
{
Debug.Assert(sampleHistorySize > 0, "Invalid sampleHistorySize");
while (m_Samples.Count >= sampleHistorySize)
m_Samples.RemoveAt(0);
m_Samples.Capacity = sampleHistorySize;
}
internal void Clear()
{
m_Samples.Clear();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d7cc2267acce47fdae2ece838f34df48
timeCreated: 1629185717

View File

@@ -0,0 +1,22 @@
using System;
using UnityEngine;
namespace UnityEngine.Rendering
{
/// <summary>
/// Interface for storing the debug settings
/// </summary>
public interface IDebugDisplaySettings : IDebugDisplaySettingsQuery
{
/// <summary>
/// Reset the stored debug settings
/// </summary>
void Reset();
/// <summary>
/// Executes an action for each element
/// </summary>
/// <param name="onExecute"></param>
void ForEach(Action<IDebugDisplaySettingsData> onExecute);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3be1aff9418ca6840a765320b0c2a2db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
namespace UnityEngine.Rendering
{
/// <summary>
/// Debug UI panel interface
/// </summary>
public interface IDebugDisplaySettingsData : IDebugDisplaySettingsQuery
{
/// <summary>
/// Creates the debug UI panel needed for these debug settings.
/// </summary>
/// <returns>The debug UI panel created.</returns>
IDebugDisplaySettingsPanelDisposable CreatePanel();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78ae6e132563244b58b0aeaf4f96453e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using System;
namespace UnityEngine.Rendering
{
/// <summary>
/// Debug UI panel
/// </summary>
public interface IDebugDisplaySettingsPanel
{
/// <summary>
/// The name used when displaying this panel.
/// </summary>
string PanelName { get; }
/// <summary>
/// Widgets used by this panel.
/// </summary>
DebugUI.Widget[] Widgets { get; }
/// <summary>
/// Flags to be applied to the top-level panel.
/// </summary>
DebugUI.Flags Flags { get; }
}
/// <summary>
/// Debug UI panel disposable
/// </summary>
public interface IDebugDisplaySettingsPanelDisposable : IDebugDisplaySettingsPanel, IDisposable
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2dd686478607420fa4600df5d27143f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using UnityEngine;
namespace UnityEngine.Rendering
{
/// <summary>
/// Interface for determining what kind of debug settings are currently active.
/// </summary>
public interface IDebugDisplaySettingsQuery
{
/// <summary>
/// Checks whether ANY of the debug settings are currently active.
/// </summary>
bool AreAnySettingsActive { get; }
/// <summary>
/// Checks whether the current state of these settings allows post-processing.
/// </summary>
bool IsPostProcessingAllowed { get; }
/// <summary>
/// Checks whether lighting is active for these settings.
/// </summary>
bool IsLightingActive { get; }
/// <summary>
/// Attempts to get the color used to clear the screen for this debug setting.
/// </summary>
/// <param name="color">A reference to the screen clear color to use.</param>
/// <returns>"true" if we updated the color, "false" if we didn't change anything.</returns>
bool TryGetScreenClearColor(ref Color color);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52acb35497424fdcb9cc2535113a9c15
timeCreated: 1613054809

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// Volume debug settings.
/// This variant is obsolete and kept only for not breaking user code. Use <see cref="IVolumeDebugSettings2"/> for all new usage.
/// </summary>
[Obsolete("This variant is obsolete and kept only for not breaking user code. Use IVolumeDebugSettings2 for all new usage.", false)]
public interface IVolumeDebugSettings
{
/// <summary>Selected component.</summary>
int selectedComponent { get; set; }
/// <summary>Current camera to debug.</summary>
Camera selectedCamera { get; }
/// <summary>Returns the collection of registered cameras.</summary>
IEnumerable<Camera> cameras { get; }
/// <summary>Selected camera index.</summary>
int selectedCameraIndex { get; set; }
/// <summary>Selected camera volume stack.</summary>
VolumeStack selectedCameraVolumeStack { get; }
/// <summary>Selected camera volume layer mask.</summary>
LayerMask selectedCameraLayerMask { get; }
/// <summary>Selected camera volume position.</summary>
Vector3 selectedCameraPosition { get; }
/// <summary>Type of the current component to debug.</summary>
Type selectedComponentType { get; set; }
/// <summary>
/// Obtains the Volumes
/// </summary>
/// <returns>The list of <see cref="Volume"/></returns>
Volume[] GetVolumes();
/// <summary>
/// Return if the <see cref="Volume"/> has influence
/// </summary>
/// <param name="volume"><see cref="Volume"/> to check the influence</param>
/// <returns>If the volume has influence</returns>
bool VolumeHasInfluence(Volume volume);
/// <summary>
/// Refreshes the volumes, fetches the stored volumes on the panel
/// </summary>
/// <param name="newVolumes">The list of <see cref="Volume"/> to refresh</param>
/// <returns>If the volumes have been refreshed</returns>
bool RefreshVolumes(Volume[] newVolumes);
/// <summary>
/// Obtains the volume weight
/// </summary>
/// <param name="volume"><see cref="Volume"/></param>
/// <returns>The weight of the volume</returns>
float GetVolumeWeight(Volume volume);
}
/// <summary>
/// Volume debug settings.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public interface IVolumeDebugSettings2 : IVolumeDebugSettings
#pragma warning restore CS0618 // Type or member is obsolete
{
/// <summary>
/// Specifies the render pipelie
/// </summary>
Type targetRenderPipeline { get; }
/// <summary>List of Volume component types and their path</summary>
List<(string, Type)> volumeComponentsPathAndType { get; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9771abc6c607d7244b4ddbe608ab86fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,205 @@
#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
#define USE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
#endif
using UnityEditor;
namespace UnityEngine.Rendering
{
/// <summary>
/// Provides mouse position for debugging purpose.
/// </summary>
public class MousePositionDebug
{
// Singleton
private static MousePositionDebug s_Instance = null;
/// <summary>
/// Singleton instance.
/// </summary>
static public MousePositionDebug instance
{
get
{
if (s_Instance == null)
{
s_Instance = new MousePositionDebug();
}
return s_Instance;
}
}
#if UNITY_EDITOR
[ExecuteAlways]
class GameViewEventCatcher : MonoBehaviour
{
public static GameViewEventCatcher s_Instance = null;
public static void Cleanup()
{
if (s_Instance != null)
{
// Either we call DestroyImmediate or Destroy we get an error :(
// GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
//DestroyImmediate(s_Instance.gameObject);
//Destroy(s_Instance.gameObject);
}
}
public static void Build()
{
Cleanup();
var go = new GameObject("__GameViewEventCatcher");
go.hideFlags = HideFlags.HideAndDontSave;
s_Instance = go.AddComponent<GameViewEventCatcher>();
}
void Update()
{
Vector2 mousePosition;
bool rightClickPressed = false;
bool endKeyPressed = false;
#if USE_INPUT_SYSTEM
mousePosition = Pointer.current != null ? Pointer.current.position.ReadValue() : new Vector2(-1, -1);
if (Mouse.current != null)
rightClickPressed = Mouse.current.rightButton.isPressed;
if (Keyboard.current != null)
endKeyPressed = Keyboard.current.endKey.isPressed;
#else
mousePosition = Input.mousePosition;
rightClickPressed = Input.GetMouseButton(1);
endKeyPressed = Input.GetKey(KeyCode.End);
#endif
if (mousePosition.x < 0
|| mousePosition.y < 0
|| mousePosition.x > Screen.width
|| mousePosition.y > Screen.height)
return;
instance.m_mousePosition = mousePosition;
instance.m_mousePosition.y = Screen.height - instance.m_mousePosition.y;
if (rightClickPressed)
instance.m_MouseClickPosition = instance.m_mousePosition;
if (endKeyPressed)
instance.m_MouseClickPosition = instance.m_mousePosition;
}
}
private Vector2 m_mousePosition = Vector2.zero;
Vector2 m_MouseClickPosition = Vector2.zero;
private void OnSceneGUI(UnityEditor.SceneView sceneview)
{
m_mousePosition = Event.current.mousePosition;
switch (Event.current.type)
{
case EventType.MouseDown:
m_MouseClickPosition = m_mousePosition;
break;
case EventType.KeyDown:
switch (Event.current.keyCode)
{
case KeyCode.End:
// Usefull we you don't want to change the scene viewport but still update the mouse click position
m_MouseClickPosition = m_mousePosition;
sceneview.Repaint();
break;
}
break;
}
}
#endif
/// <summary>
/// Initialize the MousePositionDebug class.
/// </summary>
public void Build()
{
#if UNITY_EDITOR
UnityEditor.SceneView.duringSceneGui -= OnSceneGUI;
UnityEditor.SceneView.duringSceneGui += OnSceneGUI;
// Disabled as it cause error: GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
//GameViewEventCatcher.Build();
#endif
}
/// <summary>
/// Cleanup the MousePositionDebug class.
/// </summary>
public void Cleanup()
{
#if UNITY_EDITOR
UnityEditor.SceneView.duringSceneGui -= OnSceneGUI;
// Disabled as it cause error: GameViewEventCatcher is only use for SSR debugging currently so comment this code and uncomment it if you want to debug SSR
//GameViewEventCatcher.Cleanup();
#endif
}
/// <summary>
/// Get the mouse position in the scene or game view.
/// </summary>
/// <param name="ScreenHeight">Window height.</param>
/// <param name="sceneView">Get position in the scene view?</param>
/// <returns>Coordinates of the mouse in the specified window.</returns>
public Vector2 GetMousePosition(float ScreenHeight, bool sceneView)
{
#if UNITY_EDITOR
if (sceneView)
{
// In play mode, m_mousePosition the one in the scene view
Vector2 mousePixelCoord = EditorGUIUtility.PointsToPixels(m_mousePosition);
mousePixelCoord.y = (ScreenHeight - 1.0f) - mousePixelCoord.y;
return mousePixelCoord;
}
else
{
// In play mode, Input.mousecoords matches the position in the game view
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
return GetInputMousePosition();
}
else
{
// In non-play mode, only m_mousePosition is valid.
// We force -1, -1 as a game view pixel pos to avoid
// rendering un-wanted effects
return new Vector2(-1.0f, -1.0f);
}
}
#else
// In app mode, we only use the Input.mousecoords
return GetInputMousePosition();
#endif
}
Vector2 GetInputMousePosition()
{
#if USE_INPUT_SYSTEM
return Pointer.current != null ? Pointer.current.position.ReadValue() : new Vector2(-1, -1);
#else
return Input.mousePosition;
#endif
}
/// <summary>
/// Returns the position of the mouse click.
/// </summary>
/// <param name="ScreenHeight">Window height.</param>
/// <returns>The coordinates of the mouse click.</returns>
public Vector2 GetMouseClickPosition(float ScreenHeight)
{
#if UNITY_EDITOR
Vector2 mousePixelCoord = EditorGUIUtility.PointsToPixels(m_MouseClickPosition);
mousePixelCoord.y = (ScreenHeight - 1.0f) - mousePixelCoord.y;
return mousePixelCoord;
#else
return Vector2.zero;
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16870bb28557a8f4182d1c8191e951a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2160913896effa4aa65b93012ff3465
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a11be56b627f5034bbca389c76bd3964
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 74a5091d8707f334b9a5c31ef71a64ba
TrueTypeFontImporter:
externalObjects: {}
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontName: Perfect DOS VGA 437
fontNames:
- Perfect DOS VGA 437
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 2
ascentCalculationMode: 1
useLegacyBoundsCalculation: 0
shouldRoundAdvanceValue: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3774a3e29f7c59445ba79c15769126fd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,196 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1153602445894428
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 224711363741255626}
- component: {fileID: 223912878945851142}
- component: {fileID: 114908889885781782}
- component: {fileID: 114649910605725082}
- component: {fileID: 114530362809716058}
m_Layer: 5
m_Name: DebugUICanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &224711363741255626
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1153602445894428}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!223 &223912878945851142
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1153602445894428}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 1
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 32767
m_TargetDisplay: 0
--- !u!114 &114908889885781782
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1153602445894428}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!114 &114649910605725082
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1153602445894428}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &114530362809716058
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1153602445894428}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76db615e524a19c4990482d75a475543, type: 3}
m_Name:
m_EditorClassIdentifier:
panelPrefab: {fileID: 224481716535368988, guid: daa46a58178a6ad41ae1ddc2dc7f856d, type: 3}
prefabs:
- type: UnityEngine.Rendering.DebugUI+Value, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: dc0f88987826e6e48b1fe9c7c2b53a53, type: 3}
- type: UnityEngine.Rendering.DebugUI+BoolField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224131888606727344, guid: ce347ad101f41ee4ab5c3fbc0ea447db, type: 3}
- type: UnityEngine.Rendering.DebugUI+IntField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: ae00bb75e0cd5b04b8fe7fb4ab662629, type: 3}
- type: UnityEngine.Rendering.DebugUI+UIntField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: f22bcc84a5f4a1944b075a2c4ac71493, type: 3}
- type: UnityEngine.Rendering.DebugUI+FloatField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: d8c744701b43c864b88e7f8144e19bc5, type: 3}
- type: UnityEngine.Rendering.DebugUI+EnumField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224224135738715566, guid: 988db55689193434fb0b3b89538f978f, type: 3}
- type: UnityEngine.Rendering.DebugUI+Button, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224438017010656346, guid: f6ce33b91f6ffe54cadacbf4bb112440, type: 3}
- type: UnityEngine.Rendering.DebugUI+Foldout, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224053494956566916, guid: 1c87ab2ce8b8b304d98fbe9a734b1f74, type: 3}
- type: UnityEngine.Rendering.DebugUI+ColorField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224636372931965878, guid: 77c185820dd1a464eac89cae3abccddf, type: 3}
- type: UnityEngine.Rendering.DebugUI+Vector2Field, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224169904409585018, guid: 326f7c58aed965d41bf7805a782d1e44, type: 3}
- type: UnityEngine.Rendering.DebugUI+Vector3Field, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224119945032119512, guid: 94afea5f242d72547979595ba963f335, type: 3}
- type: UnityEngine.Rendering.DebugUI+Vector4Field, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224325631027038092, guid: d47f009476100f545971a81ede14c750, type: 3}
- type: UnityEngine.Rendering.DebugUI+VBox, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224489511352681190, guid: ca3e294656861a64b8aeeb9f916da0a9, type: 3}
- type: UnityEngine.Rendering.DebugUI+HBox, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224719784157228276, guid: f7f5e36797cf0c1408561665c67b179b, type: 3}
- type: UnityEngine.Rendering.DebugUI+Container, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224284813447651300, guid: 38a07789c9e87004dad98c2909f58369, type: 3}
- type: UnityEngine.Rendering.DebugUI+BitField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 5833802642077810669, guid: 7c78b588b2e1f7c4a86ca4a985cf6e4a, type: 3}
- type: UnityEngine.Rendering.DebugUI+HistoryBoolField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 108402283379224504, guid: 5088d0220f0c4df439cf06c5c270eacb, type: 3}
- type: UnityEngine.Rendering.DebugUI+HistoryEnumField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 8535926254376877601, guid: b2da6b27df236b144b3516ed8e7d36ac, type: 3}
- type: UnityEngine.Rendering.DebugUI+Table, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224284813447651300, guid: 38a07789c9e87004dad98c2909f58369, type: 3}
- type: UnityEngine.Rendering.DebugUI+Table+Row, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224053494956566916, guid: 2d019437ff89b8d44949727731cd9357, type: 3}
- type: UnityEngine.Rendering.DebugUI+MessageBox, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224053494956566916, guid: 10a25524b0986f9488b430e2829bbbe8, type: 3}
- type: UnityEngine.Rendering.DebugUI+ProgressBarValue, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: d3770aaa3bbd8384aabab9ddd383e21e, type: 3}
- type: UnityEngine.Rendering.DebugUI+ValueTuple, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: a2148203dd960814ca5db0c293ceda35, type: 3}
- type: UnityEngine.Rendering.DebugUI+ObjectField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224720214277421396, guid: a87cfaef648f17c41b568e842e068c51, type: 3}
- type: UnityEngine.Rendering.DebugUI+ObjectListField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 224224135738715566, guid: ad83bc56407925d44a77a5bd01cd6783, type: 3}
- type: UnityEngine.Rendering.DebugUI+ObjectPopupField, Unity.RenderPipelines.Core.Runtime,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
prefab: {fileID: 4224455051203994714, guid: 48fb043c850cadc4a883b53785e14c6e, type: 3}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cf6cbdd672089a84796e55a21fed1cbe
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 100100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,237 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1388241697787146
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 224499400523491650}
- component: {fileID: 222203031975944290}
- component: {fileID: 114530022413994304}
- component: {fileID: 114399612179518328}
- component: {fileID: 114307594989265542}
m_Layer: 5
m_Name: DebugUI Persistent Panel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &224499400523491650
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1388241697787146}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 224556897823040040}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -5, y: -5}
m_SizeDelta: {x: 400, y: 0}
m_Pivot: {x: 1, y: 1}
--- !u!222 &222203031975944290
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1388241697787146}
m_CullTransparentMesh: 0
--- !u!114 &114530022413994304
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1388241697787146}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.1, g: 0.1, b: 0.1, a: 0.8509804}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 127279d577f25ac4ea17dae3782e5074, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &114399612179518328
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1388241697787146}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 5
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 0
m_Spacing: 0
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 1
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!114 &114307594989265542
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1388241697787146}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 0
m_VerticalFit: 1
--- !u!1 &1822588063230394
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 224556897823040040}
- component: {fileID: 223125086719629416}
- component: {fileID: 114876729554496680}
- component: {fileID: 114213191034542798}
- component: {fileID: 114605181728370468}
m_Layer: 5
m_Name: DebugUIPersistentCanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &224556897823040040
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1822588063230394}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 224499400523491650}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!223 &223125086719629416
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1822588063230394}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 1
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 32766
m_TargetDisplay: 0
--- !u!114 &114876729554496680
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1822588063230394}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!114 &114213191034542798
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1822588063230394}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &114605181728370468
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1822588063230394}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 87177621604658d4e893f55be7cfae4a, type: 3}
m_Name:
m_EditorClassIdentifier:
panel: {fileID: 224499400523491650}
valuePrefab: {fileID: 224720214277421396, guid: 8778e6b26b51c6a4999b94d7cacd8b5d, type: 3}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f6b1a0fe75d5009449cf55ae76220e2b
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 100100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9383aa19796d6e84e8031655b9ac912f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,174 @@
using System.Collections.Generic;
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for Bitfield widget. Require the enum to have a None field set to 0 in its values.
/// </summary>
public class DebugUIHandlerBitField : DebugUIHandlerWidget
{
/// <summary>Name of the widget.</summary>
public Text nameLabel;
/// <summary>Value toggle.</summary>
public UIFoldout valueToggle;
/// <summary>Toggles for the bitfield.</summary>
public List<DebugUIHandlerIndirectToggle> toggles;
DebugUI.BitField m_Field;
DebugUIHandlerContainer m_Container;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.BitField>();
m_Container = GetComponent<DebugUIHandlerContainer>();
nameLabel.text = m_Field.displayName;
int toggleIndex = 0;
foreach (var enumName in m_Field.enumNames)
{
if (toggleIndex >= toggles.Count)
continue;
var toggle = toggles[toggleIndex];
toggle.getter = GetValue;
toggle.setter = SetValue;
toggle.nextUIHandler = toggleIndex < (m_Field.enumNames.Length - 1) ? toggles[toggleIndex + 1] : null;
toggle.previousUIHandler = toggleIndex > 0 ? toggles[toggleIndex - 1] : null;
toggle.parentUIHandler = this;
toggle.index = toggleIndex;
toggle.nameLabel.text = enumName.text;
toggle.Init();
toggleIndex++;
}
// Destroy the remaining toggles outside of the range of the displayed enum.
for (; toggleIndex < toggles.Count; ++toggleIndex)
{
CoreUtils.Destroy(toggles[toggleIndex].gameObject);
toggles[toggleIndex] = null;
}
}
bool GetValue(int index)
{
if (index == 0)
{
// None can't be selected
return false;
}
else
{
// We need to remove 1 to the index because there is the None element on top of
// the enum and it doesn't count in the bit field because it's value is 0
index--;
int intValue = System.Convert.ToInt32(m_Field.GetValue());
return (intValue & (1 << index)) != 0;
}
}
void SetValue(int index, bool value)
{
if (index == 0)
{
// None was selected so we reset all the bits to false
m_Field.SetValue(System.Enum.ToObject(m_Field.enumType, 0));
foreach (var toggle in toggles)
{
if (toggle?.getter != null)
toggle.UpdateValueLabel();
}
}
else
{
int intValue = System.Convert.ToInt32(m_Field.GetValue());
if (value)
intValue |= m_Field.enumValues[index];
else
intValue &= ~m_Field.enumValues[index];
m_Field.SetValue(System.Enum.ToObject(m_Field.enumType, intValue));
}
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (fromNext || valueToggle.isOn == false)
{
nameLabel.color = colorSelected;
}
else if (valueToggle.isOn)
{
if (m_Container.IsDirectChild(previous))
{
nameLabel.color = colorSelected;
}
else
{
var lastItem = m_Container.GetLastItem();
DebugManager.instance.ChangeSelection(lastItem, false);
}
}
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
valueToggle.isOn = true;
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
valueToggle.isOn = false;
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
valueToggle.isOn = !valueToggle.isOn;
}
/// <summary>
/// Next implementation.
/// </summary>
/// <returns>Next widget UI handler, parent if there is none.</returns>
public override DebugUIHandlerWidget Next()
{
if (!valueToggle.isOn || m_Container == null)
return base.Next();
var firstChild = m_Container.GetFirstItem();
if (firstChild == null)
return base.Next();
return firstChild;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 662c81b0e51f80a4797c39bbd7116f9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for Button widget.
/// </summary>
public class DebugUIHandlerButton : DebugUIHandlerWidget
{
/// <summary>Name of the widget.</summary>
public Text nameLabel;
DebugUI.Button m_Field;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.Button>();
nameLabel.text = m_Field.displayName;
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>State of the widget.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
if (m_Field.action != null)
m_Field.action();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bff080b4e3bae64c80b54402ced6cc6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,345 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// Debug UI Prefab bundle.
/// </summary>
[Serializable]
public class DebugUIPrefabBundle
{
/// <summary>type of the widget.</summary>
public string type;
/// <summary>Prefab for the widget.</summary>
public RectTransform prefab;
}
/// <summary>
/// DebugUIHandler for canvas widget.
/// </summary>
public class DebugUIHandlerCanvas : MonoBehaviour
{
int m_DebugTreeState;
Dictionary<Type, Transform> m_PrefabsMap;
/// <summary>Panel prefab.</summary>
public Transform panelPrefab;
/// <summary>List of prefabs.</summary>
public List<DebugUIPrefabBundle> prefabs;
List<DebugUIHandlerPanel> m_UIPanels;
int m_SelectedPanel;
DebugUIHandlerWidget m_SelectedWidget;
string m_CurrentQueryPath;
void OnEnable()
{
if (prefabs == null)
prefabs = new List<DebugUIPrefabBundle>();
if (m_PrefabsMap == null)
m_PrefabsMap = new Dictionary<Type, Transform>();
if (m_UIPanels == null)
m_UIPanels = new List<DebugUIHandlerPanel>();
DebugManager.instance.RegisterRootCanvas(this);
}
void Update()
{
int state = DebugManager.instance.GetState();
if (m_DebugTreeState != state)
{
ResetAllHierarchy();
}
HandleInput();
// Update scroll position in the panel
if (m_UIPanels != null && m_SelectedPanel < m_UIPanels.Count && m_UIPanels[m_SelectedPanel] != null)
m_UIPanels[m_SelectedPanel].UpdateScroll();
}
internal void RequestHierarchyReset()
{
m_DebugTreeState = -1;
}
void ResetAllHierarchy()
{
foreach (Transform t in transform)
CoreUtils.Destroy(t.gameObject);
Rebuild();
}
void Rebuild()
{
// Check prefab associations
m_PrefabsMap.Clear();
foreach (var bundle in prefabs)
{
var type = Type.GetType(bundle.type);
if (type != null && bundle.prefab != null)
m_PrefabsMap.Add(type, bundle.prefab);
}
m_UIPanels.Clear();
m_DebugTreeState = DebugManager.instance.GetState();
var panels = DebugManager.instance.panels;
#if UNITY_ANDROID || UNITY_IPHONE
// Mobile device safe area
Rect parentRect = GetComponentInParent<RectTransform>().rect;
float parentWidth = Math.Min(parentRect.width, parentRect.height);
float scaleRatio = parentWidth / Math.Min(Screen.height, Screen.width);
Rect safeAreaRect = Screen.safeArea;
Vector2 margin = new Vector2(5, 5);
var safeAreaOffsetLeft = safeAreaRect.xMin * scaleRatio;
var safeAreaOffsetTop = -safeAreaRect.yMin * scaleRatio;
Vector2 safeAreaOffset = new Vector2(safeAreaOffsetLeft, safeAreaOffsetTop) + margin;
#endif
DebugUIHandlerWidget selectedWidget = null;
foreach (var panel in panels)
{
if (panel.isEditorOnly || panel.children.Count(x => !x.isEditorOnly && !x.isHidden) == 0)
continue;
var go = Instantiate(panelPrefab, transform, false).gameObject;
go.name = panel.displayName;
#if UNITY_ANDROID || UNITY_IPHONE
RectTransform rectTransform = go.GetComponent<RectTransform>();
rectTransform.anchoredPosition = safeAreaOffset;
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, safeAreaRect.height * scaleRatio + 2 * safeAreaOffsetTop);
#endif
var uiPanel = go.GetComponent<DebugUIHandlerPanel>();
uiPanel.SetPanel(panel);
uiPanel.Canvas = this;
m_UIPanels.Add(uiPanel);
var container = go.GetComponent<DebugUIHandlerContainer>();
DebugUIHandlerWidget selected = null;
Traverse(panel, container.contentHolder, null, ref selected);
if (selected != null && selected.GetWidget().queryPath.Contains(panel.queryPath))
{
selectedWidget = selected;
}
}
ActivatePanel(m_SelectedPanel, selectedWidget);
}
void Traverse(DebugUI.IContainer container, Transform parentTransform, DebugUIHandlerWidget parentUIHandler, ref DebugUIHandlerWidget selectedHandler)
{
DebugUIHandlerWidget previousUIHandler = null;
for (int i = 0; i < container.children.Count; i++)
{
var child = container.children[i];
if (child.isEditorOnly || child.isHidden)
continue;
Transform prefab;
if (!m_PrefabsMap.TryGetValue(child.GetType(), out prefab))
{
Debug.LogWarning("DebugUI widget doesn't have a prefab: " + child.GetType());
continue;
}
var go = Instantiate(prefab, parentTransform, false).gameObject;
go.name = child.displayName;
var uiHandler = go.GetComponent<DebugUIHandlerWidget>();
if (uiHandler == null)
{
Debug.LogWarning("DebugUI prefab is missing a DebugUIHandler for: " + child.GetType());
continue;
}
if (!string.IsNullOrEmpty(m_CurrentQueryPath) && child.queryPath.Equals(m_CurrentQueryPath))
{
selectedHandler = uiHandler;
}
if (previousUIHandler != null) previousUIHandler.nextUIHandler = uiHandler;
uiHandler.previousUIHandler = previousUIHandler;
previousUIHandler = uiHandler;
uiHandler.parentUIHandler = parentUIHandler;
uiHandler.SetWidget(child);
var childContainer = go.GetComponent<DebugUIHandlerContainer>();
if (childContainer != null && child is DebugUI.IContainer childAsContainer)
Traverse(childAsContainer, childContainer.contentHolder, uiHandler, ref selectedHandler);
}
}
DebugUIHandlerWidget GetWidgetFromPath(string queryPath)
{
if (string.IsNullOrEmpty(queryPath))
return null;
var panel = m_UIPanels[m_SelectedPanel];
return panel
.GetComponentsInChildren<DebugUIHandlerWidget>()
.FirstOrDefault(w => w.GetWidget().queryPath == queryPath);
}
void ActivatePanel(int index, DebugUIHandlerWidget selectedWidget = null)
{
if (m_UIPanels.Count == 0)
return;
if (index >= m_UIPanels.Count)
index = m_UIPanels.Count - 1;
m_UIPanels.ForEach(p => p.gameObject.SetActive(false));
m_UIPanels[index].gameObject.SetActive(true);
m_SelectedPanel = index;
if (selectedWidget == null)
selectedWidget = m_UIPanels[index].GetFirstItem();
ChangeSelection(selectedWidget, true);
}
internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
{
if (widget == null)
return;
if (m_SelectedWidget != null)
m_SelectedWidget.OnDeselection();
var prev = m_SelectedWidget;
m_SelectedWidget = widget;
SetScrollTarget(widget);
if (!m_SelectedWidget.OnSelection(fromNext, prev))
{
if (fromNext)
SelectNextItem();
else
SelectPreviousItem();
}
else
{
if (m_SelectedWidget == null || m_SelectedWidget.GetWidget() == null)
m_CurrentQueryPath = string.Empty;
else
m_CurrentQueryPath = m_SelectedWidget.GetWidget().queryPath;
}
}
internal void SelectPreviousItem()
{
if (m_SelectedWidget == null)
return;
var newSelection = m_SelectedWidget.Previous();
if (newSelection != null)
ChangeSelection(newSelection, false);
}
internal void SelectNextPanel()
{
int index = m_SelectedPanel + 1;
if (index >= m_UIPanels.Count)
index = 0;
index = Mathf.Clamp(index, 0, m_UIPanels.Count - 1);
ActivatePanel(index);
}
internal void SelectPreviousPanel()
{
int index = m_SelectedPanel - 1;
if (index < 0)
index = m_UIPanels.Count - 1;
index = Mathf.Clamp(index, 0, m_UIPanels.Count - 1);
ActivatePanel(index);
}
internal void SelectNextItem()
{
if (m_SelectedWidget == null)
return;
var newSelection = m_SelectedWidget.Next();
if (newSelection != null)
ChangeSelection(newSelection, true);
}
void ChangeSelectionValue(float multiplier)
{
if (m_SelectedWidget == null)
return;
bool fast = DebugManager.instance.GetAction(DebugAction.Multiplier) != 0f;
if (multiplier < 0f)
m_SelectedWidget.OnDecrement(fast);
else
m_SelectedWidget.OnIncrement(fast);
}
void ActivateSelection()
{
if (m_SelectedWidget == null)
return;
m_SelectedWidget.OnAction();
}
void HandleInput()
{
if (DebugManager.instance.GetAction(DebugAction.PreviousDebugPanel) != 0f)
{
SelectPreviousPanel();
}
if (DebugManager.instance.GetAction(DebugAction.NextDebugPanel) != 0f)
{
SelectNextPanel();
}
if (DebugManager.instance.GetAction(DebugAction.Action) != 0f)
ActivateSelection();
if (DebugManager.instance.GetAction(DebugAction.MakePersistent) != 0f && m_SelectedWidget != null)
DebugManager.instance.TogglePersistent(m_SelectedWidget.GetWidget());
float moveHorizontal = DebugManager.instance.GetAction(DebugAction.MoveHorizontal);
if (moveHorizontal != 0f)
ChangeSelectionValue(moveHorizontal);
float moveVertical = DebugManager.instance.GetAction(DebugAction.MoveVertical);
if (moveVertical != 0f)
{
if (moveVertical < 0f)
SelectNextItem();
else
SelectPreviousItem();
}
}
internal void SetScrollTarget(DebugUIHandlerWidget widget)
{
if (m_UIPanels != null && m_SelectedPanel < m_UIPanels.Count && m_UIPanels[m_SelectedPanel] != null)
m_UIPanels[m_SelectedPanel].SetScrollTarget(widget);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76db615e524a19c4990482d75a475543
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,167 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for color widget.
/// </summary>
public class DebugUIHandlerColor : DebugUIHandlerWidget
{
/// <summary>Name of the widget.</summary>
public Text nameLabel;
/// <summary>/// <summary>Name of the widget.</summary>alue toggle.</summary>
public UIFoldout valueToggle;
/// <summary>Color image.</summary>
public Image colorImage;
/// <summary>Red float field.</summary>
public DebugUIHandlerIndirectFloatField fieldR;
/// <summary>Green float field.</summary>
public DebugUIHandlerIndirectFloatField fieldG;
/// <summary>Blue float field.</summary>
public DebugUIHandlerIndirectFloatField fieldB;
/// <summary>Alpha float field.</summary>
public DebugUIHandlerIndirectFloatField fieldA;
DebugUI.ColorField m_Field;
DebugUIHandlerContainer m_Container;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.ColorField>();
m_Container = GetComponent<DebugUIHandlerContainer>();
nameLabel.text = m_Field.displayName;
fieldR.getter = () => m_Field.GetValue().r;
fieldR.setter = x => SetValue(x, r: true);
fieldR.nextUIHandler = fieldG;
SetupSettings(fieldR);
fieldG.getter = () => m_Field.GetValue().g;
fieldG.setter = x => SetValue(x, g: true);
fieldG.previousUIHandler = fieldR;
fieldG.nextUIHandler = fieldB;
SetupSettings(fieldG);
fieldB.getter = () => m_Field.GetValue().b;
fieldB.setter = x => SetValue(x, b: true);
fieldB.previousUIHandler = fieldG;
fieldB.nextUIHandler = m_Field.showAlpha ? fieldA : null;
SetupSettings(fieldB);
fieldA.gameObject.SetActive(m_Field.showAlpha);
fieldA.getter = () => m_Field.GetValue().a;
fieldA.setter = x => SetValue(x, a: true);
fieldA.previousUIHandler = fieldB;
SetupSettings(fieldA);
UpdateColor();
}
void SetValue(float x, bool r = false, bool g = false, bool b = false, bool a = false)
{
var color = m_Field.GetValue();
if (r) color.r = x;
if (g) color.g = x;
if (b) color.b = x;
if (a) color.a = x;
m_Field.SetValue(color);
UpdateColor();
}
void SetupSettings(DebugUIHandlerIndirectFloatField field)
{
field.parentUIHandler = this;
field.incStepGetter = () => m_Field.incStep;
field.incStepMultGetter = () => m_Field.incStepMult;
field.decimalsGetter = () => m_Field.decimals;
field.Init();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>State of the widget.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (fromNext || valueToggle.isOn == false)
{
nameLabel.color = colorSelected;
}
else if (valueToggle.isOn)
{
if (m_Container.IsDirectChild(previous))
{
nameLabel.color = colorSelected;
}
else
{
var lastItem = m_Container.GetLastItem();
DebugManager.instance.ChangeSelection(lastItem, false);
}
}
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
valueToggle.isOn = true;
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
valueToggle.isOn = false;
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
valueToggle.isOn = !valueToggle.isOn;
}
internal void UpdateColor()
{
if (colorImage != null)
colorImage.color = m_Field.GetValue();
}
/// <summary>
/// Next implementation.
/// </summary>
/// <returns>Next child.</returns>
public override DebugUIHandlerWidget Next()
{
if (!valueToggle.isOn || m_Container == null)
return base.Next();
var firstChild = m_Container.GetFirstItem();
if (firstChild == null)
return base.Next();
return firstChild;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 590d0a4a23369fe4094bee34430c4b38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for container widget.
/// </summary>
public class DebugUIHandlerContainer : MonoBehaviour
{
/// <summary>Content holder.</summary>
[SerializeField]
public RectTransform contentHolder;
internal DebugUIHandlerWidget GetFirstItem()
{
if (contentHolder.childCount == 0)
return null;
var items = GetActiveChildren();
if (items.Count == 0)
return null;
return items[0];
}
internal DebugUIHandlerWidget GetLastItem()
{
if (contentHolder.childCount == 0)
return null;
var items = GetActiveChildren();
if (items.Count == 0)
return null;
return items[items.Count - 1];
}
internal bool IsDirectChild(DebugUIHandlerWidget widget)
{
if (contentHolder.childCount == 0)
return false;
return GetActiveChildren()
.Count(x => x == widget) > 0;
}
List<DebugUIHandlerWidget> GetActiveChildren()
{
var list = new List<DebugUIHandlerWidget>();
foreach (Transform t in contentHolder)
{
if (!t.gameObject.activeInHierarchy)
continue;
var c = t.GetComponent<DebugUIHandlerWidget>();
if (c != null)
list.Add(c);
}
return list;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2bd470ffc0c0fe54faddbf8d466bf519
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for enumerator widget.
/// </summary>
public class DebugUIHandlerEnumField : DebugUIHandlerField<DebugUI.EnumField>
{
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
if (m_Field.enumValues.Length == 0)
return;
var array = m_Field.enumValues;
int index = m_Field.currentIndex;
if (index == array.Length - 1)
{
index = 0;
}
else
{
if (fast)
{
//check if quickSeparators have not been constructed
//it is the case when not constructed with autoenum
var separators = m_Field.quickSeparators;
if (separators == null)
{
m_Field.InitQuickSeparators();
separators = m_Field.quickSeparators;
}
int idxSup = 0;
for (; idxSup < separators.Length && index + 1 > separators[idxSup]; ++idxSup) ;
if (idxSup == separators.Length)
{
index = 0;
}
else
{
index = separators[idxSup];
}
}
else
{
index += 1;
}
}
m_Field.SetValue(array[index]);
m_Field.currentIndex = index;
UpdateValueLabel();
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
if (m_Field.enumValues.Length == 0)
return;
var array = m_Field.enumValues;
int index = m_Field.currentIndex;
if (index == 0)
{
if (fast)
{
//check if quickSeparators have not been constructed
//it is thecase when not constructed with autoenum
var separators = m_Field.quickSeparators;
if (separators == null)
{
m_Field.InitQuickSeparators();
separators = m_Field.quickSeparators;
}
index = separators[separators.Length - 1];
}
else
{
index = array.Length - 1;
}
}
else
{
if (fast)
{
//check if quickSeparators have not been constructed
//it is the case when not constructed with autoenum
var separators = m_Field.quickSeparators;
if (separators == null)
{
m_Field.InitQuickSeparators();
separators = m_Field.quickSeparators;
}
int idxInf = separators.Length - 1;
for (; idxInf > 0 && index <= separators[idxInf]; --idxInf) ;
index = separators[idxInf];
}
else
{
index -= 1;
}
}
m_Field.SetValue(array[index]);
m_Field.currentIndex = index;
UpdateValueLabel();
}
/// <summary>
/// Update the label of the widget.
/// </summary>
public override void UpdateValueLabel()
{
int index = m_Field.currentIndex;
// Fallback just in case, we may be handling sub/sectionned enums here
if (index < 0)
index = 0;
SetLabelText(m_Field.enumNames[index].text);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ca07cb82ca30874c849ad6a8be4ce42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System.Collections;
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for enum with history widget.
/// </summary>
public class DebugUIHandlerEnumHistory : DebugUIHandlerEnumField
{
Text[] historyValues;
const float k_XOffset = 230f;
internal override void SetWidget(DebugUI.Widget widget)
{
int historyDepth = (widget as DebugUI.HistoryEnumField)?.historyDepth ?? 0;
historyValues = new Text[historyDepth];
float columnOffset = historyDepth > 0 ? k_XOffset / (float)historyDepth : 0f;
for (int index = 0; index < historyDepth; ++index)
{
var historyValue = Instantiate(valueLabel, transform);
Vector3 pos = historyValue.transform.position;
pos.x += (index + 1) * columnOffset;
historyValue.transform.position = pos;
var text = historyValue.GetComponent<Text>();
text.color = new Color32(110, 110, 110, 255);
historyValues[index] = text;
}
//this call UpdateValueLabel which will rely on historyToggles
base.SetWidget(widget);
}
/// <summary>
/// Update the label of the widget.
/// </summary>
public override void UpdateValueLabel()
{
int index = m_Field.currentIndex;
// Fallback just in case, we may be handling sub/sectionned enums here
if (index < 0)
index = 0;
valueLabel.text = m_Field.enumNames[index].text;
DebugUI.HistoryEnumField field = m_Field as DebugUI.HistoryEnumField;
int historyDepth = field?.historyDepth ?? 0;
for (int indexHistory = 0; indexHistory < historyDepth; ++indexHistory)
{
if (indexHistory < historyValues.Length && historyValues[indexHistory] != null)
historyValues[indexHistory].text = field.enumNames[field.GetHistoryValue(indexHistory)].text;
}
if (isActiveAndEnabled)
StartCoroutine(RefreshAfterSanitization());
}
IEnumerator RefreshAfterSanitization()
{
yield return null; //wait one frame
m_Field.currentIndex = m_Field.getIndex();
valueLabel.text = m_Field.enumNames[m_Field.currentIndex].text;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecc0d22e5e3de604f9e4d3d8997e9122
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// Base class for handling UI actions for widgets.
/// </summary>
/// <typeparam name="T">Base type for the field</typeparam>
public abstract class DebugUIHandlerField<T> : DebugUIHandlerWidget
where T : DebugUI.Widget
{
/// <summary>Text displayed for the "next" button.</summary>
public Text nextButtonText;
/// <summary>Text displayed for the "previous" button.</summary>
public Text previousButtonText;
/// <summary>Name of the enum field.</summary>
public Text nameLabel;
/// <summary>Value of the enum field.</summary>
public Text valueLabel;
/// <summary>
/// The field
/// </summary>
internal protected T m_Field;
/// <summary>
/// Sets the widget and updates the label
/// </summary>
/// <param name="widget">The <see cref="DebugUI.Widget"/></param>
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<T>();
nameLabel.text = m_Field.displayName;
UpdateValueLabel();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>State of the widget.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (nextButtonText != null)
nextButtonText.color = colorSelected;
if (previousButtonText != null)
previousButtonText.color = colorSelected;
nameLabel.color = colorSelected;
valueLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
if (nextButtonText != null)
nextButtonText.color = colorDefault;
if (previousButtonText != null)
previousButtonText.color = colorDefault;
nameLabel.color = colorDefault;
valueLabel.color = colorDefault;
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
OnIncrement(false);
}
/// <summary>
/// Update the label of the widget.
/// </summary>
public abstract void UpdateValueLabel();
/// <summary>
/// Sets the label text
/// </summary>
/// <param name="text">The text to set to the label</param>
protected void SetLabelText(string text)
{
// The UI implementation is tight with space, so let's just truncate the string here if too long.
const int maxLength = 26;
if (text.Length > maxLength)
{
text = text.Substring(0, maxLength - 3) + "...";
}
valueLabel.text = text;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a117669f07d7994419ebdfb1a79e13db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for float widget.
/// </summary>
public class DebugUIHandlerFloatField : DebugUIHandlerWidget
{
/// <summary>Name of the enum field.</summary>
public Text nameLabel;
/// <summary>Value of the enum field.</summary>
public Text valueLabel;
DebugUI.FloatField m_Field;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.FloatField>();
nameLabel.text = m_Field.displayName;
UpdateValueLabel();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
valueLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
valueLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
ChangeValue(fast, 1);
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
ChangeValue(fast, -1);
}
void ChangeValue(bool fast, float multiplier)
{
float value = m_Field.GetValue();
value += m_Field.incStep * (fast ? m_Field.incStepMult : 1f) * multiplier;
m_Field.SetValue(value);
UpdateValueLabel();
}
void UpdateValueLabel()
{
valueLabel.text = m_Field.GetValue().ToString("N" + m_Field.decimals);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c71addad67814c418e8376c7fabd008
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,139 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for foldout widget.
/// </summary>
public class DebugUIHandlerFoldout : DebugUIHandlerWidget
{
/// <summary>Name of the Foldout.</summary>
public Text nameLabel;
/// <summary>Toggle value of the Foldout.</summary>
public UIFoldout valueToggle;
DebugUI.Foldout m_Field;
DebugUIHandlerContainer m_Container;
const float k_FoldoutXOffset = 215f;
const float k_XOffset = 230f;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.Foldout>();
m_Container = GetComponent<DebugUIHandlerContainer>();
nameLabel.text = m_Field.displayName;
int columnNumber = m_Field.columnLabels?.Length ?? 0;
float columnOffset = columnNumber > 0 ? k_XOffset / (float)columnNumber : 0f;
for (int index = 0; index < columnNumber; ++index)
{
var column = Instantiate(nameLabel.gameObject, GetComponent<DebugUIHandlerContainer>().contentHolder);
column.AddComponent<LayoutElement>().ignoreLayout = true;
var rectTransform = column.transform as RectTransform;
var originalTransform = nameLabel.transform as RectTransform;
rectTransform.anchorMax = rectTransform.anchorMin = new Vector2(0, 1);
rectTransform.sizeDelta = new Vector2(100, 26);
Vector3 pos = originalTransform.anchoredPosition;
pos.x += (index + 1) * columnOffset + k_FoldoutXOffset;
rectTransform.anchoredPosition = pos;
rectTransform.pivot = new Vector2(0, 0.5f);
rectTransform.eulerAngles = new Vector3(0, 0, 13);
var text = column.GetComponent<Text>();
text.fontSize = 15;
text.text = m_Field.columnLabels[index];
}
UpdateValue();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (fromNext || valueToggle.isOn == false)
{
nameLabel.color = colorSelected;
}
else if (valueToggle.isOn)
{
if (m_Container.IsDirectChild(previous))
{
nameLabel.color = colorSelected;
}
else
{
var lastItem = m_Container.GetLastItem();
DebugManager.instance.ChangeSelection(lastItem, false);
}
}
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
m_Field.SetValue(true);
UpdateValue();
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
m_Field.SetValue(false);
UpdateValue();
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
bool value = !m_Field.GetValue();
m_Field.SetValue(value);
UpdateValue();
}
void UpdateValue()
{
valueToggle.isOn = m_Field.GetValue();
}
/// <summary>
/// Next implementation.
/// </summary>
/// <returns>Next widget UI handler, parent if there is none.</returns>
public override DebugUIHandlerWidget Next()
{
if (!m_Field.GetValue() || m_Container == null)
return base.Next();
var firstChild = m_Container.GetFirstItem();
if (firstChild == null)
return base.Next();
return firstChild;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e5dc9f9bba5df14fbe439bcc4c67063
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for group widget.
/// </summary>
public class DebugUIHandlerGroup : DebugUIHandlerWidget
{
/// <summary>Name of the group.</summary>
public Text nameLabel;
/// <summary>Header of the group.</summary>
public Transform header;
DebugUI.Container m_Field;
DebugUIHandlerContainer m_Container;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.Container>();
m_Container = GetComponent<DebugUIHandlerContainer>();
if (m_Field.hideDisplayName)
header.gameObject.SetActive(false);
else
nameLabel.text = m_Field.displayName;
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (!fromNext && !m_Container.IsDirectChild(previous))
{
var lastItem = m_Container.GetLastItem();
DebugManager.instance.ChangeSelection(lastItem, false);
return true;
}
return false;
}
/// <summary>
/// Next implementation.
/// </summary>
/// <returns>Next widget UI handler, parent if there is none.</returns>
public override DebugUIHandlerWidget Next()
{
if (m_Container == null)
return base.Next();
var firstChild = m_Container.GetFirstItem();
if (firstChild == null)
return base.Next();
return firstChild;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5d44b76204264d4eb90d4a92b067d7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for Horizontal Layout widget.
/// </summary>
public class DebugUIHandlerHBox : DebugUIHandlerWidget
{
DebugUIHandlerContainer m_Container;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Container = GetComponent<DebugUIHandlerContainer>();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
if (!fromNext && !m_Container.IsDirectChild(previous))
{
var lastItem = m_Container.GetLastItem();
DebugManager.instance.ChangeSelection(lastItem, false);
return true;
}
return false;
}
/// <summary>
/// Next implementation.
/// </summary>
/// <returns>Next widget UI handler, parent if there is none.</returns>
public override DebugUIHandlerWidget Next()
{
if (m_Container == null)
return base.Next();
var firstChild = m_Container.GetFirstItem();
if (firstChild == null)
return base.Next();
return firstChild;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8261e6f2e5fac44da64da2b23939e9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,100 @@
using System;
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for indirect float widget.
/// </summary>
public class DebugUIHandlerIndirectFloatField : DebugUIHandlerWidget
{
/// <summary>Name of the indirect float field.</summary>
public Text nameLabel;
/// <summary>Value of the indirect float field.</summary>
public Text valueLabel;
/// <summary>
/// Getter function for this indirect widget.
/// </summary>
public Func<float> getter;
/// <summary>
/// Setter function for this indirect widget.
/// </summary>
public Action<float> setter;
/// <summary>
/// Getter function for the increment step of this indirect widget.
/// </summary>
public Func<float> incStepGetter;
/// <summary>
/// Getter function for the increment step multiplier of this indirect widget.
/// </summary>
public Func<float> incStepMultGetter;
/// <summary>
/// Getter function for the number of decimals of this indirect widget.
/// </summary>
public Func<float> decimalsGetter;
/// <summary>
/// Initialize the indirect widget.
/// </summary>
public void Init()
{
UpdateValueLabel();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
valueLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
valueLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
ChangeValue(fast, 1);
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
ChangeValue(fast, -1);
}
void ChangeValue(bool fast, float multiplier)
{
float value = getter();
value += incStepGetter() * (fast ? incStepMultGetter() : 1f) * multiplier;
setter(value);
UpdateValueLabel();
}
void UpdateValueLabel()
{
if (valueLabel != null)
valueLabel.text = getter().ToString("N" + decimalsGetter());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c42b71ff0e23e2d4a8a32a2bc85acac0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using System;
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for indirect toggle widget.
/// </summary>
public class DebugUIHandlerIndirectToggle : DebugUIHandlerWidget
{
/// <summary>
/// Label of the widget.
/// </summary>
public Text nameLabel;
/// <summary>Toggle of the toggle field.</summary>
public Toggle valueToggle;
/// <summary>Checkmark image.</summary>
public Image checkmarkImage;
/// <summary>
/// Getter function for this indirect widget.
/// </summary>
public Func<int, bool> getter;
/// <summary>
/// Setter function for this indirect widget.
/// </summary>
public Action<int, bool> setter;
// Should not be here, this is a byproduct of the Bitfield UI Handler implementation.
internal int index;
/// <summary>
/// Initialize the indirect widget.
/// </summary>
public void Init()
{
UpdateValueLabel();
valueToggle.onValueChanged.AddListener(OnToggleValueChanged);
}
void OnToggleValueChanged(bool value)
{
setter(index, value);
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
checkmarkImage.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
checkmarkImage.color = colorDefault;
}
/// <summary>
/// OnAction implementation.
/// </summary>
public override void OnAction()
{
bool value = !getter(index);
setter(index, value);
UpdateValueLabel();
}
internal void UpdateValueLabel()
{
if (valueToggle != null)
valueToggle.isOn = getter(index);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0012fc4688b5d5342a441aa32c0e099e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for integer widget.
/// </summary>
public class DebugUIHandlerIntField : DebugUIHandlerWidget
{
/// <summary>Name of the int field.</summary>
public Text nameLabel;
/// <summary>Value of the int field.</summary>
public Text valueLabel;
DebugUI.IntField m_Field;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.IntField>();
nameLabel.text = m_Field.displayName;
UpdateValueLabel();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
valueLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
valueLabel.color = colorDefault;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
ChangeValue(fast, 1);
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
ChangeValue(fast, -1);
}
void ChangeValue(bool fast, int multiplier)
{
int value = m_Field.GetValue();
value += m_Field.incStep * (fast ? m_Field.intStepMult : 1) * multiplier;
m_Field.SetValue(value);
UpdateValueLabel();
}
void UpdateValueLabel()
{
if (valueLabel != null)
valueLabel.text = m_Field.GetValue().ToString("N0");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3de67e2fb3c96a542b808862989985e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for MessageBox widget.
/// </summary>
public class DebugUIHandlerMessageBox : DebugUIHandlerWidget
{
/// <summary>Name of the widget.</summary>
public Text nameLabel;
DebugUI.MessageBox m_Field;
static Color32 k_WarningBackgroundColor = new Color32(231, 180, 3, 30);
static Color32 k_WarningTextColor = new Color32(231, 180, 3, 255);
static Color32 k_ErrorBackgroundColor = new Color32(231, 75, 3, 30);
static Color32 k_ErrorTextColor = new Color32(231, 75, 3, 255);
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Field = CastWidget<DebugUI.MessageBox>();
nameLabel.text = m_Field.displayName;
var image = GetComponent<Image>();
switch (m_Field.style)
{
case DebugUI.MessageBox.Style.Warning:
image.color = k_WarningBackgroundColor;
break;
case DebugUI.MessageBox.Style.Error:
image.color = k_ErrorBackgroundColor;
break;
}
}
/// <summary>
/// Method called when the box is selected
/// </summary>
/// <param name="fromNext">If is from next</param>
/// <param name="previous">The previous <see cref="DebugUIHandlerWidget"/></param>
/// <returns></returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e25c98bed7eaeec4cb32c57a5280f85e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for object widget.
/// </summary>
public class DebugUIHandlerObject : DebugUIHandlerWidget
{
/// <summary>Name of the value field.</summary>
public Text nameLabel;
/// <summary>Value of the value field.</summary>
public Text valueLabel;
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
var field = CastWidget<DebugUI.ObjectField>();
nameLabel.text = field.displayName;
valueLabel.text = field.GetValue().name;
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
valueLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
valueLabel.color = colorDefault;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8b3da1ab40a53c49a3de1e3fe357ad2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for object list widget.
/// </summary>
public class DebugUIHandlerObjectList : DebugUIHandlerField<DebugUI.ObjectListField>
{
int m_Index;
/// <summary>
/// Sets the widget and updates the label
/// </summary>
/// <param name="widget">The <see cref="DebugUI.Widget"/></param>
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Index = 0;
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
m_Index++;
UpdateValueLabel();
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
m_Index--;
UpdateValueLabel();
}
/// <summary>
/// Update the label of the widget.
/// </summary>
public override void UpdateValueLabel()
{
string text = "Empty";
var values = m_Field.GetValue();
if (values != null)
{
m_Index = System.Math.Clamp(m_Index, 0, values.Length - 1);
text = values[m_Index].name;
}
SetLabelText(text);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79e8dd95427d853478c168019e7ce191
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using System.Linq;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for object popup widget.
/// </summary>
public class DebugUIHandlerObjectPopupField : DebugUIHandlerField<DebugUI.ObjectPopupField>
{
int m_Index;
/// <summary>
/// Sets the widget and updates the label
/// </summary>
/// <param name="widget">The <see cref="DebugUI.Widget"/></param>
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Index = 0;
}
private void ChangeSelectedObject()
{
if (m_Field == null)
return;
var elements = m_Field.getObjects();
if (elements == null)
return;
var elementsArray = elements.ToArray();
var count = elementsArray.Count();
if (m_Index >= count)
{
m_Index = 0;
}
else if (m_Index < 0)
{
m_Index = count - 1;
}
var newSelectedValue = elementsArray[m_Index];
m_Field.SetValue(newSelectedValue);
UpdateValueLabel();
}
/// <summary>
/// OnIncrement implementation.
/// </summary>
/// <param name="fast">True if incrementing fast.</param>
public override void OnIncrement(bool fast)
{
m_Index++;
ChangeSelectedObject();
}
/// <summary>
/// OnDecrement implementation.
/// </summary>
/// <param name="fast">Trye if decrementing fast.</param>
public override void OnDecrement(bool fast)
{
m_Index--;
ChangeSelectedObject();
}
/// <summary>
/// Update the label of the widget.
/// </summary>
public override void UpdateValueLabel()
{
var selectedObject = m_Field.GetValue();
string text = (selectedObject != null) ? selectedObject.name : "Empty";
SetLabelText(text);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9721515c2a23578428cf3aa4626db09d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for panels.
/// </summary>
public class DebugUIHandlerPanel : MonoBehaviour
{
/// <summary>Name of the panel.</summary>
public Text nameLabel;
/// <summary>Scroll rect of the panel.</summary>
public ScrollRect scrollRect;
/// <summary>Viewport of the panel.</summary>
public RectTransform viewport;
/// <summary>Associated canvas.</summary>
public DebugUIHandlerCanvas Canvas;
RectTransform m_ScrollTransform;
RectTransform m_ContentTransform;
RectTransform m_MaskTransform;
DebugUIHandlerWidget m_ScrollTarget;
internal protected DebugUI.Panel m_Panel;
void OnEnable()
{
m_ScrollTransform = scrollRect.GetComponent<RectTransform>();
m_ContentTransform = GetComponent<DebugUIHandlerContainer>().contentHolder;
m_MaskTransform = GetComponentInChildren<Mask>(true).rectTransform;
}
internal void SetPanel(DebugUI.Panel panel)
{
m_Panel = panel;
nameLabel.text = panel.displayName;
}
internal DebugUI.Panel GetPanel()
{
return m_Panel;
}
/// <summary>
/// Select next panel on the canvas.
/// </summary>
public void SelectNextItem()
{
Canvas.SelectNextPanel();
}
/// <summary>
/// Select previous panel on the canvas.
/// </summary>
public void SelectPreviousItem()
{
Canvas.SelectPreviousPanel();
}
/// <summary>
/// Scrollbar value clicked via mouse/touch.
/// </summary>
public void OnScrollbarClicked()
{
DebugManager.instance.SetScrollTarget(null); // Release scroll target
}
internal void SetScrollTarget(DebugUIHandlerWidget target)
{
m_ScrollTarget = target;
}
// TODO: Jumps around with foldouts and the likes, fix me
internal void UpdateScroll()
{
if (m_ScrollTarget == null)
return;
var targetTransform = m_ScrollTarget.GetComponent<RectTransform>();
float itemY = GetYPosInScroll(targetTransform);
float targetY = GetYPosInScroll(m_MaskTransform);
float normalizedDiffY = (targetY - itemY) / (m_ContentTransform.rect.size.y - m_ScrollTransform.rect.size.y);
float normalizedPosY = scrollRect.verticalNormalizedPosition - normalizedDiffY;
normalizedPosY = Mathf.Clamp01(normalizedPosY);
scrollRect.verticalNormalizedPosition = Mathf.Lerp(scrollRect.verticalNormalizedPosition, normalizedPosY, Time.deltaTime * 10f);
}
float GetYPosInScroll(RectTransform target)
{
var pivotOffset = new Vector3(
(0.5f - target.pivot.x) * target.rect.size.x,
(0.5f - target.pivot.y) * target.rect.size.y,
0f
);
var localPos = target.localPosition + pivotOffset;
var worldPos = target.parent.TransformPoint(localPos);
return m_ScrollTransform.TransformPoint(worldPos).y;
}
internal DebugUIHandlerWidget GetFirstItem()
{
return GetComponent<DebugUIHandlerContainer>()
.GetFirstItem();
}
/// <summary>
/// Function to reset DebugManager, provided for UI.
/// </summary>
public void ResetDebugManager()
{
DebugManager.instance.Reset();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f61540bb3be4c2409cf6d2f51435714
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
namespace UnityEngine.Rendering.UI
{
class DebugUIHandlerPersistentCanvas : MonoBehaviour
{
public RectTransform panel;
public RectTransform valuePrefab;
List<DebugUIHandlerValue> m_Items = new List<DebugUIHandlerValue>();
// Toggles persistent value widget on/off.
internal void Toggle(DebugUI.Value widget, string displayName = null)
{
int index = m_Items.FindIndex(x => x.GetWidget() == widget);
// Remove
if (index > -1)
{
var item = m_Items[index];
CoreUtils.Destroy(item.gameObject);
m_Items.RemoveAt(index);
return;
}
// Add
var go = Instantiate(valuePrefab, panel, false).gameObject;
var uiHandler = go.GetComponent<DebugUIHandlerValue>();
uiHandler.SetWidget(widget);
uiHandler.nameLabel.text = string.IsNullOrEmpty(displayName) ? widget.displayName : displayName;
m_Items.Add(uiHandler);
}
List<DebugUI.ValueTuple> m_ValueTupleWidgets = new();
// For ValueTuples (multiple values on one row), we cycle through the columns, and turn the widget
// off after the last column.
internal void Toggle(DebugUI.ValueTuple widget, int? forceTupleIndex = null)
{
var val = m_ValueTupleWidgets.Find(x => x == widget);
int tupleIndex = val?.pinnedElementIndex ?? -1;
// Clear old widget
if (val != null)
{
m_ValueTupleWidgets.Remove(val);
Toggle(widget.values[tupleIndex]);
}
if (forceTupleIndex != null)
tupleIndex = forceTupleIndex.Value;
// Enable next widget (unless at the last index)
if (tupleIndex + 1 < widget.numElements)
{
widget.pinnedElementIndex = tupleIndex + 1;
// Add column to name
string displayName = widget.displayName;
if (widget.parent is DebugUI.Foldout)
{
var columnLabels = (widget.parent as DebugUI.Foldout).columnLabels;
if (columnLabels != null && widget.pinnedElementIndex < columnLabels.Length)
{
displayName += $" ({columnLabels[widget.pinnedElementIndex]})";
}
}
Toggle(widget.values[widget.pinnedElementIndex], displayName);
m_ValueTupleWidgets.Add(widget);
}
else
{
widget.pinnedElementIndex = -1;
}
}
internal bool IsEmpty()
{
return m_Items.Count == 0;
}
internal void Clear()
{
foreach (var item in m_Items)
CoreUtils.Destroy(item.gameObject);
m_Items.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87177621604658d4e893f55be7cfae4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using System;
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for progress bar widget.
/// </summary>
public class DebugUIHandlerProgressBar : DebugUIHandlerWidget
{
/// <summary>Name of the progress bar.</summary>
public Text nameLabel;
/// <summary>Value of the progress bar.</summary>
public Text valueLabel;
/// <summary>Rectangle representing the progress bar.</summary>
public RectTransform progressBarRect;
DebugUI.ProgressBarValue m_Value;
float m_Timer;
/// <summary>
/// OnEnable implementation.
/// </summary>
protected override void OnEnable()
{
m_Timer = 0f;
}
internal override void SetWidget(DebugUI.Widget widget)
{
base.SetWidget(widget);
m_Value = CastWidget<DebugUI.ProgressBarValue>();
nameLabel.text = m_Value.displayName;
UpdateValue();
}
/// <summary>
/// OnSelection implementation.
/// </summary>
/// <param name="fromNext">True if the selection wrapped around.</param>
/// <param name="previous">Previous widget.</param>
/// <returns>True if the selection is allowed.</returns>
public override bool OnSelection(bool fromNext, DebugUIHandlerWidget previous)
{
nameLabel.color = colorSelected;
return true;
}
/// <summary>
/// OnDeselection implementation.
/// </summary>
public override void OnDeselection()
{
nameLabel.color = colorDefault;
}
void Update()
{
if (m_Timer >= m_Value.refreshRate)
{
UpdateValue();
m_Timer -= m_Value.refreshRate;
}
m_Timer += Time.deltaTime;
}
void UpdateValue()
{
float value = (float)m_Value.GetValue();
valueLabel.text = m_Value.FormatString(value);
Vector3 scale = progressBarRect.localScale;
scale.x = value;
progressBarRect.localScale = scale;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1ad6bc96b5de44ad93f820fd338ee684
timeCreated: 1625131386

View File

@@ -0,0 +1,124 @@
using UnityEngine.UI;
namespace UnityEngine.Rendering.UI
{
/// <summary>
/// DebugUIHandler for row widget.
/// </summary>
public class DebugUIHandlerRow : DebugUIHandlerFoldout
{
float m_Timer;
/// <summary>
/// OnEnable implementation.
/// </summary>
protected override void OnEnable()
{
m_Timer = 0f;
}
GameObject GetChild(int index)
{
if (index < 0)
return null;
if (gameObject.transform != null)
{
var firstChild = gameObject.transform.GetChild(1);
if (firstChild != null && firstChild.childCount > index)
{
return firstChild.GetChild(index).gameObject;
}
}
return null;
}
bool TryGetChild(int index, out GameObject child)
{
child = GetChild(index);
return child != null;
}
bool IsActive(DebugUI.Table table, int index, GameObject child)
{
if (!table.GetColumnVisibility(index))
return false;
var valueChild = child.transform.Find("Value");
if (valueChild != null && valueChild.TryGetComponent<Text>(out var text))
return !string.IsNullOrEmpty(text.text);
return true;
}
/// <summary>
/// Update implementation.
/// </summary>
protected void Update()
{
var row = CastWidget<DebugUI.Table.Row>();
var table = row.parent as DebugUI.Table;
float refreshRate = 0.1f;
bool refreshRow = m_Timer >= refreshRate;
if (refreshRow)
m_Timer -= refreshRate;
m_Timer += Time.deltaTime;
for (int i = 0; i < row.children.Count; i++)
{
if (!TryGetChild(i, out var child))
continue;
bool active = IsActive(table, i, child);
if (child != null)
child.SetActive(active);
if (active && refreshRow)
{
if (child.TryGetComponent<DebugUIHandlerColor>(out var color))
color.UpdateColor();
if (child.TryGetComponent<DebugUIHandlerToggle>(out var toggle))
toggle.UpdateValueLabel();
if (child.TryGetComponent<DebugUIHandlerObjectList>(out var list))
list.UpdateValueLabel();
}
}
// Update previous and next ui handlers to skip hidden volumes
var itemWidget = GetChild(0).GetComponent<DebugUIHandlerWidget>();
DebugUIHandlerWidget previous = null;
for (int i = 0; i < row.children.Count; i++)
{
itemWidget.previousUIHandler = previous;
if (!TryGetChild(i, out var child))
continue;
if (IsActive(table, i, child))
previous = itemWidget;
bool found = false;
for (int j = i + 1; j < row.children.Count; j++)
{
if (!TryGetChild(j, out var innerChild))
continue;
if (IsActive(table, j, innerChild))
{
var childWidget = child.GetComponent<DebugUIHandlerWidget>();
itemWidget.nextUIHandler = childWidget;
itemWidget = childWidget;
i = j - 1;
found = true;
break;
}
}
if (!found)
{
itemWidget.nextUIHandler = null;
break;
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More