using UnityEngine.Scripting;
#if VISUAL_SCRIPTING_ENABLED
using Unity.VisualScripting;
using UnityEngine.AdaptivePerformance.VisualScripting;
#endif
namespace UnityEngine.AdaptivePerformance
{
///
/// Scaler impact on visuals.
///
public enum ScalerVisualImpact
{
///
/// Low impact on visual quality. Changes might not be very noticeable to the user.
///
Low,
///
/// Medium impact on visual quality. Mildly affects the visuals in the scene and might be noticeable to the user.
///
Medium,
///
/// High impact on visual quality. The scaler will have an easily visible effect on quality.
///
High
}
///
/// Bottleneck flags that the scaler targets.
///
[System.Flags]
public enum ScalerTarget
{
///
/// The scaler targets the CPU and attempts to reduce the CPU load.
///
CPU = 0x1,
///
/// The scaler targets the GPU and attempts to reduce the GPU load.
///
GPU = 0x2,
///
/// The scaler targets fillrate, often at the expense of visual quality.
///
FillRate = 0x4
}
///
/// Abstract class representing single feature that is controlled by .
/// You control the quality through changing the levels, where 0 represents the controller not being applied and 1,2... as applied.
/// As a result, a higher level represents lower visuals, but better performance.
///
#if UNITY_2021_2_OR_NEWER
[RequireDerived]
#endif
public abstract class AdaptivePerformanceScaler : ScriptableObject
{
private AdaptivePerformanceIndexer m_Indexer;
///
/// Returns a string with the name of the scaler.
///
public virtual string Name
{
get => m_defaultSetting.name;
set
{
if (m_defaultSetting.name == value)
return;
m_defaultSetting.name = value;
}
}
///
/// Returns `true` if this scaler is active, otherwise returns `false`.
///
public virtual bool Enabled
{
get => m_defaultSetting.enabled;
set
{
if (m_defaultSetting.enabled == value)
return;
m_defaultSetting.enabled = value;
AdaptivePerformanceAnalytics.SendAdaptiveFeatureUpdateEvent(Name, m_defaultSetting.enabled);
}
}
///
/// Returns a generic scale of this scaler in range [0,1] which is applied to the quality.
///
public virtual float Scale
{
get => m_defaultSetting.scale;
set
{
if (m_defaultSetting.scale == value)
return;
m_defaultSetting.scale = value;
}
}
///
/// Returns the visual impact of scaler when applied.
///
public virtual ScalerVisualImpact VisualImpact
{
get => m_defaultSetting.visualImpact;
set
{
if (m_defaultSetting.visualImpact == value)
return;
m_defaultSetting.visualImpact = value;
}
}
///
/// Returns the bottlenecks that this scaler targets.
///
public virtual ScalerTarget Target
{
get => m_defaultSetting.target;
set
{
if (m_defaultSetting.target == value)
return;
m_defaultSetting.target = value;
}
}
///
/// Returns the maximum level of this scaler.
///
public virtual int MaxLevel
{
get => m_defaultSetting.maxLevel;
set
{
if (m_defaultSetting.maxLevel == value)
return;
m_defaultSetting.maxLevel = value;
}
}
///
/// Returns the minimum boundary of this scaler.
///
public virtual float MinBound
{
get => m_defaultSetting.minBound;
set
{
if (m_defaultSetting.minBound == value)
return;
m_defaultSetting.minBound = value;
}
}
///
/// Returns the maximum boundary of this scaler.
///
public virtual float MaxBound
{
get => m_defaultSetting.maxBound;
set
{
if (m_defaultSetting.maxBound == value)
return;
m_defaultSetting.maxBound = value;
}
}
///
/// Returns the current level of scale.
///
public int CurrentLevel { get; private set; }
///
/// Returns `true` if the scaler can no longer be applied, otherwise returns `false`.
///
public bool IsMaxLevel { get => CurrentLevel == MaxLevel; }
///
/// Returns `true` if the scaler is not applied, otherwise returns `false`.
///
public bool NotLeveled { get => CurrentLevel == 0; }
///
/// Scaler impact on GPU so far in milliseconds.
///
public int GpuImpact { get; internal set; }
///
/// Scaler impact on CPU so far in milliseconds.
///
public int CpuImpact { get; internal set; }
int m_OverrideLevel = -1;
AdaptivePerformanceScalerSettingsBase m_defaultSetting = new AdaptivePerformanceScalerSettingsBase();
#if VISUAL_SCRIPTING_ENABLED
AdaptivePerformanceScalerEvent m_ScalerEvent;
#endif
///
/// Settings reference for all scalers.
///
protected IAdaptivePerformanceSettings m_Settings;
///
/// Allows you to manually override the scaler level.
/// If the value is -1, will handle levels, otherwise scaler will be forced to the value you specify.
///
public int OverrideLevel
{
get => m_OverrideLevel;
set
{
m_OverrideLevel = value;
m_Indexer.UpdateOverrideLevel(this);
}
}
///
/// Calculate the cost of applying this particular scaler.
///
/// Cost of scaler ranges from 0 to infinity.
public int CalculateCost()
{
var bottleneck = Holder.Instance.PerformanceStatus.PerformanceMetrics.PerformanceBottleneck;
var cost = 0;
switch (VisualImpact)
{
case ScalerVisualImpact.Low:
cost += CurrentLevel * 1;
break;
case ScalerVisualImpact.Medium:
cost += CurrentLevel * 2;
break;
case ScalerVisualImpact.High:
cost += CurrentLevel * 3;
break;
}
// Bottleneck should always be best priority
if (bottleneck == PerformanceBottleneck.CPU && (Target & ScalerTarget.CPU) == 0)
cost = 6;
if (bottleneck == PerformanceBottleneck.GPU && (Target & ScalerTarget.GPU) == 0)
cost = 6;
if (bottleneck == PerformanceBottleneck.TargetFrameRate && (Target & ScalerTarget.FillRate) == 0)
cost = 6;
return cost;
}
///
/// Ensures settings are applied during startup.
///
protected virtual void Awake()
{
if (Holder.Instance == null)
return;
m_Settings = Holder.Instance.Settings;
m_Indexer = Holder.Instance.Indexer;
}
private void OnEnable()
{
if (m_Indexer == null)
return;
m_Indexer.AddScaler(this);
AdaptivePerformanceAnalytics.RegisterFeature(Name, Enabled);
AdaptivePerformanceAnalytics.SendAdaptiveFeatureUpdateEvent(Name, Enabled);
OnEnabled();
}
private void OnDisable()
{
if (m_Indexer == null)
return;
m_Indexer.RemoveScaler(this);
OnDisabled();
}
internal void IncreaseLevel()
{
if (IsMaxLevel)
{
Debug.LogError("Cannot increase scaler level as it is already max.");
return;
}
CurrentLevel++;
OnLevelIncrease();
OnLevel();
#if VISUAL_SCRIPTING_ENABLED
m_ScalerEvent.Name = Name;
m_ScalerEvent.Level = CurrentLevel;
EventBus.Trigger(AdaptivePerformanceEventHooks.OnScalerEvent, m_ScalerEvent);
#endif
}
internal void DecreaseLevel()
{
if (NotLeveled)
{
Debug.LogError("Cannot decrease scaler level as it is already 0.");
return;
}
CurrentLevel--;
OnLevelDecrease();
OnLevel();
#if VISUAL_SCRIPTING_ENABLED
m_ScalerEvent.Name = Name;
m_ScalerEvent.Level = CurrentLevel;
EventBus.Trigger(AdaptivePerformanceEventHooks.OnScalerEvent, m_ScalerEvent);
#endif
}
internal void Activate()
{
OnEnabled();
}
internal void Deactivate()
{
OnDisabled();
}
///
/// Any scaler with settings in needs to call this method and provide the scaler specific setting. Unity uses the setting arguments in the base-scaler as the default settings.
/// This is also used by Scaler Profiles to apply their Settings.
///
/// The settings to apply to the scaler.
public void ApplyDefaultSetting(AdaptivePerformanceScalerSettingsBase defaultSetting)
{
m_defaultSetting = defaultSetting;
}
///
/// Checks if scale changed based on the current level and returns true if scale changed false otherwise.
///
/// Returns true if scale changed false otherwise.
protected bool ScaleChanged()
{
float oldScaleFactor = Scale;
float scaleIncrement = (MaxBound - MinBound) / MaxLevel;
Scale = scaleIncrement * (MaxLevel - CurrentLevel) + MinBound;
return Scale != oldScaleFactor;
}
///
/// Callback for when the performance level is increased.
///
protected virtual void OnLevelIncrease() {}
///
/// Callback for when the performance level is decreased.
///
protected virtual void OnLevelDecrease() {}
///
/// Callback for any level change
///
protected virtual void OnLevel() {}
///
/// Callback when scaler gets enabled and added to the indexer
///
protected virtual void OnEnabled() {}
///
/// Callback when scaler gets disabled and removed from indexer
///
protected virtual void OnDisabled() {}
}
}