Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce allocation overhead for keyboard / binding / button handling #6126

Merged
merged 8 commits into from
Jan 12, 2024

Conversation

peppy
Copy link
Member

@peppy peppy commented Jan 11, 2024

Overall gains are pretty substantial. All testing done with 13 keystrokes. Ignore overall allocations as the runtime of tests was significantly different.

Before After
CleanShot 2024-01-11 at 07 44 09 CleanShot 2024-01-11 at 07 44 17

94b4b24 Remove allocation overhead in button propagation flow

Before After
CleanShot 2024-01-11 at 07 40 36 CleanShot 2024-01-11 at 07 41 14

d16d899 Remove allocation overhead from KeyBindingContainer key handling

Before After
CleanShot 2024-01-11 at 07 35 00 CleanShot 2024-01-11 at 07 35 54

7641f70 Reduce allocation overhead of KeyCombination constructor

Before After
CleanShot 2024-01-11 at 07 30 23 CleanShot 2024-01-11 at 07 30 28

osu.Framework/Input/Bindings/KeyCombination.cs Outdated Show resolved Hide resolved
}

// 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));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically a change in behaviour as .Sort() uses an unstable algorithm while .OrderByDescending() uses a stable one. I don't realistically think it matters though.

bdach
bdach previously approved these changes Jan 11, 2024
pressedBindings.Remove(binding);
var binding = pressedBindings[i];

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a behavioural change here that breaks tests...

The previous code had an explicit pressedInputKeys.Count == 0 check. That would basically "short circuit" the IsPressed() checking by just assuming that in such a case all pressed combinations must be released. But now this transformation breaks things because due to the lack of that special check, IsPressed is given KeyCombination.none as the pressedCombination here and dies.

Something like this fixes:

diff --git a/osu.Framework/Input/Bindings/KeyBindingContainer.cs b/osu.Framework/Input/Bindings/KeyBindingContainer.cs
index 6fcb2ba06..4e7908212 100644
--- a/osu.Framework/Input/Bindings/KeyBindingContainer.cs
+++ b/osu.Framework/Input/Bindings/KeyBindingContainer.cs
@@ -363,11 +363,12 @@ private void handleNewReleased(InputState state, InputKey releasedKey)
             {
                 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();
+                if (pressedInputKeys.Count == 0 || !binding.KeyCombination.IsPressed(pressedCombination, KeyCombinationMatchingMode.Any))
+                {
+                    pressedBindings.RemoveAt(i--);
+                    PropagateReleased(getInputQueue(binding).Where(d => d.IsRootedAt(this)), state, binding.GetAction<T>());
+                    keyBindingQueues[binding].Clear();
+                }
             }
         }
 

but also feels immensely dodgy... Not sure what else to do, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it.

My thinking is that if pressedBindings is empty, there's nothing to release. What am I missing?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous code was filtering pressedInputBindings basing on the value of pressedKeys. Significant difference.

Which then, with the current code, dies at

https://github.com/ppy/osu-framework/blob/master/osu.Framework%2FInput%2FBindings%2FKeyCombination.cs#L75

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Got confused with variables to some degree. I think your solution is okay.

@peppy peppy merged commit cd28e1c into ppy:master Jan 12, 2024
18 of 20 checks passed
@peppy peppy deleted the keyboard-input-allocs branch January 17, 2024 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants