Skip to content

Commit

Permalink
Remove allocation overhead from KeyBindingContainer key handling
Browse files Browse the repository at this point in the history
  • Loading branch information
peppy committed Jan 11, 2024
1 parent 94b4b24 commit d16d899
Showing 1 changed file with 26 additions and 11 deletions.
37 changes: 26 additions & 11 deletions osu.Framework/Input/Bindings/KeyBindingContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ private bool handleRepeat(InputState state)
return drawables.FirstOrDefault(d => triggerKeyBindingEvent(d, pressEvent)) != null;
}

private readonly List<IKeyBinding> newlyPressed = new List<IKeyBinding>();

private bool handleNewPressed(InputState state, InputKey newKey, Vector2? scrollDelta = null, bool isPrecise = false)
{
pressedInputKeys.Add(newKey);
Expand All @@ -210,22 +212,36 @@ private bool handleNewPressed(InputState state, InputKey newKey, Vector2? scroll
var pressedCombination = new KeyCombination(pressedInputKeys);

bool handled = false;
var bindings = KeyBindings?.Except(pressedBindings) ?? Enumerable.Empty<IKeyBinding>();

var newlyPressed = bindings.Where(m =>
m.KeyCombination.IsPressed(pressedCombination, matchingMode));
newlyPressed.Clear();

if (KeyBindings != null)
{
foreach (IKeyBinding binding in KeyBindings)
{
if (pressedBindings.Contains(binding))
continue;

if (binding.KeyCombination.IsPressed(pressedCombination, matchingMode))
newlyPressed.Add(binding);
}
}

if (KeyCombination.IsModifierKey(newKey))
{
// if the current key pressed was a modifier, only handle modifier-only bindings.
// lambda expression is used so that the delegate is cached (see: https://github.com/dotnet/roslyn/issues/5835)
// TODO: remove when we switch to .NET 7.
// ReSharper disable once ConvertClosureToMethodGroup
newlyPressed = newlyPressed.Where(b => b.KeyCombination.Keys.All(key => KeyCombination.IsModifierKey(key)));
for (int i = 0; i < newlyPressed.Count; i++)
{
if (!newlyPressed[i].KeyCombination.Keys.All(key => KeyCombination.IsModifierKey(key)))
newlyPressed.RemoveAt(i--);
}
}

// we want to always handle bindings with more keys before bindings with less.
newlyPressed = newlyPressed.OrderByDescending(b => b.KeyCombination.Keys.Length).ToList();
newlyPressed.Sort(static (a, b) => b.KeyCombination.Keys.Length.CompareTo(a.KeyCombination.Keys.Length));

pressedBindings.AddRange(newlyPressed);

Expand Down Expand Up @@ -343,14 +359,13 @@ private void handleNewReleased(InputState state, InputKey releasedKey)
// we don't want to consider exact matching here as we are dealing with bindings, not actions.
var pressedCombination = new KeyCombination(pressedInputKeys);

var newlyReleased = pressedInputKeys.Count == 0
? pressedBindings.ToList()
: pressedBindings.Where(b => !b.KeyCombination.IsPressed(pressedCombination, KeyCombinationMatchingMode.Any)).ToList();

foreach (var binding in newlyReleased)
for (int i = 0; i < pressedBindings.Count; i++)
{
pressedBindings.Remove(binding);
var binding = pressedBindings[i];

if (binding.KeyCombination.IsPressed(pressedCombination, KeyCombinationMatchingMode.Any)) continue;

pressedBindings.RemoveAt(i--);
PropagateReleased(getInputQueue(binding).Where(d => d.IsRootedAt(this)), state, binding.GetAction<T>());
keyBindingQueues[binding].Clear();
}
Expand Down

0 comments on commit d16d899

Please sign in to comment.