A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3
4namespace UnityEngine.Rendering
5{
6 /// <summary>
7 /// The format of the delegate used to perofrm dynamic resolution.
8 /// </summary>
9 /// <returns>A float value representing the scale factor for dynamic resolution.</returns>
10 public delegate float PerformDynamicRes();
11
12 /// <summary>
13 /// The type of dynamic resolution scaler. It essentially defines what the output of the scaler is expected to be.
14 /// </summary>
15 public enum DynamicResScalePolicyType
16 {
17 /// <summary>
18 /// If is the option, DynamicResolutionHandler expects the scaler to return a screen percentage.
19 /// The value set will be clamped between the minimum and maximum percentage set in the GlobalDynamicResolutionSettings.
20 /// </summary>
21 ReturnsPercentage,
22 /// <summary>
23 /// If is the option, DynamicResolutionHandler expects the scaler to return a factor t in the [0..1] such that the final resolution percentage
24 /// is determined by lerp(minimumPercentage, maximumPercentage, t), where the minimum and maximum percentages are the one set in the GlobalDynamicResolutionSettings.
25 /// </summary>
26 ReturnsMinMaxLerpFactor
27 }
28
29 /// <summary>
30 /// The source slots for dynamic resolution scaler. Defines registers were the scalers assigned are stored. By default the User one is always used
31 /// </summary>
32 public enum DynamicResScalerSlot
33 {
34 /// <summary> Scaler slot set by the function SetDynamicResScaler</summary>
35 User,
36 /// <summary> Scaler slot set by the function SetSystemDynamicResScaler</summary>
37 System,
38 /// <summary> total number of scaler slots </summary>
39 Count
40 }
41
42 /// <summary>
43 /// The class responsible to handle dynamic resolution.
44 /// </summary>
45 public class DynamicResolutionHandler
46 {
47 private bool m_Enabled;
48 private bool m_UseMipBias;
49 private float m_MinScreenFraction;
50 private float m_MaxScreenFraction;
51 private float m_CurrentFraction;
52 private bool m_ForcingRes;
53 private bool m_CurrentCameraRequest;
54 private float m_PrevFraction;
55 private bool m_ForceSoftwareFallback;
56 private bool m_RunUpscalerFilterOnFullResolution;
57
58 private float m_PrevHWScaleWidth;
59 private float m_PrevHWScaleHeight;
60 private Vector2Int m_LastScaledSize;
61
62 private void Reset()
63 {
64 m_Enabled = false;
65 m_UseMipBias = false;
66 m_MinScreenFraction = 1.0f;
67 m_MaxScreenFraction = 1.0f;
68 m_CurrentFraction = 1.0f;
69 m_ForcingRes = false;
70 m_CurrentCameraRequest = true;
71 m_PrevFraction = -1.0f;
72 m_ForceSoftwareFallback = false;
73 m_RunUpscalerFilterOnFullResolution = false;
74
75 m_PrevHWScaleWidth = 1.0f;
76 m_PrevHWScaleHeight = 1.0f;
77 m_LastScaledSize = new Vector2Int(0, 0);
78 filter = DynamicResUpscaleFilter.CatmullRom;
79 }
80
81 private struct ScalerContainer
82 {
83 public DynamicResScalePolicyType type;
84 public PerformDynamicRes method;
85 }
86
87 private static DynamicResScalerSlot s_ActiveScalerSlot = DynamicResScalerSlot.User;
88 private static ScalerContainer[] s_ScalerContainers = new ScalerContainer[(int)DynamicResScalerSlot.Count]
89 {
90 new ScalerContainer() { type = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor, method = DefaultDynamicResMethod },
91 new ScalerContainer() { type = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor, method = DefaultDynamicResMethod }
92 };
93
94 // Debug
95 private Vector2Int cachedOriginalSize;
96
97 /// <summary>
98 /// The filter that is used to upscale the rendering result to the native resolution.
99 /// </summary>
100 public DynamicResUpscaleFilter filter { get; private set; }
101
102 // Used to detect the filters set via user API
103 static Dictionary<int, DynamicResUpscaleFilter> s_CameraUpscaleFilters = new Dictionary<int, DynamicResUpscaleFilter>();
104
105 /// <summary>
106 /// 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
107 /// but the resolution the scaled rendered result will be upscaled to.
108 /// </summary>
109 public Vector2Int finalViewport { get; set; }
110
111 /// <summary>
112 /// By default, dynamic resolution scaling is turned off automatically when the source matches the final viewport (100% scale).
113 /// That is, DynamicResolutionEnabled and SoftwareDynamicResIsEnabled will return false if the scale is 100%.
114 /// For certain upscalers, we dont want this behavior since they could possibly include anti aliasing and other quality improving post processes.
115 /// Setting this to true will eliminate this behavior.
116 /// Note: when the EdgeAdaptiveScalingUpres (FSR 1.0) filter is set, this will cause this parameter to always be true.
117 /// </summary>
118 public bool runUpscalerFilterOnFullResolution
119 {
120 set { m_RunUpscalerFilterOnFullResolution = value; }
121 get { return m_RunUpscalerFilterOnFullResolution || (filter == DynamicResUpscaleFilter.EdgeAdaptiveScalingUpres); }
122 }
123
124 /// <summary>
125 /// True if the resolution is being forced to a fixed value rather than varying dynamically within a range. Otherwise, false.
126 /// </summary>
127 public bool forcingResolution { get { return m_ForcingRes; } }
128
129 private DynamicResolutionType type;
130
131 private GlobalDynamicResolutionSettings m_CachedSettings = GlobalDynamicResolutionSettings.NewDefault();
132
133 private const int CameraDictionaryMaxcCapacity = 32;
134 private WeakReference m_OwnerCameraWeakRef = null;
135 private static Dictionary<int, DynamicResolutionHandler> s_CameraInstances = new Dictionary<int, DynamicResolutionHandler>(CameraDictionaryMaxcCapacity);
136 private static DynamicResolutionHandler s_DefaultInstance = new DynamicResolutionHandler();
137
138 private static int s_ActiveCameraId = 0;
139 private static DynamicResolutionHandler s_ActiveInstance = s_DefaultInstance;
140
141 //private global state of ScalableBufferManager
142 private static bool s_ActiveInstanceDirty = true;
143 private static float s_GlobalHwFraction = 1.0f;
144 private static bool s_GlobalHwUpresActive = false;
145
146 private bool FlushScalableBufferManagerState()
147 {
148 if (s_GlobalHwUpresActive == HardwareDynamicResIsEnabled() && s_GlobalHwFraction == m_CurrentFraction)
149 return false;
150
151 s_GlobalHwUpresActive = HardwareDynamicResIsEnabled();
152 s_GlobalHwFraction = m_CurrentFraction;
153 float currentFraction = s_GlobalHwUpresActive ? s_GlobalHwFraction : 1.0f;
154 ScalableBufferManager.ResizeBuffers(currentFraction, currentFraction);
155 return true;
156 }
157
158 private static DynamicResolutionHandler GetOrCreateDrsInstanceHandler(Camera camera)
159 {
160 if (camera == null)
161 return null;
162
163 DynamicResolutionHandler instance = null;
164 var key = camera.GetInstanceID();
165 if (!s_CameraInstances.TryGetValue(key, out instance))
166 {
167 //if this camera is not available in the map of cameras lets try creating one.
168
169 //first and foremost, if we exceed the dictionary capacity, lets try and recycle an object that is dead.
170 if (s_CameraInstances.Count >= CameraDictionaryMaxcCapacity)
171 {
172 int recycledInstanceKey = 0;
173 DynamicResolutionHandler recycledInstance = null;
174 foreach (var kv in s_CameraInstances)
175 {
176 //is this object dead? that is, belongs to a camera that was destroyed?
177 if (kv.Value.m_OwnerCameraWeakRef == null || !kv.Value.m_OwnerCameraWeakRef.IsAlive)
178 {
179 recycledInstance = kv.Value;
180 recycledInstanceKey = kv.Key;
181 break;
182 }
183 }
184
185 if (recycledInstance != null)
186 {
187 instance = recycledInstance;
188 s_CameraInstances.Remove(recycledInstanceKey);
189 s_CameraUpscaleFilters.Remove(recycledInstanceKey);
190 }
191 }
192
193 //if we didnt find a dead object, we create one from scratch.
194 if (instance == null)
195 {
196 instance = new DynamicResolutionHandler();
197 instance.m_OwnerCameraWeakRef = new WeakReference(camera);
198 }
199 else
200 {
201 //otherwise, we found a dead object, lets reset it, and have a weak ref to this camera,
202 //so we can possibly recycle it in the future by checking the camera's weak pointer state.
203 instance.Reset();
204 instance.m_OwnerCameraWeakRef.Target = camera;
205 }
206
207 s_CameraInstances.Add(key, instance);
208 }
209 return instance;
210 }
211
212 /// <summary>
213 /// The scheduling mechanism to apply upscaling.
214 /// </summary>
215 public enum UpsamplerScheduleType
216 {
217 /// <summary>
218 /// Indicates that upscaling must happen before post processing.
219 /// This means that everything runs at the source resolution during rasterization, and post processes will
220 /// run at full resolution. Ideal for temporal upscalers.
221 /// </summary>
222 BeforePost,
223
224 /// <summary>
225 /// Indicates that upscaling should happen after depth of field but before other post processing.
226 /// This means that everything runs at the source resolution during rasterization and depth of field, and other post processes
227 /// will run at full resolution. More performant alternative for temporal upscalers at the expense of reduced image quality.
228 /// </summary>
229 AfterDepthOfField,
230
231 /// <summary>
232 /// Indicates that upscaling must happen after post processing.
233 /// This means that everything in the frame runs at the source resolution, and upscaling happens after
234 /// the final pass. This is ideal for spatial upscalers.
235 /// </summary>
236 AfterPost
237 }
238
239 private UpsamplerScheduleType m_UpsamplerSchedule = UpsamplerScheduleType.AfterPost;
240
241 /// <summary>
242 /// Property that sets / gets the state of the upscaling schedule.
243 /// This must be set at the beginning of the frame, once per camera.
244 /// </summary>
245 public UpsamplerScheduleType upsamplerSchedule { set { m_UpsamplerSchedule = value; } get { return m_UpsamplerSchedule; } }
246
247
248 /// <summary>
249 /// Get the instance of the global dynamic resolution handler.
250 /// </summary>
251 public static DynamicResolutionHandler instance { get { return s_ActiveInstance; } }
252
253
254 private DynamicResolutionHandler()
255 {
256 Reset();
257 }
258
259 // TODO: Eventually we will need to provide a good default implementation for this.
260 static private float DefaultDynamicResMethod()
261 {
262 return 1.0f;
263 }
264
265 private void ProcessSettings(GlobalDynamicResolutionSettings settings)
266 {
267 m_Enabled = settings.enabled && (Application.isPlaying || settings.forceResolution);
268
269 if (!m_Enabled)
270 {
271 m_CurrentFraction = 1.0f;
272 }
273 else
274 {
275 type = settings.dynResType;
276 m_UseMipBias = settings.useMipBias;
277 float minScreenFrac = Mathf.Clamp(settings.minPercentage / 100.0f, 0.1f, 1.0f);
278 m_MinScreenFraction = minScreenFrac;
279 float maxScreenFrac = Mathf.Clamp(settings.maxPercentage / 100.0f, m_MinScreenFraction, 3.0f);
280 m_MaxScreenFraction = maxScreenFrac;
281
282 // Check if a filter has been set via user API, if so we use that, otherwise we use the default from the GlobalDynamicResolutionSettings
283 bool hasUserRequestedFilter = s_CameraUpscaleFilters.TryGetValue(s_ActiveCameraId, out DynamicResUpscaleFilter requestedFilter);
284
285 filter = hasUserRequestedFilter ? requestedFilter : settings.upsampleFilter;
286 m_ForcingRes = settings.forceResolution;
287
288 if (m_ForcingRes)
289 {
290 float fraction = Mathf.Clamp(settings.forcedPercentage / 100.0f, 0.1f, 1.5f);
291 m_CurrentFraction = fraction;
292 }
293 }
294 m_CachedSettings = settings;
295 }
296
297 /// <summary>
298 /// Gets the resolved scale
299 /// </summary>
300 /// <returns>The resolved scale in form of <see cref="Vector2"/></returns>
301 public Vector2 GetResolvedScale()
302 {
303 if (!m_Enabled || !m_CurrentCameraRequest)
304 {
305 return new Vector2(1.0f, 1.0f);
306 }
307
308 float scaleFractionX = m_CurrentFraction;
309 float scaleFractionY = m_CurrentFraction;
310 if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
311 {
312 scaleFractionX = ScalableBufferManager.widthScaleFactor;
313 scaleFractionY = ScalableBufferManager.heightScaleFactor;
314 }
315
316 return new Vector2(scaleFractionX, scaleFractionY);
317 }
318
319 /// <summary>
320 /// Returns the mip bias to apply in the rendering pipeline. This mip bias helps bring detail since sampling of textures occurs at the target rate.
321 /// </summary>
322 /// <param name="inputResolution">The input width x height resolution in pixels.</param>
323 /// <param name="outputResolution">The output width x height resolution in pixels.</param>
324 /// <param name="forceApply">False by default. If true, we ignore the useMipBias setting and return a mip bias regardless.</param>
325 /// <returns>The calculated mip bias</returns>
326 public float CalculateMipBias(Vector2Int inputResolution, Vector2Int outputResolution, bool forceApply = false)
327 {
328 if (!m_UseMipBias && !forceApply)
329 return 0.0f;
330
331 return (float)Math.Log((double)inputResolution.x / (double)outputResolution.x, 2.0);
332 }
333
334 /// <summary>
335 /// Set the scaler method used to drive dynamic resolution by the user.
336 /// </summary>
337 /// <param name="scaler">The delegate used to determine the resolution percentage used by the dynamic resolution system.</param>
338 /// <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>
339 static public void SetDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
340 {
341 s_ScalerContainers[(int)DynamicResScalerSlot.User] = new ScalerContainer() { type = scalerType, method = scaler };
342 }
343
344 /// <summary>
345 /// Set the scaler method used to drive dynamic resolution internally from the Scriptable Rendering Pipeline. This function should only be called by Scriptable Rendering Pipeline.
346 /// </summary>
347 /// <param name="scaler">The delegate used to determine the resolution percentage used by the dynamic resolution system.</param>
348 /// <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>
349 static public void SetSystemDynamicResScaler(PerformDynamicRes scaler, DynamicResScalePolicyType scalerType = DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
350 {
351 s_ScalerContainers[(int)DynamicResScalerSlot.System] = new ScalerContainer() { type = scalerType, method = scaler };
352 }
353
354 /// <summary>
355 /// Sets the active dynamic scaler slot to be used by the runtime when calculating frame resolutions.
356 /// See DynamicResScalerSlot for more information.
357 /// </summary>
358 /// <param name="slot">The scaler to be selected and used by the runtime.</param>
359 static public void SetActiveDynamicScalerSlot(DynamicResScalerSlot slot)
360 {
361 s_ActiveScalerSlot = slot;
362 }
363
364 /// <summary>
365 /// Will clear the currently used camera. Use this function to restore the default instance when UpdateAndUseCamera is called.
366 /// </summary>
367 public static void ClearSelectedCamera()
368 {
369 s_ActiveInstance = s_DefaultInstance;
370 s_ActiveCameraId = 0;
371 s_ActiveInstanceDirty = true;
372 }
373
374 /// <summary>
375 /// Set the Upscale filter used by the camera when dynamic resolution is run.
376 /// </summary>
377 /// <param name="camera">The camera for which the upscale filter is set.</param>
378 /// <param name="filter">The filter to be used by the camera to upscale to final resolution.</param>
379 static public void SetUpscaleFilter(Camera camera, DynamicResUpscaleFilter filter)
380 {
381 var cameraID = camera.GetInstanceID();
382 if (s_CameraUpscaleFilters.ContainsKey(cameraID))
383 {
384 s_CameraUpscaleFilters[cameraID] = filter;
385 }
386 else
387 {
388 s_CameraUpscaleFilters.Add(cameraID, filter);
389 }
390 }
391
392 /// <summary>
393 /// Set whether the camera that is currently processed by the pipeline has requested dynamic resolution or not.
394 /// </summary>
395 /// <param name="cameraRequest">Determines whether the camera has requested dynamic resolution or not.</param>
396 public void SetCurrentCameraRequest(bool cameraRequest)
397 {
398 m_CurrentCameraRequest = cameraRequest;
399 }
400
401 /// <summary>
402 /// Update the state of the dynamic resolution system for a specific camera.
403 /// Call this function also to switch context between cameras (will set the current camera as active).
404 /// Passing a null camera has the same effect as calling Update without the camera parameter.
405 /// </summary>
406 /// <param name="camera">Camera used to select a specific instance tied to this DynamicResolutionHandler instance.</param>
407 /// <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>
408 /// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
409 public static void UpdateAndUseCamera(Camera camera, GlobalDynamicResolutionSettings? settings = null, Action OnResolutionChange = null)
410 {
411 int newCameraId;
412 if (camera == null)
413 {
414 s_ActiveInstance = s_DefaultInstance;
415 newCameraId = 0;
416 }
417 else
418 {
419 s_ActiveInstance = GetOrCreateDrsInstanceHandler(camera);
420 newCameraId = camera.GetInstanceID();
421 }
422
423 s_ActiveInstanceDirty = newCameraId != s_ActiveCameraId;
424 s_ActiveCameraId = newCameraId;
425 s_ActiveInstance.Update(settings.HasValue ? settings.Value : s_ActiveInstance.m_CachedSettings, OnResolutionChange);
426 }
427
428 /// <summary>
429 /// Update the state of the dynamic resolution system.
430 /// </summary>
431 /// <param name="settings">The settings that are to be used by the dynamic resolution system.</param>
432 /// <param name="OnResolutionChange">An action that will be called every time the dynamic resolution system triggers a change in resolution.</param>
433 public void Update(GlobalDynamicResolutionSettings settings, Action OnResolutionChange = null)
434 {
435 ProcessSettings(settings);
436
437 if (!m_Enabled || !s_ActiveInstanceDirty)
438 {
439 FlushScalableBufferManagerState();
440 s_ActiveInstanceDirty = false;
441 return;
442 }
443
444 if (!m_ForcingRes)
445 {
446 ref ScalerContainer scaler = ref s_ScalerContainers[(int)s_ActiveScalerSlot];
447 if (scaler.type == DynamicResScalePolicyType.ReturnsMinMaxLerpFactor)
448 {
449 float currLerp = scaler.method();
450 float lerpFactor = Mathf.Clamp(currLerp, 0.0f, 1.0f);
451 m_CurrentFraction = Mathf.Lerp(m_MinScreenFraction, m_MaxScreenFraction, lerpFactor);
452 }
453 else if (scaler.type == DynamicResScalePolicyType.ReturnsPercentage)
454 {
455 float percentageRequested = Mathf.Max(scaler.method(), 5.0f);
456 m_CurrentFraction = Mathf.Clamp(percentageRequested / 100.0f, m_MinScreenFraction, m_MaxScreenFraction);
457 }
458 }
459
460 bool hardwareResolutionChanged = false;
461 bool softwareResolutionChanged = m_CurrentFraction != m_PrevFraction;
462
463 m_PrevFraction = m_CurrentFraction;
464
465 if (!m_ForceSoftwareFallback && type == DynamicResolutionType.Hardware)
466 {
467 hardwareResolutionChanged = FlushScalableBufferManagerState();
468 if (ScalableBufferManager.widthScaleFactor != m_PrevHWScaleWidth ||
469 ScalableBufferManager.heightScaleFactor != m_PrevHWScaleHeight)
470 {
471 hardwareResolutionChanged = true;
472 }
473 }
474
475
476 if ((softwareResolutionChanged || hardwareResolutionChanged) && OnResolutionChange != null)
477 OnResolutionChange();
478
479 s_ActiveInstanceDirty = false;
480 m_PrevHWScaleWidth = ScalableBufferManager.widthScaleFactor;
481 m_PrevHWScaleHeight = ScalableBufferManager.heightScaleFactor;
482 }
483
484 /// <summary>
485 /// Determines whether software dynamic resolution is enabled or not.
486 /// </summary>
487 /// <returns>True: Software dynamic resolution is enabled</returns>
488 public bool SoftwareDynamicResIsEnabled()
489 {
490 return m_CurrentCameraRequest && m_Enabled && (m_CurrentFraction != 1.0f || runUpscalerFilterOnFullResolution) && (m_ForceSoftwareFallback || type == DynamicResolutionType.Software);
491 }
492
493 /// <summary>
494 /// Determines whether hardware dynamic resolution is enabled or not.
495 /// </summary>
496 /// <returns>True: Hardware dynamic resolution is enabled</returns>
497 public bool HardwareDynamicResIsEnabled()
498 {
499 return !m_ForceSoftwareFallback && m_CurrentCameraRequest && m_Enabled && type == DynamicResolutionType.Hardware;
500 }
501
502 /// <summary>
503 /// Identifies whether hardware dynamic resolution has been requested and is going to be used.
504 /// </summary>
505 /// <returns>True: Hardware dynamic resolution is requested by user and software fallback has not been forced</returns>
506 public bool RequestsHardwareDynamicResolution()
507 {
508 if (m_ForceSoftwareFallback)
509 return false;
510
511 return type == DynamicResolutionType.Hardware;
512 }
513
514 /// <summary>
515 /// Identifies whether dynamic resolution is enabled and scaling the render targets.
516 /// </summary>
517 /// <returns>True: Dynamic resolution is enabled.</returns>
518 public bool DynamicResolutionEnabled()
519 {
520 //we assume that the DRS schedule takes care of anti aliasing. Thus we dont care if the fraction requested is 1.0
521 return m_CurrentCameraRequest && m_Enabled && (m_CurrentFraction != 1.0f || runUpscalerFilterOnFullResolution);
522 }
523
524 /// <summary>
525 /// 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.
526 /// </summary>
527 public void ForceSoftwareFallback()
528 {
529 m_ForceSoftwareFallback = true;
530 }
531
532 /// <summary>
533 /// Applies to the passed size the scale imposed by the dynamic resolution system.
534 /// Note: this function has the side effect of caching the last scale size, and the output is always smaller or equal then the input.
535 /// </summary>
536 /// <param name="size">The starting size of the render target that will be scaled by dynamic resolution.</param>
537 /// <returns>The parameter size scaled by the dynamic resolution system.</returns>
538 public Vector2Int GetScaledSize(Vector2Int size)
539 {
540 cachedOriginalSize = size;
541
542 if (!m_Enabled || !m_CurrentCameraRequest)
543 {
544 return size;
545 }
546
547 Vector2Int scaledSize = ApplyScalesOnSize(size);
548
549 m_LastScaledSize = scaledSize;
550 return scaledSize;
551 }
552
553 /// <summary>
554 /// Applies to the passed size the scale imposed by the dynamic resolution system.
555 /// This function uses the internal resolved scale from the dynamic resolution system.
556 /// Note: this function is pure (has no side effects), this function does not cache the pre-scale size
557 /// </summary>
558 /// <param name="size">The size to apply the scaling</param>
559 /// <returns>The parameter size scaled by the dynamic resolution system.</returns>
560 public Vector2Int ApplyScalesOnSize(Vector2Int size)
561 {
562 return ApplyScalesOnSize(size, GetResolvedScale());
563 }
564
565 internal Vector2Int ApplyScalesOnSize(Vector2Int size, Vector2 scales)
566 {
567 Vector2Int scaledSize = new Vector2Int(Mathf.CeilToInt(size.x * scales.x), Mathf.CeilToInt(size.y * scales.y));
568 if (m_ForceSoftwareFallback || type != DynamicResolutionType.Hardware)
569 {
570 scaledSize.x += (1 & scaledSize.x);
571 scaledSize.y += (1 & scaledSize.y);
572 }
573
574 scaledSize.x = Math.Min(scaledSize.x, size.x);
575 scaledSize.y = Math.Min(scaledSize.y, size.y);
576
577 return scaledSize;
578 }
579
580 /// <summary>
581 /// Returns the scale that is currently applied by the dynamic resolution system.
582 /// </summary>
583 /// <returns>The scale that is currently applied by the dynamic resolution system.</returns>
584 public float GetCurrentScale()
585 {
586 return (m_Enabled && m_CurrentCameraRequest) ? m_CurrentFraction : 1.0f;
587 }
588
589 /// <summary>
590 /// Returns the latest scaled size that has been produced by GetScaledSize.
591 /// </summary>
592 /// <returns>The latest scaled size that has been produced by GetScaledSize.</returns>
593 public Vector2Int GetLastScaledSize()
594 {
595 return m_LastScaledSize;
596 }
597
598 /// <summary>
599 /// Returns the resolved low res multiplier based on the low res transparency threshold settings.
600 /// Note: The pipeline can use this to drive the scale for low res transparency if available.
601 /// </summary>
602 /// <param name="targetLowRes"> the target low resolution.
603 /// If by any chance thresholding is disabled or clamped, the exact same resolution is returned.
604 /// This allows the caller to directly compare the float result safely with the floating point target resolution.
605 /// </param>
606 /// <returns>Returns the resolved low res multiplier based on the low transparency threshold settings.</returns>
607 public float GetLowResMultiplier(float targetLowRes) => GetLowResMultiplier(targetLowRes, m_CachedSettings.lowResTransparencyMinimumThreshold );
608
609 /// <summary>
610 /// Returns the resolved low res multiplier based on the a low res threshold.
611 /// </summary>
612 /// <param name="targetLowRes"> the target low resolution.
613 /// If by any chance thresholding is disabled or clamped, the exact same resolution is returned.
614 /// This allows the caller to directly compare the float result safely with the floating point target resolution.
615 /// </param>
616 /// <param name="minimumThreshold"> The custom threshold used to clamp the effect's resolution. </param>
617 /// <returns>Returns the resolved low res multiplier based on the minimumThreshold threshold settings.</returns>
618 public float GetLowResMultiplier(float targetLowRes, float minimumThreshold)
619 {
620 if (!m_Enabled)
621 return targetLowRes;
622
623 float thresholdPercentage = Math.Min(minimumThreshold / 100.0f, targetLowRes);
624 float targetPercentage = targetLowRes * m_CurrentFraction;
625 if (targetPercentage >= thresholdPercentage)
626 return targetLowRes;
627
628 return Mathf.Clamp(thresholdPercentage / m_CurrentFraction, 0.0f, 1.0f);
629 }
630 }
631}