using System; namespace UnityEngine.Rendering { /// /// The format of the delegate used to perofrm dynamic resolution. /// public delegate float PerformDynamicRes(); /// /// The type of dynamic resolution scaler. It essentially defines what the output of the scaler is expected to be. /// public enum DynamicResScalePolicyType { /// /// If is the option, DynamicResolutionHandler expects the scaler to return a screen percentage. /// The value set will be clamped between the minimum and maximum percentage set in the GlobalDynamicResolutionSettings. /// ReturnsPercentage, /// /// If is the option, DynamicResolutionHandler expects the scaler to return a factor t in the [0..1] such that the final resolution percentage /// is determined by lerp(minimumPercentage, maximumPercentage, t), where the minimum and maximum percentages are the one set in the GlobalDynamicResolutionSettings. /// ReturnsMinMaxLerpFactor } /// /// The class responsible to handle dynamic resolution. /// public class DynamicResolutionHandler { private bool m_Enabled = false; private float m_MinScreenFraction = 1.0f; private float m_MaxScreenFraction = 1.0f; private float m_CurrentFraction = 1.0f; private float m_PrevFraction = -1.0f; private bool m_ForcingRes = false; private bool m_CurrentCameraRequest = true; private bool m_ForceSoftwareFallback = false; private float m_PrevHWScaleWidth = 1.0f; private float m_PrevHWScaleHeight = 1.0f; private Vector2Int m_LastScaledSize = new Vector2Int(0, 0); private DynamicResScalePolicyType m_ScalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor; // Debug private Vector2Int cachedOriginalSize; /// /// The filter that is used to upscale the rendering result to the native resolution. /// public DynamicResUpscaleFilter filter { get; set; } private DynamicResolutionType type; private PerformDynamicRes m_DynamicResMethod = null; private static DynamicResolutionHandler s_Instance = new DynamicResolutionHandler(); /// /// Get the instance of the global dynamic resolution handler. /// public static DynamicResolutionHandler instance { get { return s_Instance; } } private DynamicResolutionHandler() { m_DynamicResMethod = DefaultDynamicResMethod; filter = DynamicResUpscaleFilter.Bilinear; } // TODO: Eventually we will need to provide a good default implementation for this. static private float DefaultDynamicResMethod() { return 1.0f; } private void ProcessSettings(GlobalDynamicResolutionSettings settings) { m_Enabled = settings.enabled; if (!m_Enabled) { m_CurrentFraction = 1.0f; } else { type = settings.dynResType; float minScreenFrac = Mathf.Clamp(settings.minPercentage / 100.0f, 0.1f, 1.0f); m_MinScreenFraction = minScreenFrac; float maxScreenFrac = Mathf.Clamp(settings.maxPercentage / 100.0f, m_MinScreenFraction, 3.0f); m_MaxScreenFraction = maxScreenFrac; filter = settings.upsampleFilter; m_ForcingRes = settings.forceResolution; if (m_ForcingRes) { float fraction = Mathf.Clamp(settings.forcedPercentage / 100.0f, 0.1f, 1.5f); m_CurrentFraction = fraction; } } } /// /// Set the scaler method used to drive dynamic resolution. /// /// The delegate used to determine the resolution percentage used by the dynamic resolution system. /// The type of scaler that is used, this is used to indicate the return type of the scaler to the dynamic resolution system. static public void SetDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor) { s_Instance.m_ScalerType = scalerType; s_Instance.m_DynamicResMethod = scaler; } /// /// Set whether the camera that is currently processed by the pipeline has requested dynamic resolution or not. /// /// Determines whether the camera has requested dynamic resolution or not. public void SetCurrentCameraRequest(bool cameraRequest) { m_CurrentCameraRequest = cameraRequest; } /// /// Update the state of the dynamic resolution system. /// /// The settings that are to be used by the dynamic resolution system. /// An action that will be called every time the dynamic resolution system triggers a change in resolution. public void Update(GlobalDynamicResolutionSettings settings, Action OnResolutionChange = null) { ProcessSettings(settings); if (!m_Enabled) return; if (!m_ForcingRes) { if(m_ScalerType == DynamicResScalePolicyType.ReturnsMinMaxLerpFactor) { float currLerp = m_DynamicResMethod(); float lerpFactor = Mathf.Clamp(currLerp, 0.0f, 1.0f); m_CurrentFraction = Mathf.Lerp(m_MinScreenFraction, m_MaxScreenFraction, lerpFactor); } else if(m_ScalerType == DynamicResScalePolicyType.ReturnsPercentage) { float percentageRequested = Mathf.Max(m_DynamicResMethod(), 5.0f); m_CurrentFraction = Mathf.Clamp(percentageRequested / 100.0f, m_MinScreenFraction, m_MaxScreenFraction); } } if (m_CurrentFraction != m_PrevFraction) { m_PrevFraction = m_CurrentFraction; if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware) { ScalableBufferManager.ResizeBuffers(m_CurrentFraction, m_CurrentFraction); } OnResolutionChange(); } else { // Unity can change the scale factor by itself so we need to trigger the Action if that happens as well. if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware) { if(ScalableBufferManager.widthScaleFactor != m_PrevHWScaleWidth || ScalableBufferManager.heightScaleFactor != m_PrevHWScaleHeight) { OnResolutionChange(); } } } m_PrevHWScaleWidth = ScalableBufferManager.widthScaleFactor; m_PrevHWScaleHeight = ScalableBufferManager.heightScaleFactor; } /// /// Determines whether software dynamic resolution is enabled or not. /// /// True: Software dynamic resolution is enabled public bool SoftwareDynamicResIsEnabled() { return m_CurrentCameraRequest && m_Enabled && m_CurrentFraction != 1.0f && (m_ForceSoftwareFallback || type == DynamicResolutionType.Software); } /// /// Determines whether hardware dynamic resolution is enabled or not. /// /// True: Hardware dynamic resolution is enabled public bool HardwareDynamicResIsEnabled() { return !m_ForceSoftwareFallback && m_CurrentCameraRequest && m_Enabled && type == DynamicResolutionType.Hardware; } /// /// Identifies whether hardware dynamic resolution has been requested and is going to be used. /// /// True: Hardware dynamic resolution is requested by user and software fallback has not been forced public bool RequestsHardwareDynamicResolution() { if (m_ForceSoftwareFallback) return false; return type == DynamicResolutionType.Hardware; } /// /// Identifies whether dynamic resolution is enabled and scaling the render targets. /// /// True: Dynamic resolution is enabled. public bool DynamicResolutionEnabled() { return m_CurrentCameraRequest && m_Enabled && m_CurrentFraction != 1.0f; } /// /// Forces software fallback for dynamic resolution. Needs to be called in case Hardware dynamic resolution is requested by the user, but not supported by the platform. /// public void ForceSoftwareFallback() { m_ForceSoftwareFallback = true; } /// /// Applies to the passed size the scale imposed by the dynamic resolution system. /// /// The starting size of the render target that will be scaled by dynamic resolution. /// The parameter size scaled by the dynamic resolution system. public Vector2Int GetScaledSize(Vector2Int size) { cachedOriginalSize = size; if (!m_Enabled || !m_CurrentCameraRequest) { return size; } float scaleFractionX = m_CurrentFraction; float scaleFractionY = m_CurrentFraction; if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware) { scaleFractionX = ScalableBufferManager.widthScaleFactor; scaleFractionY = ScalableBufferManager.heightScaleFactor; } Vector2Int scaledSize = new Vector2Int(Mathf.CeilToInt(size.x * scaleFractionX), Mathf.CeilToInt(size.y * scaleFractionY)); if (m_ForceSoftwareFallback || type != DynamicResolutionType.Hardware) { scaledSize.x += (1 & scaledSize.x); scaledSize.y += (1 & scaledSize.y); } m_LastScaledSize = scaledSize; return scaledSize; } /// /// Returns the scale that is currently applied by the dynamic resolution system. /// /// The scale that is currently applied by the dynamic resolution system. public float GetCurrentScale() { return (m_Enabled && m_CurrentCameraRequest) ? m_CurrentFraction : 1.0f; } /// /// Returns the latest scaled size that has been produced by GetScaledSize. /// /// The latest scaled size that has been produced by GetScaledSize. public Vector2Int GetLastScaledSize() { return m_LastScaledSize; } } }