reduce allocations in input handling

This reuses the input builders instead of creating new ones each time we build input.
This commit is contained in:
Jesse Plamondon-Willard
2026-02-01 18:17:24 -05:00
parent 81d6214729
commit 3bb18fcdaf
6 changed files with 74 additions and 47 deletions
+1
View File
@@ -3,6 +3,7 @@
# Release notes
## Upcoming release
* For players:
* Minor performance optimizations.
* Fixed the Linux/macOS installer not saving the color scheme correctly in 4.5.0+.
* Fixed typo in config UI text (thanks to QuentiumYT!).
* Improved translations. Thanks to dewanggatrustha (updated Indonesian), QuentiumYT (updated French), and Timur13240
@@ -21,7 +21,7 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
private GamePadState? State;
/// <summary>The current button states.</summary>
private readonly Dictionary<SButton, ButtonState> ButtonStates;
private readonly Dictionary<SButton, ButtonState> ButtonStates = [];
/// <summary>The left trigger value.</summary>
private float LeftTrigger;
@@ -39,9 +39,8 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="state">The initial state.</param>
public GamePadStateBuilder(GamePadState state)
/// <inheritdoc />
public void Reset(GamePadState state)
{
this.State = state;
@@ -51,32 +50,39 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
GamePadButtons buttons = state.Buttons;
GamePadTriggers triggers = state.Triggers;
GamePadThumbSticks sticks = state.ThumbSticks;
this.ButtonStates = new Dictionary<SButton, ButtonState>
{
[SButton.DPadUp] = pad.Up,
[SButton.DPadDown] = pad.Down,
[SButton.DPadLeft] = pad.Left,
[SButton.DPadRight] = pad.Right,
[SButton.ControllerA] = buttons.A,
[SButton.ControllerB] = buttons.B,
[SButton.ControllerX] = buttons.X,
[SButton.ControllerY] = buttons.Y,
[SButton.LeftStick] = buttons.LeftStick,
[SButton.RightStick] = buttons.RightStick,
[SButton.LeftShoulder] = buttons.LeftShoulder,
[SButton.RightShoulder] = buttons.RightShoulder,
[SButton.ControllerBack] = buttons.Back,
[SButton.ControllerStart] = buttons.Start,
[SButton.BigButton] = buttons.BigButton
};
var states = this.ButtonStates;
states.Clear();
states[SButton.DPadUp] = pad.Up;
states[SButton.DPadDown] = pad.Down;
states[SButton.DPadLeft] = pad.Left;
states[SButton.DPadRight] = pad.Right;
states[SButton.ControllerA] = buttons.A;
states[SButton.ControllerB] = buttons.B;
states[SButton.ControllerX] = buttons.X;
states[SButton.ControllerY] = buttons.Y;
states[SButton.LeftStick] = buttons.LeftStick;
states[SButton.RightStick] = buttons.RightStick;
states[SButton.LeftShoulder] = buttons.LeftShoulder;
states[SButton.RightShoulder] = buttons.RightShoulder;
states[SButton.ControllerBack] = buttons.Back;
states[SButton.ControllerStart] = buttons.Start;
states[SButton.BigButton] = buttons.BigButton;
this.LeftTrigger = triggers.Left;
this.RightTrigger = triggers.Right;
this.LeftStickPos = sticks.Left;
this.RightStickPos = sticks.Right;
}
else
this.ButtonStates = [];
{
this.ButtonStates.Clear();
this.LeftTrigger = 0;
this.RightTrigger = 0;
this.LeftStickPos = Vector2.Zero;
this.RightStickPos = Vector2.Zero;
}
}
/// <inheritdoc />
@@ -12,6 +12,10 @@ internal interface IInputStateBuilder<out THandler, TState>
/*********
** Methods
*********/
/// <summary>Reset the state.</summary>
/// <param name="state">The initial state before any overrides are applied.</param>
void Reset(TState state);
/// <summary>Override the states for a set of buttons.</summary>
/// <param name="overrides">The button state overrides.</param>
THandler OverrideButtons(IDictionary<SButton, SButtonState> overrides);
@@ -20,9 +20,8 @@ internal class KeyboardStateBuilder : IInputStateBuilder<KeyboardStateBuilder, K
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="state">The initial state.</param>
public KeyboardStateBuilder(KeyboardState state)
/// <inheritdoc />
public void Reset(KeyboardState state)
{
this.State = state;
+14 -15
View File
@@ -13,39 +13,38 @@ internal class MouseStateBuilder : IInputStateBuilder<MouseStateBuilder, MouseSt
private MouseState? State;
/// <summary>The current button states.</summary>
private readonly IDictionary<SButton, ButtonState> ButtonStates;
private readonly Dictionary<SButton, ButtonState> ButtonStates = [];
/// <summary>The mouse wheel scroll value.</summary>
private readonly int ScrollWheelValue;
private int ScrollWheelValue;
/*********
** Accessors
*********/
/// <summary>The X cursor position.</summary>
public int X { get; }
public int X { get; private set; }
/// <summary>The Y cursor position.</summary>
public int Y { get; }
public int Y { get; private set; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="state">The initial state.</param>
public MouseStateBuilder(MouseState state)
/// <inheritdoc />
public void Reset(MouseState state)
{
this.State = state;
this.ButtonStates = new Dictionary<SButton, ButtonState>
{
[SButton.MouseLeft] = state.LeftButton,
[SButton.MouseMiddle] = state.MiddleButton,
[SButton.MouseRight] = state.RightButton,
[SButton.MouseX1] = state.XButton1,
[SButton.MouseX2] = state.XButton2
};
var states = this.ButtonStates;
states.Clear();
states[SButton.MouseLeft] = state.LeftButton;
states[SButton.MouseMiddle] = state.MiddleButton;
states[SButton.MouseRight] = state.RightButton;
states[SButton.MouseX1] = state.XButton1;
states[SButton.MouseX2] = state.XButton2;
this.X = state.X;
this.Y = state.Y;
this.ScrollWheelValue = state.ScrollWheelValue;
+24 -6
View File
@@ -29,6 +29,15 @@ internal sealed class SInputState : InputState
/// <summary>Whether there are new overrides in <see cref="CustomPressedKeys"/> or <see cref="CustomReleasedKeys"/> that haven't been applied to the previous state.</summary>
private bool HasNewOverrides;
/// <summary>The builder which reads the game pad state and applies overrides.</summary>
private readonly GamePadStateBuilder ControllerStateBuilder = new();
/// <summary>The builder which reads the keyboard state and applies overrides.</summary>
private readonly KeyboardStateBuilder KeyboardStateBuilder = new();
/// <summary>The builder which reads the mouse state and applies overrides.</summary>
private readonly MouseStateBuilder MouseStateBuilder = new();
/*********
** Accessors
@@ -68,10 +77,15 @@ internal sealed class SInputState : InputState
{
float zoomMultiplier = (1f / Game1.options.zoomLevel);
// get builders
GamePadStateBuilder controller = this.ControllerStateBuilder;
KeyboardStateBuilder keyboard = this.KeyboardStateBuilder;
MouseStateBuilder mouse = this.MouseStateBuilder;
// get real values
var controller = new GamePadStateBuilder(base.GetGamePadState());
var keyboard = new KeyboardStateBuilder(base.GetKeyboardState());
var mouse = new MouseStateBuilder(base.GetMouseState());
controller.Reset(base.GetGamePadState());
keyboard.Reset(base.GetKeyboardState());
mouse.Reset(base.GetMouseState());
Vector2 cursorAbsolutePos = new((mouse.X * zoomMultiplier) + Game1.viewport.X, (mouse.Y * zoomMultiplier) + Game1.viewport.Y);
Vector2? playerTilePos = Context.IsPlayerFree ? Game1.player.Tile : null;
HashSet<SButton> reallyDown = new(this.GetPressedButtons(keyboard, mouse, controller));
@@ -159,9 +173,13 @@ internal sealed class SInputState : InputState
{
if (this.HasNewOverrides)
{
var controller = new GamePadStateBuilder(this.ControllerState);
var keyboard = new KeyboardStateBuilder(this.KeyboardState);
var mouse = new MouseStateBuilder(this.MouseState);
GamePadStateBuilder controller = this.ControllerStateBuilder;
KeyboardStateBuilder keyboard = this.KeyboardStateBuilder;
MouseStateBuilder mouse = this.MouseStateBuilder;
controller.Reset(this.ControllerState);
keyboard.Reset(this.KeyboardState);
mouse.Reset(this.MouseState);
if (this.ApplyOverrides(pressed: this.CustomPressedKeys, released: this.CustomReleasedKeys, controller, keyboard, mouse))
{