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;
}
}
}