Skip to content
1 change: 1 addition & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Increased path tracing BSDFs roughness range from [0.001, 0.999] to [0.00001, 0.99999].
- Changing the default SSGI radius for the all configurations.
- Changed the default parameters for quality RTGI to match expected behavior.
- Improved DoF UI/UX (case 1240204)

## [7.1.1] - 2019-09-05

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,66 +60,182 @@ public override void OnInspectorGUI()

int mode = m_FocusMode.value.intValue;
if (mode == (int)DepthOfFieldMode.Off)
return;
{
GUI.enabled = false;
}

// Draw the focus mode controls
HDEditorUtils.BeginIndent();
DrawFocusSettings(mode);
HDEditorUtils.EndIndent();

EditorGUILayout.Space();

// Draw the quality controls
base.OnInspectorGUI();
HDEditorUtils.BeginIndent();
GUI.enabled = GUI.enabled && base.overrideState;
DrawQualitySettings();
HDEditorUtils.EndIndent();

bool advanced = isInAdvancedMode;
GUI.enabled = true;
}

if (mode == (int)DepthOfFieldMode.UsePhysicalCamera)
void DrawFocusSettings(int mode)
{
if (mode == (int)DepthOfFieldMode.Off)
{
// When DoF is off, display a focus distance at infinity
var val = m_FocusDistance.value.floatValue;
m_FocusDistance.value.floatValue = Mathf.Infinity;
PropertyField(m_FocusDistance);
m_FocusDistance.value.floatValue = val;
}
else if (mode == (int)DepthOfFieldMode.UsePhysicalCamera)
{
PropertyField(m_FocusDistance);

if (advanced)
{
GUI.enabled = useCustomValue;
EditorGUILayout.LabelField("Near Blur", EditorStyles.miniLabel);
PropertyField(m_NearSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_NearMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));

EditorGUILayout.LabelField("Far Blur", EditorStyles.miniLabel);
PropertyField(m_FarSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_FarMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));
GUI.enabled = true;
}
}
else if (mode == (int)DepthOfFieldMode.Manual)
{
EditorGUILayout.Space();

EditorGUILayout.LabelField("Near Blur", EditorStyles.miniLabel);
EditorGUILayout.LabelField("Near Range", EditorStyles.miniLabel);
PropertyField(m_NearFocusStart, EditorGUIUtility.TrTextContent("Start"));
PropertyField(m_NearFocusEnd, EditorGUIUtility.TrTextContent("End"));

if (advanced)
{
GUI.enabled = useCustomValue;
PropertyField(m_NearSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_NearMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));
GUI.enabled = true;
}

EditorGUILayout.LabelField("Far Blur", EditorStyles.miniLabel);
EditorGUILayout.LabelField("Far Range", EditorStyles.miniLabel);
PropertyField(m_FarFocusStart, EditorGUIUtility.TrTextContent("Start"));
PropertyField(m_FarFocusEnd, EditorGUIUtility.TrTextContent("End"));

if (advanced)
{
GUI.enabled = useCustomValue;
PropertyField(m_FarSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_FarMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));
GUI.enabled = true;
}
}
}

void DrawQualitySettings()
{
object oldSettings = SaveCustomQualitySettingsAsObject();
EditorGUI.BeginChangeCheck();
EditorGUILayout.LabelField("Near Blur", EditorStyles.miniLabel);
PropertyField(m_NearSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_NearMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));

EditorGUILayout.LabelField("Far Blur", EditorStyles.miniLabel);
PropertyField(m_FarSampleCount, EditorGUIUtility.TrTextContent("Sample Count"));
PropertyField(m_FarMaxBlur, EditorGUIUtility.TrTextContent("Max Radius"));

if (advanced)
if (isInAdvancedMode)
{
GUI.enabled = useCustomValue;
EditorGUILayout.LabelField("Advanced Tweaks", EditorStyles.miniLabel);
PropertyField(m_Resolution);
PropertyField(m_HighQualityFiltering);
GUI.enabled = true;
}

if (EditorGUI.EndChangeCheck())
{
object newSettings = SaveCustomQualitySettingsAsObject();

if (!QualitySettingsBlob.IsEqual(oldSettings as QualitySettingsBlob, newSettings as QualitySettingsBlob))
QualitySettingsWereChanged();
}
}

/// An opaque binary blob storing preset settings (used to remember what were the last custom settings that were used).
/// For the functionality to save and restore the settings <see cref="VolumeComponentWithQualityEditor"/>
class QualitySettingsBlob
{
public int nearSampleCount;
public float nearMaxBlur;
public int farSampleCount;
public float farMaxBlur;
public DepthOfFieldResolution resolution;
public bool hqFiltering;

public bool[] overrideState = new bool[6];

public static bool IsEqual(QualitySettingsBlob left, QualitySettingsBlob right)
{
if ((right == null && left != null) || (right != null && left == null))
{
return false;
}

if (right == null && left == null)
{
return true;
}

for (int i=0; i < left.overrideState.Length; ++i)
{
if (left.overrideState[i] != right.overrideState[i])
{
return false;
}
}

return left.nearSampleCount == right.nearSampleCount
&& left.nearMaxBlur == right.nearMaxBlur
&& left.farSampleCount == right.farSampleCount
&& left.farMaxBlur == right.farMaxBlur
&& left.resolution == right.resolution
&& left.hqFiltering == right.hqFiltering;
}
}

public override void LoadSettingsFromObject(object settings)
{
QualitySettingsBlob qualitySettings = settings as QualitySettingsBlob;

m_NearSampleCount.value.intValue = qualitySettings.nearSampleCount;
m_NearMaxBlur.value.floatValue = qualitySettings.nearMaxBlur;
m_FarSampleCount.value.intValue = qualitySettings.farSampleCount;
m_FarMaxBlur.value.floatValue = qualitySettings.farMaxBlur;
m_Resolution.value.intValue = (int) qualitySettings.resolution;
m_HighQualityFiltering.value.boolValue = qualitySettings.hqFiltering;

m_NearSampleCount.overrideState.boolValue = qualitySettings.overrideState[0];
m_NearMaxBlur.overrideState.boolValue = qualitySettings.overrideState[1];
m_FarSampleCount.overrideState.boolValue = qualitySettings.overrideState[2];
m_FarMaxBlur.overrideState.boolValue = qualitySettings.overrideState[3];
m_Resolution.overrideState.boolValue = qualitySettings.overrideState[4];
m_HighQualityFiltering.overrideState.boolValue = qualitySettings.overrideState[5];
}

public override void LoadSettingsFromQualityPreset(RenderPipelineSettings settings, int level)
{
m_NearSampleCount.value.intValue = settings.postProcessQualitySettings.NearBlurSampleCount[level];
m_NearMaxBlur.value.floatValue = settings.postProcessQualitySettings.NearBlurMaxRadius[level];

m_FarSampleCount.value.intValue = settings.postProcessQualitySettings.FarBlurSampleCount[level];
m_FarMaxBlur.value.floatValue = settings.postProcessQualitySettings.FarBlurMaxRadius[level];

m_Resolution.value.intValue = (int) settings.postProcessQualitySettings.DoFResolution[level];
m_HighQualityFiltering.value.boolValue = settings.postProcessQualitySettings.DoFHighQualityFiltering[level];

// set all quality override states to true, to indicate that these values are actually used
m_NearSampleCount.overrideState.boolValue = true;
m_NearMaxBlur.overrideState.boolValue = true;
m_FarSampleCount.overrideState.boolValue = true;
m_FarMaxBlur.overrideState.boolValue = true;
m_Resolution.overrideState.boolValue = true;
m_HighQualityFiltering.overrideState.boolValue = true;

}

public override object SaveCustomQualitySettingsAsObject(object history = null)
{
QualitySettingsBlob qualitySettings = (history != null) ? history as QualitySettingsBlob : new QualitySettingsBlob();

qualitySettings.nearSampleCount = m_NearSampleCount.value.intValue;
qualitySettings.nearMaxBlur = m_NearMaxBlur.value.floatValue;
qualitySettings.farSampleCount = m_FarSampleCount.value.intValue;
qualitySettings.farMaxBlur = m_FarMaxBlur.value.floatValue;
qualitySettings.resolution = (DepthOfFieldResolution) m_Resolution.value.intValue;
qualitySettings.hqFiltering = m_HighQualityFiltering.value.boolValue;

qualitySettings.overrideState[0] = m_NearSampleCount.overrideState.boolValue;
qualitySettings.overrideState[1] = m_NearMaxBlur.overrideState.boolValue;
qualitySettings.overrideState[2] = m_FarSampleCount.overrideState.boolValue;
qualitySettings.overrideState[3] = m_FarMaxBlur.overrideState.boolValue;
qualitySettings.overrideState[4] = m_Resolution.overrideState.boolValue;
qualitySettings.overrideState[5] = m_HighQualityFiltering.overrideState.boolValue;

return qualitySettings;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,30 @@ internal static void HandlePrefixLabelWithIndent(Rect totalPosition, Rect labelP
labelPosition.x += EditorGUI.indentLevel * 15;
EditorGUI.HandlePrefixLabel(totalPosition, labelPosition, label);
}

/// <summary>
/// Like EditorGUI.indentLevel++ but this one will also indent the override checkboxes
/// </summary>
internal static void BeginIndent()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have prefered a scope here in order to be sure anyone using it will not forget to call also EndIndent :)

{
// When using EditorGUI.indentLevel++, the clicking on the checkboxes does not work properly due to some issues on the C++ side.
// This function is a work-around for this issue.
const float offset = 15f;
GUILayout.BeginHorizontal();
EditorGUILayout.Space(offset, false);
GUILayout.BeginVertical();
EditorGUIUtility.labelWidth -= offset;
}

/// <summary>
/// To be used for resetting the indentation after calling BeginIndent
/// </summary>
internal static void EndIndent()
{
EditorGUIUtility.labelWidth = 0f;
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
}

internal static partial class SerializedPropertyExtension
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

namespace UnityEditor.Rendering.HighDefinition
Expand All @@ -7,15 +9,106 @@ internal abstract class VolumeComponentWithQualityEditor : VolumeComponentEditor
// Quality settings
SerializedDataParameter m_QualitySetting;

// Note: Editors are refreshed on gui changes by the volume system, so any state that we want to store here needs to be a static (or in a serialized variable)
// We use ConditionalWeakTable instead of a Dictionary of InstanceIDs to get automatic clean-up of dead entries in the table
static ConditionalWeakTable<UnityEngine.Object, object> s_CustomSettingsHistory = new ConditionalWeakTable<UnityEngine.Object, object>();

static readonly int k_CustomQuality = ScalableSettingLevelParameter.LevelCount;

public override void OnEnable()
{
var o = new PropertyFetcher<VolumeComponentWithQuality>(serializedObject);
m_QualitySetting = Unpack(o.Find(x => x.quality));
}

public override void OnInspectorGUI() =>PropertyField(m_QualitySetting);
public override void OnInspectorGUI()
{
int prevQualityLevel = m_QualitySetting.value.intValue;

EditorGUI.BeginChangeCheck();
PropertyField(m_QualitySetting);

// When a quality preset changes, we want to detect and reflect the settings in the UI. PropertyFields mirror the contents of one memory loccation, so
// the idea is that we copy the presets to that location. This logic is optional, if volume components don't override the helper functions at the end,
// they will continue to work, but the preset settings will not be reflected in the UI.
if (EditorGUI.EndChangeCheck())
{
// The ScalableSettingLevelParameterEditor updates the value of the referenced object directly (and not the reflected one),
// so this is what we have to do to get the new value selected by the user:
var o = m_QualitySetting.GetObjectRef<ScalableSettingLevelParameter>();
var (preset, custom) = o.levelAndOverride;
int newQualityLevel = custom ? 3 : preset;
m_QualitySetting.value.intValue = newQualityLevel;

if (newQualityLevel == k_CustomQuality)
{
// If we have switched to custom quality from a preset, then load the last custom quality settings the user has used in this volume
if (prevQualityLevel != k_CustomQuality)
{
object history = null;
s_CustomSettingsHistory.TryGetValue(serializedObject.targetObject, out history);
if (history != null)
{
LoadSettingsFromObject(history);
}
}
}
else
{
// If we are going to use a quality preset, then load the preset values so they are reflected in the UI
var pipeline = (HDRenderPipeline)RenderPipelineManager.currentPipeline;
if (pipeline != null)
{
// If we switch from a custom quality level, then save these values so we can re-use them if teh user switches back
if (prevQualityLevel == k_CustomQuality)
{
int key = serializedObject.targetObject.GetInstanceID();
object history = null;
s_CustomSettingsHistory.TryGetValue(serializedObject.targetObject, out history);
if (history != null)
{
SaveCustomQualitySettingsAsObject(history);
}
else
{
// Only keep track of custom settings for components that implement the new interface (and return not null)
history = SaveCustomQualitySettingsAsObject();
if (history != null)
{
s_CustomSettingsHistory.Add(serializedObject.targetObject, history);
}

}
}
LoadSettingsFromQualityPreset(pipeline.currentPlatformRenderPipelineSettings, newQualityLevel);
}
}
}
}

protected bool useCustomValue => m_QualitySetting.value.intValue == k_CustomQuality;
protected bool overrideState => m_QualitySetting.overrideState.boolValue;

/// <summary>
/// This should be called after the user manually edits a quality setting that appears in a preset. After calling this function, the quality preset will change to Custom.
/// </summary>
public void QualitySettingsWereChanged() { m_QualitySetting.value.intValue = k_CustomQuality; }

/// <summary>
/// This function should be overriden by a volume component to load preset settings from RenderPipelineSettings
/// </summary>
public virtual void LoadSettingsFromQualityPreset(RenderPipelineSettings settings, int level) { }

/// <summary>
/// This function should be overriden by a volume component to return an opaque object (binary blob) with the custom quality settings currently in use.
/// </summary>
public virtual object SaveCustomQualitySettingsAsObject(object history = null) { return null; }

/// <summary>
/// This function should be overriden by a volume component to load a custom preset setting from an opaque binary blob (as returned from SaveCustomQualitySettingsAsObject)
/// </summary>
public virtual void LoadSettingsFromObject(object settings) { }

protected bool useCustomValue => m_QualitySetting.value.intValue == ScalableSettingLevelParameter.LevelCount;
}

}