Files
RobustToolbox/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs
Pieter-Jan Briers 69c1161562 FormattedMessage/DebugConsole performance improvements (#5244)
* Add VisibilityChanged virtual to Control

* Defer updating invisible OutputPanels on UIScale change

DebugConsole falls under this when not hidden, and it significantly improves perf of e.g. resizing the window when there's a lot of stuff in there.

* Avoid redundant UI Scale updates on window resize.

Window resizing can change the UI scale, due to the auto-scaling system. This system had multiple perf issues:

UI scale was set and propagated even if it didn't change (system disabled, not effective, etc). This was just wasted processing.

UI scale was updated for every window resize event. When the game is lagging (due to the aforementioned UI scale updates being expensive...) this means multiple window resize events in a single frame ALL cause a UI scale update, which is useless.

UI scale updates from resizing now avoid doing *nothing* and are deferred until later in the frame for natural batching.

* Reduce allocations/memory usage of various rich-text related things

Just allocate a buncha dictionaries what could possibly go wrong.

I kept to non-breaking-changes which means this couldn't as effective as it should be.

There's some truly repulsive stuff here. Ugh.

* Cap debug console content size.

It's a CVar.

OutputPanel has been switched to use a new RingBufferList datastructure to make removal of the oldest entry efficient.

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-06-20 17:22:12 +10:00

168 lines
5.7 KiB
C#

using System;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Shared;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface;
internal partial class UserInterfaceManager
{
[ViewVariables] public float DefaultUIScale => _clyde.DefaultWindowScale.X;
[ViewVariables] private Vector2i _resolutionAutoScaleUpper;
[ViewVariables] private Vector2i _resolutionAutoScaleLower;
[ViewVariables] private bool _autoScaleEnabled;
[ViewVariables] private float _resolutionAutoScaleMinValue;
private void _initScaling()
{
_clyde.OnWindowResized += WindowSizeChanged;
_clyde.OnWindowScaleChanged += WindowContentScaleChanged;
RegisterAutoscaleCVarListeners();
_uiScaleChanged(_configurationManager.GetCVar(CVars.DisplayUIScale));
}
private void _uiScaleChanged(float newValue)
{
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}
private void WindowContentScaleChanged(WindowContentScaleEventArgs args)
{
if (_windowsToRoot.TryGetValue(args.Window.Id, out var root))
{
UpdateUIScale(root);
_fontManager.ClearFontCache();
}
}
private void RegisterAutoscaleCVarListeners()
{
_configurationManager.OnValueChanged(CVars.ResAutoScaleEnabled, i =>
{
_autoScaleEnabled = i;
foreach (var root in _roots)
{
root.UIScaleSet = 1;
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleLowX, i =>
{
_resolutionAutoScaleLower.X = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleLowY, i =>
{
_resolutionAutoScaleLower.Y = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleUpperX, i =>
{
_resolutionAutoScaleUpper.X = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleUpperY, i =>
{
_resolutionAutoScaleUpper.Y = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleMin, i =>
{
_resolutionAutoScaleMinValue = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
}
private float CalculateAutoScale(WindowRoot root)
{
//Grab the OS UIScale or the value set through CVAR debug
var osScale = _configurationManager.GetCVar(CVars.DisplayUIScale);
osScale = osScale == 0f ? root.Window.ContentScale.X : osScale;
var windowSize = root.Window.RenderTarget.Size;
//Only run autoscale if it is enabled, otherwise default to just use OS UIScale
if (!_autoScaleEnabled || root.DisableAutoScaling || windowSize.X <= 0 || windowSize.Y <= 0)
return osScale;
var maxScaleRes = _resolutionAutoScaleUpper;
var minScaleRes = _resolutionAutoScaleLower;
var autoScaleMin = _resolutionAutoScaleMinValue;
float scaleRatioX;
float scaleRatioY;
//Calculate the scale ratios and clamp it between the maximums and minimums
scaleRatioX = Math.Clamp(((float) windowSize.X - minScaleRes.X) / (maxScaleRes.X - minScaleRes.X) * osScale, autoScaleMin, osScale);
scaleRatioY = Math.Clamp(((float) windowSize.Y - minScaleRes.Y) / (maxScaleRes.Y - minScaleRes.Y) * osScale, autoScaleMin, osScale);
//Take the smallest UIScale value and use it for UI scaling
return Math.Min(scaleRatioX, scaleRatioY);
}
private void UpdateUIScale(WindowRoot root)
{
var newScale = CalculateAutoScale(root);
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (newScale == root.UIScaleSet)
return;
root.UIScaleSet = newScale;
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
private static void _propagateUIScaleChanged(Control control)
{
control.UIScaleChanged();
foreach (var child in control.Children)
{
_propagateUIScaleChanged(child);
}
}
private void WindowSizeChanged(WindowResizedEventArgs windowResizedEventArgs)
{
if (!_windowsToRoot.TryGetValue(windowResizedEventArgs.Window.Id, out var root))
return;
root.UIScaleUpdateNeeded = true;
root.InvalidateMeasure();
}
private void CheckRootUIScaleUpdate(WindowRoot root)
{
if (!root.UIScaleUpdateNeeded)
return;
using (_prof.Group("UIScaleUpdate"))
{
UpdateUIScale(root);
}
root.UIScaleUpdateNeeded = false;
}
}