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

Refactor Input system - Config 1` of N #2112

Merged
merged 9 commits into from May 5, 2020

Conversation

fmatthew5876
Copy link
Contributor

This PR refactors the Input subsystem.

  • Preparations for being able to reconfigure input mappings
  • Optimize speed and memory usage

@fmatthew5876 fmatthew5876 force-pushed the input_refactor branch 5 times, most recently from 73ff5c1 to 433345e Compare April 5, 2020 20:23
@fmatthew5876 fmatthew5876 added the UX For issues affecting the user experience, such annoyances, counter-intuitive or ugly design label Apr 5, 2020
@fmatthew5876
Copy link
Contributor Author

I used these commits quite a lot while testing #2035.

Retested some games using this PR. I also retested #1346 and 8 way movement still works fine.
This is ready

@fdelapena fdelapena added the Awaiting Rebase Pull requests with conflicting files due to former merge label Apr 7, 2020
@fmatthew5876 fmatthew5876 removed the Awaiting Rebase Pull requests with conflicting files due to former merge label Apr 9, 2020
@fdelapena fdelapena added this to the 0.6.3 milestone Apr 26, 2020
src/input_buttons.h Outdated Show resolved Hide resolved
* Speeds up InputSource::Update by removing indirections
* Reduces memory footprint of buttons array. Measured desktop
reduction to be a factor of 6x.
* Implement for Button -> Keys and Direction -> Button
* Use same layout as raw vector
* Sort buttons to improve locality in SourceUi::Update() and
make modifications to button array faster.
* Provide accesors and mutators, and tests.
* Store mappings in Input::Source
* Pass them into Input::Init(), so later we can source
them from other places.
@Ghabry
Copy link
Member

Ghabry commented May 3, 2020

The "naive" implementation would use std::(unordered_)map -> std::vector but your data structure seems to be a flat vector containing the keys. That's okay, but I'm still annoyed that this needs this unreadable std::lower_bound everywhere because C++ has no useful std::binary_search -_-

What is from an input point of view: A domain type a value type? Maybe use better words here that fit to a button mapping?

Can't you implemant ReplaceAll via RemoveAll followed by Add? Imo the ReplaceAll function is too complex. Has multiple code paths depending on src/dst size and micro-optimisations are not useful here.

@fmatthew5876
Copy link
Contributor Author

fmatthew5876 commented May 3, 2020

The "naive" implementation would use std::(unordered_)map -> std::vector but your data structure seems to be a flat vector containing the keys. That's okay, but I'm still annoyed that this needs this unreadable std::lower_bound everywhere because C++ has no useful std::binary_search -_-

lower_bound is the C++ binary search, it just has a different name. std::map has very poor memory locality and also memory usage overhead, especially when your object type is just a pair of integers. This implementation is highly optimized for the common case (check input keys each frame) at the expense of the rare case (initialize and remap the input keys).

What is from an input point of view: A domain type a value type? Maybe use better words here that fit to a button mapping?

Apologies. This was pretty manageable when I did the first pass and it was just built on the ButtonMapping class and wasn't a template.

As soon as I added DirectionMapping, I realized it was the same case with different semantics. You want a different type because you don't want to be able to assign or copy a ButtonMapping to a DirectionMapping etc.. But doing that necessitates templating the InputMappingArray class. As soon as I did that I was forced to use iterators, generic algorithms, and add a few typedefs and then code readability went to hell fast.

In C++ value_type is the value stored in a container. So I invented domain_type and range_type using the "domain" and "range" terminology from mathematics.

Can't you implemant ReplaceAll via RemoveAll followed by Add? Imo the ReplaceAll function is too complex. Has multiple code paths depending on src/dst size and micro-optimisations are not useful here.

Code is nastier, but it's more efficient. I'd rather keep the faster code, despite it being harder to follow. I wrote a unit test suite around this because that is the only way you can ensure it works. This is one self contained bit where we need to write it once, test it to death, and then never touch it again.

I can take another look at this and see if there is a way to make it more readable.

src/input.cpp Show resolved Hide resolved
@Ghabry
Copy link
Member

Ghabry commented May 4, 2020

lower_bound is the C++ binary search, it just has a different name. std::map has very poor memory locality and also memory usage overhead, especially when your object type is just a pair of integers.

Sorry this was not a criticism. I knew why you used std::vector and binary search here and it makes totally sense.

I would just prefer if there would be a binary_search function that returns "end" when the element was not found. Instead of the element next to it.


Would this data structure also be useful for the FileFinder in my refactor?

I would need a sorted std::string -> std::string mapping and a "Find" function. So imo InputArray would fulfill this required? (just need a "Find").


And clang fails to resolve the enum type correctly. When I look in the debugger it shows negative numbers for some keys even though you specified uint8_t :(

@fmatthew5876 fmatthew5876 force-pushed the input_refactor branch 2 times, most recently from 26d8306 to 36104b0 Compare May 5, 2020 05:19
@fmatthew5876
Copy link
Contributor Author

I factored out the map implementation into a FlatUniqueMultiMap container and switched the 2 mapping types to pair.

Despite the code being more generic now, I think it's actually a lot more understandable.

Would this data structure also be useful for the FileFinder in my refactor?

I would need a sorted std::string -> std::string mapping and a "Find" function. So imo InputArray > would fulfill this required? (just need a "Find").

Not exactly, this is one is mapping of keys to multiple values, where each (key, value) pair has to be unique. It's not a FlatMap or even a FlatMultiMap.

What you want is just a simple FlatMap that doesn't allow multiple keys or values. I suggest doing a quick benchmark and just pulling in something like boost::flat_map and seeing if it's faster than std::unordered_map for your use case. If it is, we can add an implementation of it for filefinder.

And clang fails to resolve the enum type correctly. When I look in the debugger it shows negative > numbers for some keys even though you specified uint8_t :(

I don't quite follow this? If the type is uint8_t it can't contain negative values. Is sizeof() the type 1 in your debugger session?

Is the bit pattern / hex the same as an unsigned uint8_t? Could it be something funky in how your debugger is printing values?

@fmatthew5876
Copy link
Contributor Author

CMakeLists.txt Show resolved Hide resolved
src/input.cpp Show resolved Hide resolved
@carstene1ns carstene1ns merged commit 2bf2d99 into EasyRPG:master May 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Refactor Testcase available UX For issues affecting the user experience, such annoyances, counter-intuitive or ugly design
Development

Successfully merging this pull request may close these issues.

None yet

4 participants