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() {} } }