123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using UnityEditor;
-
- namespace UnityEngine.Rendering
- {
- /// <summary>
- /// Debug Display Settings Volume
- /// </summary>
- public class DebugDisplaySettingsVolume : IDebugDisplaySettingsData
- {
- /// <summary>Current volume debug settings.</summary>
- public IVolumeDebugSettings volumeDebugSettings { get; }
-
- /// <summary>
- /// Constructor with the settings
- /// </summary>
- /// <param name="volumeDebugSettings">The volume debug settings object used for configuration.</param>
- public DebugDisplaySettingsVolume(IVolumeDebugSettings volumeDebugSettings)
- {
- this.volumeDebugSettings = volumeDebugSettings;
- }
-
- internal int volumeComponentEnumIndex;
-
- internal Dictionary<string, VolumeComponent> debugState = new Dictionary<string, VolumeComponent>();
-
- 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 resultValue = "Result";
- public static readonly string resultValueTooltip = "The interpolated result value of the parameter. This value is used to render the camera.";
- public static readonly string globalDefaultValue = "Default";
- public static readonly string globalDefaultValueTooltip = "Default value for this parameter, defined by the Default Volume Profile in Global Settings.";
- public static readonly string qualityLevelValue = "SRP Asset";
- public static readonly string qualityLevelValueTooltip = "Override value for this parameter, defined by the Volume Profile in the current SRP Asset.";
- public static readonly string global = "Global";
- public static readonly string local = "Local";
- }
-
- const string k_PanelTitle = "Volume";
-
- #if UNITY_EDITOR
- internal static void OpenInRenderingDebugger()
- {
- EditorApplication.ExecuteMenuItem("Window/Analysis/Rendering Debugger");
- var idx = DebugManager.instance.FindPanelIndex(k_PanelTitle);
- if (idx != -1)
- DebugManager.instance.RequestEditorWindowPanelIndex(idx);
- }
- #endif
-
- 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++ };
-
- var volumesAndTypes = VolumeManager.instance.GetVolumeComponentsForDisplay(GraphicsSettings.currentRenderPipelineAssetType);
- foreach (var type in volumesAndTypes)
- {
- 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
- };
- }
-
- static DebugUI.Value s_EmptyDebugUIValue = new DebugUI.Value { getter = () => string.Empty };
-
- public static DebugUI.Table CreateVolumeTable(DebugDisplaySettingsVolume data)
- {
- var table = new DebugUI.Table()
- {
- displayName = Strings.parameter,
- isReadOnly = true,
- isHiddenCallback = () => data.volumeDebugSettings.selectedComponent == 0
- };
-
- Type selectedType = data.volumeDebugSettings.selectedComponentType;
- if (selectedType == null)
- return table;
-
- var volumeManager = VolumeManager.instance;
- var stack = data.volumeDebugSettings.selectedCameraVolumeStack ?? volumeManager.stack;
- var stackComponent = stack.GetComponent(selectedType);
- if (stackComponent == null)
- return table;
-
- var volumes = data.volumeDebugSettings.GetVolumes();
-
- // First row for volume info
- var row1 = new DebugUI.Table.Row()
- {
- displayName = Strings.volumeInfo,
- opened = true, // Open by default for the in-game view
- children =
- {
- new DebugUI.Value()
- {
- displayName = Strings.resultValue,
- tooltip = Strings.resultValueTooltip,
- getter = () => string.Empty
- }
- }
- };
-
- // Second row, links to volume gameobjects
- var row2 = new DebugUI.Table.Row()
- {
- displayName = "GameObject",
- children = { s_EmptyDebugUIValue }
- };
-
- // Third row, links to volume profile assets
- var row3 = new DebugUI.Table.Row()
- {
- displayName = "Volume Profile",
- children = { s_EmptyDebugUIValue }
- };
-
- // Fourth row, empty (to separate from actual data)
- var row4 = new DebugUI.Table.Row()
- {
- displayName = string.Empty ,
- children = { s_EmptyDebugUIValue }
- };
-
- foreach (var volume in volumes)
- {
- var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile;
- row1.children.Add(new DebugUI.Value()
- {
- displayName = profile.name,
- tooltip = $"Override value for this parameter, defined by {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 = string.Empty, getter = () => volume });
- row3.children.Add(new DebugUI.ObjectField() { displayName = string.Empty, getter = () => profile });
- row4.children.Add(s_EmptyDebugUIValue);
- }
-
- // Default value profiles
- var globalDefaultComponent = GetSelectedVolumeComponent(volumeManager.globalDefaultProfile);
- var qualityDefaultComponent = GetSelectedVolumeComponent(volumeManager.qualityDefaultProfile);
- List<(VolumeProfile, VolumeComponent)> customDefaultComponents = new();
- if (volumeManager.customDefaultProfiles != null)
- {
- foreach (var customProfile in volumeManager.customDefaultProfiles)
- {
- var customDefaultComponent = GetSelectedVolumeComponent(customProfile);
- if (customDefaultComponent != null)
- customDefaultComponents.Add((customProfile, customDefaultComponent));
- }
- }
-
- foreach (var (customProfile, _) in customDefaultComponents)
- {
- row1.children.Add(new DebugUI.Value() { displayName = customProfile.name, getter = () => string.Empty });
- row2.children.Add(s_EmptyDebugUIValue);
- row3.children.Add(new DebugUI.ObjectField() { displayName = string.Empty, getter = () => customProfile });
- row4.children.Add(s_EmptyDebugUIValue);
- }
-
- row1.children.Add(new DebugUI.Value() { displayName = Strings.qualityLevelValue, tooltip = Strings.qualityLevelValueTooltip, getter = () => string.Empty });
- row2.children.Add(s_EmptyDebugUIValue);
- row3.children.Add(new DebugUI.ObjectField() { displayName = string.Empty, getter = () => volumeManager.qualityDefaultProfile });
- row4.children.Add(s_EmptyDebugUIValue);
-
- row1.children.Add(new DebugUI.Value() { displayName = Strings.globalDefaultValue, tooltip = Strings.globalDefaultValueTooltip, getter = () => string.Empty });
- row2.children.Add(s_EmptyDebugUIValue);
- row3.children.Add(new DebugUI.ObjectField() { displayName = string.Empty, getter = () => volumeManager.globalDefaultProfile });
- row4.children.Add(s_EmptyDebugUIValue);
-
- table.children.Add(row1);
- table.children.Add(row2);
- table.children.Add(row3);
- table.children.Add(row4);
-
- VolumeComponent GetSelectedVolumeComponent(VolumeProfile profile)
- {
- if (profile != null)
- {
- foreach (var component in profile.components)
- if (component.GetType() == selectedType)
- return component;
- }
- return null;
- }
-
- // 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;
- DebugUI.Table.Row row = new DebugUI.Table.Row()
- {
- displayName = fieldName,
- children = { CreateVolumeParameterWidget(Strings.resultValue, 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));
- }
-
- foreach (var (customProfile, customComponent) in customDefaultComponents)
- row.children.Add(CreateVolumeParameterWidget(customProfile.name,
- customComponent != null ? customComponent.parameterList[currentParam] : null));
-
- row.children.Add(CreateVolumeParameterWidget(Strings.qualityLevelValue,
- qualityDefaultComponent != null ? qualityDefaultComponent.parameterList[currentParam] : null));
-
- row.children.Add(CreateVolumeParameterWidget(Strings.globalDefaultValue,
- globalDefaultComponent != null ? globalDefaultComponent.parameterList[currentParam] : null));
-
- 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 = k_PanelTitle, order = int.MaxValue)]
- internal class SettingsPanel : DebugDisplaySettingsPanel<DebugDisplaySettingsVolume>
- {
- public SettingsPanel(DebugDisplaySettingsVolume data)
- : base(data)
- {
- AddWidget(WidgetFactory.CreateCameraSelector(this, (_, __) => Refresh()));
- AddWidget(WidgetFactory.CreateComponentSelector(this, (_, __) => Refresh()));
- m_VolumeTable = WidgetFactory.CreateVolumeTable(m_Data);
- AddWidget(m_VolumeTable);
- }
-
- 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
-
- /// <inheritdoc/>
- public IDebugDisplaySettingsPanelDisposable CreatePanel()
- {
- return new SettingsPanel(this);
- }
-
- #endregion
- }
- }
|