reduce allocations when applying input overrides

This commit is contained in:
Jesse Plamondon-Willard
2026-03-24 00:07:36 -04:00
parent f7bc2d96f5
commit 3474f49c8c
5 changed files with 123 additions and 123 deletions
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -21,7 +22,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<Buttons, ButtonState> ButtonStates = [];
/// <summary>The left trigger value.</summary>
private float LeftTrigger;
@@ -53,21 +54,21 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
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;
states[Buttons.DPadUp] = pad.Up;
states[Buttons.DPadDown] = pad.Down;
states[Buttons.DPadLeft] = pad.Left;
states[Buttons.DPadRight] = pad.Right;
states[Buttons.A] = buttons.A;
states[Buttons.B] = buttons.B;
states[Buttons.X] = buttons.X;
states[Buttons.Y] = buttons.Y;
states[Buttons.LeftStick] = buttons.LeftStick;
states[Buttons.RightStick] = buttons.RightStick;
states[Buttons.LeftShoulder] = buttons.LeftShoulder;
states[Buttons.RightShoulder] = buttons.RightShoulder;
states[Buttons.Back] = buttons.Back;
states[Buttons.Start] = buttons.Start;
states[Buttons.BigButton] = buttons.BigButton;
this.LeftTrigger = triggers.Left;
this.RightTrigger = triggers.Right;
@@ -85,63 +86,81 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
}
}
/// <inheritdoc />
public GamePadStateBuilder OverrideButtons(IDictionary<SButton, SButtonState> overrides)
/// <summary>Override the state for a button.</summary>
/// <param name="button">The button to override.</param>
/// <param name="state">The new state to set.</param>
public void OverrideButton(Buttons button, SButtonState state)
{
foreach (var pair in overrides)
bool isDown = state.IsDown();
bool changed = false;
switch (button)
{
bool changed = true;
// left thumbstick
case Buttons.LeftThumbstickUp:
changed = Set(ref this.LeftStickPos.Y, isDown ? 1 : 0);
break;
case Buttons.LeftThumbstickDown:
changed = Set(ref this.LeftStickPos.Y, isDown ? -1 : 0);
break;
case Buttons.LeftThumbstickLeft:
changed = Set(ref this.LeftStickPos.X, isDown ? -1 : 0);
break;
case Buttons.LeftThumbstickRight:
changed = Set(ref this.LeftStickPos.X, isDown ? 1 : 0);
break;
bool isDown = pair.Value.IsDown();
switch (pair.Key)
{
// left thumbstick
case SButton.LeftThumbstickUp:
this.LeftStickPos.Y = isDown ? 1 : 0;
break;
case SButton.LeftThumbstickDown:
this.LeftStickPos.Y = isDown ? -1 : 0;
break;
case SButton.LeftThumbstickLeft:
this.LeftStickPos.X = isDown ? -1 : 0;
break;
case SButton.LeftThumbstickRight:
this.LeftStickPos.X = isDown ? 1 : 0;
break;
// right thumbstick
case Buttons.RightThumbstickUp:
changed = Set(ref this.RightStickPos.Y, isDown ? 1 : 0);
break;
case Buttons.RightThumbstickDown:
changed = Set(ref this.RightStickPos.Y, isDown ? -1 : 0);
break;
case Buttons.RightThumbstickLeft:
changed = Set(ref this.RightStickPos.X, isDown ? -1 : 0);
break;
case Buttons.RightThumbstickRight:
changed = Set(ref this.RightStickPos.X, isDown ? 1 : 0);
break;
// right thumbstick
case SButton.RightThumbstickUp:
this.RightStickPos.Y = isDown ? 1 : 0;
break;
case SButton.RightThumbstickDown:
this.RightStickPos.Y = isDown ? -1 : 0;
break;
case SButton.RightThumbstickLeft:
this.RightStickPos.X = isDown ? -1 : 0;
break;
case SButton.RightThumbstickRight:
this.RightStickPos.X = isDown ? 1 : 0;
break;
// triggers
case Buttons.LeftTrigger:
changed = Set(ref this.LeftTrigger, isDown ? 1 : 0);
break;
case Buttons.RightTrigger:
changed = Set(ref this.RightTrigger, isDown ? 1 : 0);
break;
// triggers
case SButton.LeftTrigger:
this.LeftTrigger = isDown ? 1 : 0;
break;
case SButton.RightTrigger:
this.RightTrigger = isDown ? 1 : 0;
break;
// buttons
default:
{
ButtonState newState = isDown ? ButtonState.Pressed : ButtonState.Released;
// buttons
default:
this.ButtonStates[pair.Key] = isDown ? ButtonState.Pressed : ButtonState.Released;
break;
}
if (changed)
this.State = null;
if (!this.ButtonStates.TryGetValue(button, out ButtonState oldState) || newState != oldState)
{
this.ButtonStates[button] = newState;
changed = true;
}
}
break;
}
return this;
if (changed)
this.State = null;
return;
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator", Justification = "Floating points not an issue for the specific values we're checking.")]
static bool Set(ref float field, int newValue)
{
if (field != newValue)
{
field = newValue;
return true;
}
return false;
}
}
/// <inheritdoc />
@@ -200,9 +219,9 @@ internal class GamePadStateBuilder : IInputStateBuilder<GamePadStateBuilder, Gam
/// <summary>Get the pressed gamepad buttons.</summary>
private IEnumerable<Buttons> GetPressedGamePadButtons()
{
foreach (var pair in this.ButtonStates)
foreach ((Buttons button, ButtonState state) in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
if (state == ButtonState.Pressed)
yield return button;
}
}
@@ -16,10 +16,6 @@ internal interface IInputStateBuilder<out THandler, TState>
/// <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);
/// <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);
@@ -30,23 +30,17 @@ internal class KeyboardStateBuilder : IInputStateBuilder<KeyboardStateBuilder, K
this.PressedButtons.Add(button);
}
/// <inheritdoc />
public KeyboardStateBuilder OverrideButtons(IDictionary<SButton, SButtonState> overrides)
/// <summary>Override the state for a key.</summary>
/// <param name="key">The key to override.</param>
/// <param name="state">The new state to set.</param>
public void OverrideButton(Keys key, SButtonState state)
{
foreach (var pair in overrides)
{
if (pair.Key.TryGetKeyboard(out Keys key))
{
this.State = null;
bool changed = state.IsDown()
? this.PressedButtons.Add(key)
: this.PressedButtons.Remove(key);
if (pair.Value.IsDown())
this.PressedButtons.Add(key);
else
this.PressedButtons.Remove(key);
}
}
return this;
if (changed)
this.State = null;
}
/// <inheritdoc />
+12 -11
View File
@@ -50,19 +50,20 @@ internal class MouseStateBuilder : IInputStateBuilder<MouseStateBuilder, MouseSt
this.ScrollWheelValue = state.ScrollWheelValue;
}
/// <inheritdoc />
public MouseStateBuilder OverrideButtons(IDictionary<SButton, SButtonState> overrides)
/// <summary>Override the state for a button.</summary>
/// <param name="button">The button to override.</param>
/// <param name="state">The new state to set.</param>
public void OverrideButton(SButton button, SButtonState state)
{
foreach (var pair in overrides)
{
if (this.ButtonStates.ContainsKey(pair.Key))
{
this.State = null;
this.ButtonStates[pair.Key] = pair.Value.IsDown() ? ButtonState.Pressed : ButtonState.Released;
}
}
ButtonState newState = state.IsDown()
? ButtonState.Pressed
: ButtonState.Released;
return this;
if (this.ButtonStates.TryGetValue(button, out ButtonState oldState) && oldState != newState)
{
this.State = null;
this.ButtonStates[button] = newState;
}
}
/// <inheritdoc />
+16 -26
View File
@@ -251,34 +251,24 @@ internal sealed class SInputState : InputState
if (pressed.Count == 0 && released.Count == 0)
return false;
// group keys by type
IDictionary<SButton, SButtonState> keyboardOverrides = new Dictionary<SButton, SButtonState>();
IDictionary<SButton, SButtonState> controllerOverrides = new Dictionary<SButton, SButtonState>();
IDictionary<SButton, SButtonState> mouseOverrides = new Dictionary<SButton, SButtonState>();
foreach (SButton button in pressed.Concat(released))
{
SButtonState newState = SInputState.DeriveState(
oldState: this.GetState(button),
isDown: pressed.Contains(button)
);
if (button is SButton.MouseLeft or SButton.MouseMiddle or SButton.MouseRight or SButton.MouseX1 or SButton.MouseX2)
mouseOverrides[button] = newState;
else if (button.TryGetKeyboard(out Keys _))
keyboardOverrides[button] = newState;
else if (button.TryGetController(out Buttons _))
controllerOverrides[button] = newState;
}
// override states
if (keyboardOverrides.Count > 0)
keyboard.OverrideButtons(keyboardOverrides);
if (controllerOverrides.Count > 0)
controller.OverrideButtons(controllerOverrides);
if (mouseOverrides.Count > 0)
mouse.OverrideButtons(mouseOverrides);
foreach (SButton button in pressed)
Apply(button, this.GetState(button), isDown: true, controller, keyboard, mouse);
foreach (SButton button in released)
Apply(button, this.GetState(button), isDown: pressed.Contains(button), controller, keyboard, mouse);
return true;
static void Apply(SButton button, SButtonState oldState, bool isDown, GamePadStateBuilder controller, KeyboardStateBuilder keyboard, MouseStateBuilder mouse)
{
SButtonState newState = SInputState.DeriveState(oldState, isDown);
if (button is SButton.MouseLeft or SButton.MouseMiddle or SButton.MouseRight or SButton.MouseX1 or SButton.MouseX2)
mouse.OverrideButton(button, newState);
else if (button.TryGetKeyboard(out Keys key))
keyboard.OverrideButton(key, newState);
else if (button.TryGetController(out Buttons controllerButton))
controller.OverrideButton(controllerButton, newState);
}
}
/// <summary>Update active button states based on the currently pressed buttons.</summary>