reduce allocations in input API's GetPressedButtons() logic

This commit is contained in:
Jesse Plamondon-Willard
2026-03-22 22:48:38 -04:00
parent dece40841b
commit 2aeaf6fb83
5 changed files with 43 additions and 38 deletions
@@ -145,54 +145,52 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
}
/// <inheritdoc />
public IEnumerable<SButton> GetPressedButtons()
public void FillPressedButtons(HashSet<SButton> set)
{
// buttons
foreach (Buttons button in this.GetPressedGamePadButtons())
yield return button.ToSButton();
set.Add(button.ToSButton());
// triggers
if (this.LeftTrigger > 0.2f)
yield return SButton.LeftTrigger;
set.Add(SButton.LeftTrigger);
if (this.RightTrigger > 0.2f)
yield return SButton.RightTrigger;
set.Add(SButton.RightTrigger);
// left thumbstick direction
if (this.LeftStickPos.Y > GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickUp;
set.Add(SButton.LeftThumbstickUp);
if (this.LeftStickPos.Y < -GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickDown;
set.Add(SButton.LeftThumbstickDown);
if (this.LeftStickPos.X > GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickRight;
set.Add(SButton.LeftThumbstickRight);
if (this.LeftStickPos.X < -GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickLeft;
set.Add(SButton.LeftThumbstickLeft);
// right thumbstick direction
if (this.RightStickPos.Length() > GamePadStateBuilder.RightThumbstickDeadZone)
{
if (this.RightStickPos.Y > 0)
yield return SButton.RightThumbstickUp;
set.Add(SButton.RightThumbstickUp);
if (this.RightStickPos.Y < 0)
yield return SButton.RightThumbstickDown;
set.Add(SButton.RightThumbstickDown);
if (this.RightStickPos.X > 0)
yield return SButton.RightThumbstickRight;
set.Add(SButton.RightThumbstickRight);
if (this.RightStickPos.X < 0)
yield return SButton.RightThumbstickLeft;
set.Add(SButton.RightThumbstickLeft);
}
}
/// <inheritdoc />
public GamePadState GetState()
{
this.State ??= new GamePadState(
return this.State ??= new GamePadState(
leftThumbStick: this.LeftStickPos,
rightThumbStick: this.RightStickPos,
leftTrigger: this.LeftTrigger,
rightTrigger: this.RightTrigger,
buttons: this.GetPressedGamePadButtons().ToArray()
);
return this.State.Value;
}
@@ -20,8 +20,9 @@ internal interface IInputStateBuilder<out THandler, TState>
/// <param name="overrides">The button state overrides.</param>
THandler OverrideButtons(IDictionary<SButton, SButtonState> overrides);
/// <summary>Get the currently pressed buttons.</summary>
IEnumerable<SButton> GetPressedButtons();
/// <summary>Fill a set with the currently pressed buttons.</summary>
/// <param name="set">The set to populate with the pressed buttons.</param>
void FillPressedButtons(HashSet<SButton> set);
/// <summary>Get the equivalent state.</summary>
TState GetState();
@@ -50,17 +50,15 @@ internal class KeyboardStateBuilder : IInputStateBuilder<KeyboardStateBuilder, K
}
/// <inheritdoc />
public IEnumerable<SButton> GetPressedButtons()
public void FillPressedButtons(HashSet<SButton> set)
{
foreach (Keys key in this.PressedButtons)
yield return key.ToSButton();
set.Add(key.ToSButton());
}
/// <inheritdoc />
public KeyboardState GetState()
{
return
this.State
?? (this.State = new KeyboardState(this.PressedButtons.ToArray())).Value;
return this.State ??= new KeyboardState(this.PressedButtons.ToArray());
}
}
@@ -66,19 +66,19 @@ internal class MouseStateBuilder : IInputStateBuilder<MouseStateBuilder, MouseSt
}
/// <inheritdoc />
public IEnumerable<SButton> GetPressedButtons()
public void FillPressedButtons(HashSet<SButton> set)
{
foreach (var pair in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed)
yield return pair.Key;
set.Add(pair.Key);
}
}
/// <inheritdoc />
public MouseState GetState()
{
this.State ??= new MouseState(
return this.State ??= new MouseState(
x: this.X,
y: this.Y,
scrollWheel: this.ScrollWheelValue,
@@ -88,7 +88,5 @@ internal class MouseStateBuilder : IInputStateBuilder<MouseStateBuilder, MouseSt
xButton1: this.ButtonStates[SButton.MouseX1],
xButton2: this.ButtonStates[SButton.MouseX2]
);
return this.State.Value;
}
}
+21 -11
View File
@@ -38,6 +38,9 @@ internal sealed class SInputState : InputState
/// <summary>The builder which reads the mouse state and applies overrides.</summary>
private readonly MouseStateBuilder MouseStateBuilder = new();
/// <summary>The pooled cache set for <see cref="FillPressedButtons"/> in <see cref="TrueUpdate"/>.</summary>
private readonly HashSet<SButton> PooledPressedButtons = [];
/*********
** Accessors
@@ -82,21 +85,26 @@ internal sealed class SInputState : InputState
KeyboardStateBuilder keyboard = this.KeyboardStateBuilder;
MouseStateBuilder mouse = this.MouseStateBuilder;
// get pooled button set
HashSet<SButton> pressedButtons = this.PooledPressedButtons;
// get real values
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));
pressedButtons.Clear();
this.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
// apply overrides
bool hasOverrides = false;
if (this.CustomPressedKeys.Count > 0 || this.CustomReleasedKeys.Count > 0)
{
// reset overrides that no longer apply
this.CustomPressedKeys.ExceptWith(reallyDown);
this.CustomReleasedKeys.IntersectWith(reallyDown);
this.CustomPressedKeys.ExceptWith(pressedButtons);
this.CustomReleasedKeys.IntersectWith(pressedButtons);
// apply overrides
if (this.ApplyOverrides(this.CustomPressedKeys, this.CustomReleasedKeys, controller, keyboard, mouse))
@@ -107,9 +115,11 @@ internal sealed class SInputState : InputState
}
// get button states
var pressedButtons = hasOverrides
? new(this.GetPressedButtons(keyboard, mouse, controller))
: reallyDown;
if (hasOverrides)
{
pressedButtons.Clear();
this.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
}
var activeButtons = this.DeriveStates(this.ButtonStates, pressedButtons);
// update
@@ -315,15 +325,15 @@ internal sealed class SInputState : InputState
}
/// <summary>Get the buttons pressed in the given stats.</summary>
/// <param name="set">The set to populate with pressed buttons.</param>
/// <param name="keyboard">The keyboard state.</param>
/// <param name="mouse">The mouse state.</param>
/// <param name="controller">The controller state.</param>
/// <remarks>Thumbstick direction logic derived from <see cref="ButtonCollection"/>.</remarks>
private IEnumerable<SButton> GetPressedButtons(KeyboardStateBuilder keyboard, MouseStateBuilder mouse, GamePadStateBuilder controller)
private void FillPressedButtons(HashSet<SButton> set, KeyboardStateBuilder keyboard, MouseStateBuilder mouse, GamePadStateBuilder controller)
{
return keyboard
.GetPressedButtons()
.Concat(mouse.GetPressedButtons())
.Concat(controller.GetPressedButtons());
keyboard.FillPressedButtons(set);
mouse.FillPressedButtons(set);
controller.FillPressedButtons(set);
}
}