DynamicResolutionHandler.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. using System;
  2. using System.Collections.Generic;
  3. namespace UnityEngine.Rendering
  4. {
  5. /// <summary>
  6. /// The format of the delegate used to perofrm dynamic resolution.
  7. /// </summary>
  8. public delegate float PerformDynamicRes();
  9. /// <summary>
  10. /// The type of dynamic resolution scaler. It essentially defines what the output of the scaler is expected to be.
  11. /// </summary>
  12. public enum DynamicResScalePolicyType
  13. {
  14. /// <summary>
  15. /// If is the option, DynamicResolutionHandler expects the scaler to return a screen percentage.
  16. /// The value set will be clamped between the minimum and maximum percentage set in the GlobalDynamicResolutionSettings.
  17. /// </summary>
  18. ReturnsPercentage,
  19. /// <summary>
  20. /// If is the option, DynamicResolutionHandler expects the scaler to return a factor t in the [0..1] such that the final resolution percentage
  21. /// is determined by lerp(minimumPercentage, maximumPercentage, t), where the minimum and maximum percentages are the one set in the GlobalDynamicResolutionSettings.
  22. /// </summary>
  23. ReturnsMinMaxLerpFactor
  24. }
  25. /// <summary>
  26. /// The class responsible to handle dynamic resolution.
  27. /// </summary>
  28. public class DynamicResolutionHandler
  29. {
  30. private bool m_Enabled;
  31. private float m_MinScreenFraction;
  32. private float m_MaxScreenFraction;
  33. private float m_CurrentFraction;
  34. private bool m_ForcingRes;
  35. private bool m_CurrentCameraRequest;
  36. private float m_PrevFraction;
  37. private bool m_ForceSoftwareFallback;
  38. private float m_PrevHWScaleWidth;
  39. private float m_PrevHWScaleHeight;
  40. private Vector2Int m_LastScaledSize;
  41. private void Reset()
  42. {
  43. m_Enabled = false;
  44. m_MinScreenFraction = 1.0f;
  45. m_MaxScreenFraction = 1.0f;
  46. m_CurrentFraction = 1.0f;
  47. m_ForcingRes = false;
  48. m_CurrentCameraRequest = true;
  49. m_PrevFraction = -1.0f;
  50. m_ForceSoftwareFallback = false;
  51. m_PrevHWScaleWidth = 1.0f;
  52. m_PrevHWScaleHeight = 1.0f;
  53. m_LastScaledSize = new Vector2Int(0, 0);
  54. filter = DynamicResUpscaleFilter.Bilinear;
  55. }
  56. private static DynamicResScalePolicyType s_ScalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor;
  57. private static PerformDynamicRes s_DynamicResMethod = DefaultDynamicResMethod;
  58. // Debug
  59. private Vector2Int cachedOriginalSize;
  60. /// <summary>
  61. /// The filter that is used to upscale the rendering result to the native resolution.
  62. /// </summary>
  63. public DynamicResUpscaleFilter filter { get; set; }
  64. /// <summary>
  65. /// The viewport of the final buffer. This is likely the resolution the dynamic resolution starts from before any scaling. Note this is NOT the target resolution the rendering will happen in
  66. /// but the resolution the scaled rendered result will be upscaled to.
  67. /// </summary>
  68. public Vector2Int finalViewport { get; set; }
  69. private DynamicResolutionType type;
  70. private GlobalDynamicResolutionSettings m_CachedSettings = GlobalDynamicResolutionSettings.NewDefault();
  71. private const int CameraDictionaryMaxcCapacity = 32;
  72. private WeakReference m_OwnerCameraWeakRef = null;
  73. private static Dictionary<int, DynamicResolutionHandler> s_CameraInstances = new Dictionary<int, DynamicResolutionHandler>(CameraDictionaryMaxcCapacity);
  74. private static DynamicResolutionHandler s_DefaultInstance = new DynamicResolutionHandler();
  75. private static int s_ActiveCameraId = 0;
  76. private static DynamicResolutionHandler s_ActiveInstance = s_DefaultInstance;
  77. //private global state of ScalableBufferManager
  78. private static bool s_ActiveInstanceDirty = true;
  79. private static float s_GlobalHwFraction = 1.0f;
  80. private static bool s_GlobalHwUpresActive = false;
  81. private bool FlushScalableBufferManagerState()
  82. {
  83. if (s_GlobalHwUpresActive == HardwareDynamicResIsEnabled() && s_GlobalHwFraction == m_CurrentFraction)
  84. return false;
  85. s_GlobalHwUpresActive = HardwareDynamicResIsEnabled();
  86. s_GlobalHwFraction = m_CurrentFraction;
  87. ScalableBufferManager.ResizeBuffers(s_GlobalHwFraction, s_GlobalHwFraction);
  88. return true;
  89. }
  90. private static DynamicResolutionHandler GetOrCreateDrsInstanceHandler(Camera camera)
  91. {
  92. if (camera == null)
  93. return null;
  94. DynamicResolutionHandler instance = null;
  95. var key = camera.GetInstanceID();
  96. if (!s_CameraInstances.TryGetValue(key, out instance))
  97. {
  98. //if this camera is not available in the map of cameras lets try creating one.
  99. //first and foremost, if we exceed the dictionary capacity, lets try and recycle an object that is dead.
  100. if (s_CameraInstances.Count >= CameraDictionaryMaxcCapacity)
  101. {
  102. int recycledInstanceKey = 0;
  103. DynamicResolutionHandler recycledInstance = null;
  104. foreach (var kv in s_CameraInstances)
  105. {
  106. //is this object dead? that is, belongs to a camera that was destroyed?
  107. if (kv.Value.m_OwnerCameraWeakRef == null || !kv.Value.m_OwnerCameraWeakRef.IsAlive)
  108. {
  109. recycledInstance = kv.Value;
  110. recycledInstanceKey = kv.Key;
  111. break;
  112. }
  113. }
  114. if (recycledInstance != null)
  115. {
  116. instance = recycledInstance;
  117. s_CameraInstances.Remove(recycledInstanceKey);
  118. }
  119. }
  120. //if we didnt find a dead object, we create one from scratch.
  121. if (instance == null)
  122. {
  123. instance = new DynamicResolutionHandler();
  124. instance.m_OwnerCameraWeakRef = new WeakReference(camera);
  125. }
  126. else
  127. {
  128. //otherwise, we found a dead object, lets reset it, and have a weak ref to this camera,
  129. //so we can possibly recycle it in the future by checking the camera's weak pointer state.
  130. instance.Reset();
  131. instance.m_OwnerCameraWeakRef.Target = camera;
  132. }
  133. s_CameraInstances.Add(key, instance);
  134. }
  135. return instance;
  136. }
  137. /// <summary>
  138. /// Get the instance of the global dynamic resolution handler.
  139. /// </summary>
  140. public static DynamicResolutionHandler instance { get { return s_ActiveInstance; } }
  141. private DynamicResolutionHandler()
  142. {
  143. Reset();
  144. }
  145. // TODO: Eventually we will need to provide a good default implementation for this.
  146. static private float DefaultDynamicResMethod()
  147. {
  148. return 1.0f;
  149. }
  150. private void ProcessSettings(GlobalDynamicResolutionSettings settings)
  151. {
  152. m_Enabled = settings.enabled && (Application.isPlaying || settings.forceResolution);
  153. if (!m_Enabled)
  154. {
  155. m_CurrentFraction = 1.0f;
  156. }
  157. else
  158. {
  159. type = settings.dynResType;
  160. float minScreenFrac = Mathf.Clamp(settings.minPercentage / 100.0f, 0.1f, 1.0f);
  161. m_MinScreenFraction = minScreenFrac;
  162. float maxScreenFrac = Mathf.Clamp(settings.maxPercentage / 100.0f, m_MinScreenFraction, 3.0f);
  163. m_MaxScreenFraction = maxScreenFrac;
  164. filter = settings.upsampleFilter;
  165. m_ForcingRes = settings.forceResolution;
  166. if (m_ForcingRes)
  167. {
  168. float fraction = Mathf.Clamp(settings.forcedPercentage / 100.0f, 0.1f, 1.5f);
  169. m_CurrentFraction = fraction;
  170. }
  171. }
  172. m_CachedSettings = settings;
  173. }
  174. public Vector2 GetResolvedScale()
  175. {
  176. if (!m_Enabled || !m_CurrentCameraRequest)
  177. {
  178. return new Vector2(1.0f, 1.0f);
  179. }
  180. float scaleFractionX = m_CurrentFraction;
  181. float scaleFractionY = m_CurrentFraction;
  182. if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
  183. {
  184. scaleFractionX = ScalableBufferManager.widthScaleFactor;
  185. scaleFractionY = ScalableBufferManager.heightScaleFactor;
  186. }
  187. return new Vector2(scaleFractionX, scaleFractionY);
  188. }
  189. /// <summary>
  190. /// Set the scaler method used to drive dynamic resolution.
  191. /// </summary>
  192. /// <param name="scaler">The delegate used to determine the resolution percentage used by the dynamic resolution system.</param>
  193. /// <param name="scalerType">The type of scaler that is used, this is used to indicate the return type of the scaler to the dynamic resolution system.</param>
  194. static public void SetDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
  195. {
  196. s_ScalerType = scalerType;
  197. s_DynamicResMethod = scaler;
  198. }
  199. /// <summary>
  200. /// Will clear the currently used camera. Use this function to restore the default instance when UpdateAndUseCamera is called.
  201. /// </summary>
  202. public static void ClearSelectedCamera()
  203. {
  204. s_ActiveInstance = s_DefaultInstance;
  205. s_ActiveCameraId = 0;
  206. s_ActiveInstanceDirty = true;
  207. }
  208. /// <summary>
  209. /// Set whether the camera that is currently processed by the pipeline has requested dynamic resolution or not.
  210. /// </summary>
  211. /// <param name="cameraRequest">Determines whether the camera has requested dynamic resolution or not.</param>
  212. public void SetCurrentCameraRequest(bool cameraRequest)
  213. {
  214. m_CurrentCameraRequest = cameraRequest;
  215. }
  216. /// <summary>
  217. /// Update the state of the dynamic resolution system for a specific camera.
  218. /// Call this function also to switch context between cameras (will set the current camera as active).
  219. // Passing a null camera has the same effect as calling Update without the camera parameter.
  220. /// </summary>
  221. /// <param name="camera">Camera used to select a specific instance tied to this DynamicResolutionHandler instance.
  222. /// </param>
  223. /// <param name="settings">(optional) The settings that are to be used by the dynamic resolution system. passing null for the settings will result in the last update's settings used.</param>
  224. /// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
  225. public static void UpdateAndUseCamera(Camera camera, GlobalDynamicResolutionSettings? settings = null, Action OnResolutionChange = null)
  226. {
  227. int newCameraId;
  228. if (camera == null)
  229. {
  230. s_ActiveInstance = s_DefaultInstance;
  231. newCameraId = 0;
  232. }
  233. else
  234. {
  235. s_ActiveInstance = GetOrCreateDrsInstanceHandler(camera);
  236. newCameraId = camera.GetInstanceID();
  237. }
  238. s_ActiveInstanceDirty = newCameraId != s_ActiveCameraId;
  239. s_ActiveCameraId = newCameraId;
  240. s_ActiveInstance.Update(settings.HasValue ? settings.Value : s_ActiveInstance.m_CachedSettings, OnResolutionChange);
  241. }
  242. /// <summary>
  243. /// Update the state of the dynamic resolution system.
  244. /// </summary>
  245. /// <param name="settings">The settings that are to be used by the dynamic resolution system.</param>
  246. /// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
  247. public void Update(GlobalDynamicResolutionSettings settings, Action OnResolutionChange = null)
  248. {
  249. ProcessSettings(settings);
  250. if (!m_Enabled && !s_ActiveInstanceDirty)
  251. {
  252. s_ActiveInstanceDirty = false;
  253. return;
  254. }
  255. if (!m_ForcingRes)
  256. {
  257. if (s_ScalerType == DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
  258. {
  259. float currLerp = s_DynamicResMethod();
  260. float lerpFactor = Mathf.Clamp(currLerp, 0.0f, 1.0f);
  261. m_CurrentFraction = Mathf.Lerp(m_MinScreenFraction, m_MaxScreenFraction, lerpFactor);
  262. }
  263. else if (s_ScalerType == DynamicResScalePolicyType.ReturnsPercentage)
  264. {
  265. float percentageRequested = Mathf.Max(s_DynamicResMethod(), 5.0f);
  266. m_CurrentFraction = Mathf.Clamp(percentageRequested / 100.0f, m_MinScreenFraction, m_MaxScreenFraction);
  267. }
  268. }
  269. bool hardwareResolutionChanged = false;
  270. bool softwareResolutionChanged = m_CurrentFraction != m_PrevFraction;
  271. m_PrevFraction = m_CurrentFraction;
  272. if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
  273. {
  274. hardwareResolutionChanged = FlushScalableBufferManagerState();
  275. if (ScalableBufferManager.widthScaleFactor != m_PrevHWScaleWidth ||
  276. ScalableBufferManager.heightScaleFactor != m_PrevHWScaleHeight)
  277. {
  278. hardwareResolutionChanged = true;
  279. }
  280. }
  281. if ((softwareResolutionChanged || hardwareResolutionChanged) && OnResolutionChange != null)
  282. OnResolutionChange();
  283. s_ActiveInstanceDirty = false;
  284. m_PrevHWScaleWidth = ScalableBufferManager.widthScaleFactor;
  285. m_PrevHWScaleHeight = ScalableBufferManager.heightScaleFactor;
  286. }
  287. /// <summary>
  288. /// Determines whether software dynamic resolution is enabled or not.
  289. /// </summary>
  290. /// <returns>True: Software dynamic resolution is enabled</returns>
  291. public bool SoftwareDynamicResIsEnabled()
  292. {
  293. return m_CurrentCameraRequest && m_Enabled && m_CurrentFraction != 1.0f && (m_ForceSoftwareFallback || type == DynamicResolutionType.Software);
  294. }
  295. /// <summary>
  296. /// Determines whether hardware dynamic resolution is enabled or not.
  297. /// </summary>
  298. /// <returns>True: Hardware dynamic resolution is enabled</returns>
  299. public bool HardwareDynamicResIsEnabled()
  300. {
  301. return !m_ForceSoftwareFallback && m_CurrentCameraRequest && m_Enabled && type == DynamicResolutionType.Hardware;
  302. }
  303. /// <summary>
  304. /// Identifies whether hardware dynamic resolution has been requested and is going to be used.
  305. /// </summary>
  306. /// <returns>True: Hardware dynamic resolution is requested by user and software fallback has not been forced</returns>
  307. public bool RequestsHardwareDynamicResolution()
  308. {
  309. if (m_ForceSoftwareFallback)
  310. return false;
  311. return type == DynamicResolutionType.Hardware;
  312. }
  313. /// <summary>
  314. /// Identifies whether dynamic resolution is enabled and scaling the render targets.
  315. /// </summary>
  316. /// <returns>True: Dynamic resolution is enabled.</returns>
  317. public bool DynamicResolutionEnabled()
  318. {
  319. return m_CurrentCameraRequest && m_Enabled && m_CurrentFraction != 1.0f;
  320. }
  321. /// <summary>
  322. /// 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.
  323. /// </summary>
  324. public void ForceSoftwareFallback()
  325. {
  326. m_ForceSoftwareFallback = true;
  327. }
  328. /// <summary>
  329. /// Applies to the passed size the scale imposed by the dynamic resolution system.
  330. /// Note: this function has the side effect of caching the last scale size.
  331. /// </summary>
  332. /// <param name="size">The starting size of the render target that will be scaled by dynamic resolution.</param>
  333. /// <returns>The parameter size scaled by the dynamic resolution system.</returns>
  334. public Vector2Int GetScaledSize(Vector2Int size)
  335. {
  336. cachedOriginalSize = size;
  337. if (!m_Enabled || !m_CurrentCameraRequest)
  338. {
  339. return size;
  340. }
  341. Vector2Int scaledSize = ApplyScalesOnSize(size);
  342. m_LastScaledSize = scaledSize;
  343. return scaledSize;
  344. }
  345. /// <summary>
  346. /// Applies to the passed size the scale imposed by the dynamic resolution system.
  347. /// Note: this function is pure (has no side effects), this function does not cache the pre-scale size
  348. /// </summary>
  349. /// <param name="size">The size to apply the scaling</param>
  350. /// <returns>The parameter size scaled by the dynamic resolution system.</returns>
  351. public Vector2Int ApplyScalesOnSize(Vector2Int size)
  352. {
  353. Vector2 resolvedScales = GetResolvedScale();
  354. Vector2Int scaledSize = new Vector2Int(Mathf.CeilToInt(size.x * resolvedScales.x), Mathf.CeilToInt(size.y * resolvedScales.y));
  355. if (m_ForceSoftwareFallback || type != DynamicResolutionType.Hardware)
  356. {
  357. scaledSize.x += (1 & scaledSize.x);
  358. scaledSize.y += (1 & scaledSize.y);
  359. }
  360. return scaledSize;
  361. }
  362. /// <summary>
  363. /// Returns the scale that is currently applied by the dynamic resolution system.
  364. /// </summary>
  365. /// <returns>The scale that is currently applied by the dynamic resolution system.</returns>
  366. public float GetCurrentScale()
  367. {
  368. return (m_Enabled && m_CurrentCameraRequest) ? m_CurrentFraction : 1.0f;
  369. }
  370. /// <summary>
  371. /// Returns the latest scaled size that has been produced by GetScaledSize.
  372. /// </summary>
  373. /// <returns>The latest scaled size that has been produced by GetScaledSize.</returns>
  374. public Vector2Int GetLastScaledSize()
  375. {
  376. return m_LastScaledSize;
  377. }
  378. }
  379. }