using System; using System.Collections.Generic; using System.Reflection; using System.Linq; namespace UnityEngine.Rendering.HighDefinition { /// /// Type of entity on which frame settings are applied. /// public enum FrameSettingsRenderType { /// Frame settings are applied to a camera. Camera, /// Frame settings are applied to a baked or custom reflection probe. CustomOrBakedReflection, /// Frame settings are applied to a realtime reflection. RealtimeReflection } internal interface IFrameSettingsHistoryContainer : IDebugData { FrameSettingsHistory frameSettingsHistory { get; set; } FrameSettingsOverrideMask frameSettingsMask { get; } FrameSettings frameSettings { get; } bool hasCustomFrameSettings { get; } string panelName { get; } } struct FrameSettingsHistory { internal static readonly string[] foldoutNames = { "Rendering", "Lighting", "Async Compute", "Light Loop" }; static readonly string[] columnNames = { "Debug", "Sanitized", "Overridden", "Default" }; static readonly Dictionary attributes; static Dictionary>> attributesGroup = new Dictionary>>(); // due to strange management of Scene view cameras, all camera of type Scene will share same FrameSettingsHistory #if UNITY_EDITOR //internal static Camera sceneViewCamera; class MinimalHistoryContainer : IFrameSettingsHistoryContainer { FrameSettingsHistory m_FrameSettingsHistory = new FrameSettingsHistory(); FrameSettingsHistory IFrameSettingsHistoryContainer.frameSettingsHistory { get => m_FrameSettingsHistory; set => m_FrameSettingsHistory = value; } // never used as hasCustomFrameSettings forced to false FrameSettingsOverrideMask IFrameSettingsHistoryContainer.frameSettingsMask => throw new NotImplementedException(); // never used as hasCustomFrameSettings forced to false FrameSettings IFrameSettingsHistoryContainer.frameSettings => throw new NotImplementedException(); // forced to false as there is no control on this object bool IFrameSettingsHistoryContainer.hasCustomFrameSettings => false; string IFrameSettingsHistoryContainer.panelName => "Scene Camera"; public MinimalHistoryContainer() => m_FrameSettingsHistory.debug = HDRenderPipeline.defaultAsset?.GetDefaultFrameSettings(FrameSettingsRenderType.Camera) ?? FrameSettings.NewDefaultCamera(); Action IDebugData.GetReset() //caution: we actually need to retrieve the //m_FrameSettingsHistory as it is a struct so no direct // => m_FrameSettingsHistory.TriggerReset => () => m_FrameSettingsHistory.TriggerReset(); } internal static IFrameSettingsHistoryContainer sceneViewFrameSettingsContainer = new MinimalHistoryContainer(); #endif internal static HashSet containers = new HashSet(); public FrameSettingsRenderType defaultType; public FrameSettings overridden; public FrameSettingsOverrideMask customMask; public FrameSettings sanitazed; public FrameSettings debug; bool hasDebug; static bool s_PossiblyInUse; public static bool enabled { get { // The feature is enabled when either DebugWindow or DebugRuntimeUI // are displayed. When none are displayed, the feature remain in use // as long as there is one renderer that have debug modification. // We use s_PossiblyInUse to perform the check on the FrameSettingsHistory // collection the less possible (only when we exited all the windows // as long as there is modification). if (!s_PossiblyInUse) return s_PossiblyInUse = DebugManager.instance.displayEditorUI || DebugManager.instance.displayRuntimeUI; else return DebugManager.instance.displayEditorUI || DebugManager.instance.displayRuntimeUI // a && (a = something) different than a &= something as if a is false something is not evaluated in second version || (s_PossiblyInUse && (s_PossiblyInUse = containers.Any(history => history.frameSettingsHistory.hasDebug))); } } /// Initialize data for FrameSettings panel of DebugMenu construction. static FrameSettingsHistory() { attributes = new Dictionary(); attributesGroup = new Dictionary>>(); Dictionary frameSettingsEnumNameMap = FrameSettingsFieldAttribute.GetEnumNameMap(); Type type = typeof(FrameSettingsField); foreach (FrameSettingsField enumVal in frameSettingsEnumNameMap.Keys) { attributes[enumVal] = type.GetField(frameSettingsEnumNameMap[enumVal]).GetCustomAttribute(); } } /// Same than FrameSettings.AggregateFrameSettings but keep history of agregation in a collection for DebugMenu. /// Aggregation is default with override of the renderer then sanitazed depending on supported features of hdrpasset. Then the DebugMenu override occurs. /// The aggregated FrameSettings result. /// The camera rendering. /// Additional data of the camera rendering. /// HDRenderPipelineAsset contening default FrameSettings. public static void AggregateFrameSettings(ref FrameSettings aggregatedFrameSettings, Camera camera, HDAdditionalCameraData additionalData, HDRenderPipelineAsset hdrpAsset, HDRenderPipelineAsset defaultHdrpAsset) => AggregateFrameSettings( ref aggregatedFrameSettings, camera, #if UNITY_EDITOR camera.cameraType == CameraType.SceneView ? sceneViewFrameSettingsContainer : #endif additionalData, ref defaultHdrpAsset.GetDefaultFrameSettings(additionalData?.defaultFrameSettings ?? FrameSettingsRenderType.Camera), //fallback on Camera for SceneCamera and PreviewCamera hdrpAsset.currentPlatformRenderPipelineSettings ); // Note: this version is the one tested as there is issue getting HDRenderPipelineAsset in batchmode in unit test framework currently. /// Same than FrameSettings.AggregateFrameSettings but keep history of agregation in a collection for DebugMenu. /// Aggregation is default with override of the renderer then sanitazed depending on supported features of hdrpasset. Then the DebugMenu override occurs. /// The aggregated FrameSettings result. /// The camera rendering. /// Additional data of the camera rendering. /// Base framesettings to copy prior any override. /// Currently supported feature for the sanitazation pass. public static void AggregateFrameSettings(ref FrameSettings aggregatedFrameSettings, Camera camera, IFrameSettingsHistoryContainer historyContainer, ref FrameSettings defaultFrameSettings, RenderPipelineSettings supportedFeatures) { FrameSettingsHistory history = historyContainer.frameSettingsHistory; aggregatedFrameSettings = defaultFrameSettings; bool updatedComponent = false; if (historyContainer.hasCustomFrameSettings) { FrameSettings.Override(ref aggregatedFrameSettings, historyContainer.frameSettings, historyContainer.frameSettingsMask); updatedComponent = history.customMask.mask != historyContainer.frameSettingsMask.mask; history.customMask = historyContainer.frameSettingsMask; } history.overridden = aggregatedFrameSettings; FrameSettings.Sanitize(ref aggregatedFrameSettings, camera, supportedFeatures); history.hasDebug = history.debug != aggregatedFrameSettings; updatedComponent |= history.sanitazed != aggregatedFrameSettings; bool dirtyDebugData = !history.hasDebug || updatedComponent; history.sanitazed = aggregatedFrameSettings; if (dirtyDebugData) { // Reset debug data history.debug = history.sanitazed; } else { // Keep user modified debug data // Ensure user is not trying to activate unsupported settings in DebugMenu FrameSettings.Sanitize(ref history.debug, camera, supportedFeatures); } aggregatedFrameSettings = history.debug; historyContainer.frameSettingsHistory = history; } static DebugUI.HistoryBoolField GenerateHistoryBoolField(HDRenderPipelineAsset defaultHdrpAsset, IFrameSettingsHistoryContainer frameSettingsContainer, FrameSettingsField field, FrameSettingsFieldAttribute attribute) { string displayIndent = ""; for (int indent = 0; indent < attribute.indentLevel; ++indent) displayIndent += " "; return new DebugUI.HistoryBoolField { displayName = displayIndent + attribute.displayedName, getter = () => frameSettingsContainer.frameSettingsHistory.debug.IsEnabled(field), setter = value => { var tmp = frameSettingsContainer.frameSettingsHistory; tmp.debug.SetEnabled(field, value); frameSettingsContainer.frameSettingsHistory = tmp; }, historyGetter = new Func[] { () => frameSettingsContainer.frameSettingsHistory.sanitazed.IsEnabled(field), () => frameSettingsContainer.frameSettingsHistory.overridden.IsEnabled(field), () => defaultHdrpAsset.GetDefaultFrameSettings(frameSettingsContainer.frameSettingsHistory.defaultType).IsEnabled(field) } }; } static DebugUI.HistoryEnumField GenerateHistoryEnumField(HDRenderPipelineAsset defaultHdrpAsset, IFrameSettingsHistoryContainer frameSettingsContainer, FrameSettingsField field, FrameSettingsFieldAttribute attribute, Type autoEnum) { string displayIndent = ""; for (int indent = 0; indent < attribute.indentLevel; ++indent) displayIndent += " "; return new DebugUI.HistoryEnumField { displayName = displayIndent + attribute.displayedName, getter = () => frameSettingsContainer.frameSettingsHistory.debug.IsEnabled(field) ? 1 : 0, setter = value => { var tmp = frameSettingsContainer.frameSettingsHistory; //indexer with struct will create a copy tmp.debug.SetEnabled(field, value == 1); frameSettingsContainer.frameSettingsHistory = tmp; }, autoEnum = autoEnum, // Contrarily to other enum of DebugMenu, we do not need to stock index as // it can be computed again with data in the dedicated debug section of history getIndex = () => frameSettingsContainer.frameSettingsHistory.debug.IsEnabled(field) ? 1 : 0, setIndex = (int a) => {}, historyIndexGetter = new Func[] { () => frameSettingsContainer.frameSettingsHistory.sanitazed.IsEnabled(field) ? 1 : 0, () => frameSettingsContainer.frameSettingsHistory.overridden.IsEnabled(field) ? 1 : 0, () => defaultHdrpAsset.GetDefaultFrameSettings(frameSettingsContainer.frameSettingsHistory.defaultType).IsEnabled(field) ? 1 : 0 } }; } static ObservableList GenerateHistoryArea(HDRenderPipelineAsset defaultHdrpAsset, IFrameSettingsHistoryContainer frameSettingsContainer, int groupIndex) { if (!attributesGroup.ContainsKey(groupIndex) || attributesGroup[groupIndex] == null) attributesGroup[groupIndex] = attributes?.Where(pair => pair.Value?.group == groupIndex)?.OrderBy(pair => pair.Value.orderInGroup); if (!attributesGroup.ContainsKey(groupIndex)) throw new ArgumentException("Unknown groupIndex"); var area = new ObservableList(); foreach (var field in attributesGroup[groupIndex]) { switch (field.Value.type) { case FrameSettingsFieldAttribute.DisplayType.BoolAsCheckbox: area.Add(GenerateHistoryBoolField( defaultHdrpAsset, frameSettingsContainer, field.Key, field.Value)); break; case FrameSettingsFieldAttribute.DisplayType.BoolAsEnumPopup: area.Add(GenerateHistoryEnumField( defaultHdrpAsset, frameSettingsContainer, field.Key, field.Value, RetrieveEnumTypeByField(field.Key) )); break; case FrameSettingsFieldAttribute.DisplayType.Others: // for now, skip other display settings. Add them if needed break; } } return area; } static DebugUI.Widget[] GenerateFrameSettingsPanelContent(HDRenderPipelineAsset defaultHdrpAsset, IFrameSettingsHistoryContainer frameSettingsContainer) { var panelContent = new DebugUI.Widget[foldoutNames.Length]; for (int index = 0; index < foldoutNames.Length; ++index) { panelContent[index] = new DebugUI.Foldout(foldoutNames[index], GenerateHistoryArea(defaultHdrpAsset, frameSettingsContainer, index), columnNames); } return panelContent; } static void GenerateFrameSettingsPanel(string menuName, IFrameSettingsHistoryContainer frameSettingsContainer) { HDRenderPipelineAsset defaultHdrpAsset = HDRenderPipeline.defaultAsset; List widgets = new List(); widgets.AddRange(GenerateFrameSettingsPanelContent(defaultHdrpAsset, frameSettingsContainer)); var panel = DebugManager.instance.GetPanel( menuName, createIfNull: true, #if UNITY_EDITOR frameSettingsContainer == sceneViewFrameSettingsContainer ? 1 : // Scene Camera #endif 2, // Other Cameras (from Camera component) overrideIfExist: true); panel.children.Add(widgets.ToArray()); } static Type RetrieveEnumTypeByField(FrameSettingsField field) { switch (field) { case FrameSettingsField.LitShaderMode: return typeof(LitShaderMode); default: throw new ArgumentException("Unknown enum type for this field"); } } /// Register FrameSettingsHistory for DebugMenu public static IDebugData RegisterDebug(IFrameSettingsHistoryContainer frameSettingsContainer, bool sceneViewCamera = false) { #if UNITY_EDITOR if (sceneViewCamera) frameSettingsContainer = sceneViewFrameSettingsContainer; #endif GenerateFrameSettingsPanel(frameSettingsContainer.panelName, frameSettingsContainer); containers.Add(frameSettingsContainer); return frameSettingsContainer; } /// Unregister FrameSettingsHistory for DebugMenu public static void UnRegisterDebug(IFrameSettingsHistoryContainer container) { DebugManager.instance.RemovePanel(container.panelName); containers.Remove(container); } /// Check if a camera is registered. public static bool IsRegistered(IFrameSettingsHistoryContainer container, bool sceneViewCamera = false) { if (sceneViewCamera) return true; return containers.Contains(container); } internal void TriggerReset() { debug = sanitazed; hasDebug = false; } } }