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

Segment Displays: Stack vs Replace Option #1779

Merged
merged 6 commits into from
Mar 16, 2024

Conversation

avanwinkle
Copy link
Collaborator

@avanwinkle avanwinkle commented Mar 16, 2024

This PR adds a new update_method: config option for segment displays with accepted values of "stack" and "replace".

How Segment Displays Work

At an architectural level, segment displays in MPF (currently) are structured similar to Slides in MC. Consider the physical segment display like an MC display, and each set of text like a Slide. Text can include dynamic values (e.g. player variables) that will automatically update when those variables change, like a Widget.

Most importantly, a segment display retains in memory a "stack" of text entries—like an MC display has a stack of slides—with the highest-priority text on top and the lowest-priority text on the bottom. At any given time, the highest-priority text is rendered on the display and when it's removed, the next-highest-priority text is shown in its place.

To ensure the text is always accurate, text entries that have dynamic values will continue to update even when they're not on top. This way, whenever the stack changes the new text will always be correct.

A Different Mental Model

After it's explained, and when considered in parallel to Slides and Widgets, the above architecture for segment displays makes sense—but it's not necessarily intuitive, or how a user would necessarily expect segment displays to work.

The alternative mental model is to consider segment displays like lights or static widgets: send specific text to the display, and it renders that text until told otherwise. In this scenario the game code is responsible for tracking and sending every single text update to the display, which requires more diligence but eliminates the mental overhead of tracking the stacks and priorities.

The new update_method: config option in this PR allows a user to choose which approach to segment display text they want to use, and MPF will correspondingly build and manage a text stack when the config option is "stack", or skip stacking and simply render text directly to the display when the config option is "replace".

Why Does it Matter?

There is a sneaky pitfall if a game is coded with the "replace" model in mind but MPF is running the "stack" model under the hood.

Specifically, the part about "Text can include dynamic values that will automatically update when those variables change". To maintain the current value of a variable, a text entry with a dynamic value creates an event listener to subscribe to that value and update on changes. However in a game coded to explicitly send text updates to the displays, each time new text is sent to the display that references a variable (e.g. a player's score), that new text creates a new event handler and is added to the stack.

On a modern performant computer and a single player game, this extra stack of text entries and event handlers isn't enough to be noticable. But think of a spinner: each spin of the spinner, a new text entry is sent to the segment display with the player score. This text entry creates an event listener for changes to the score, which doesn't matter because on the next score, a new text entry will be stacked on top of that one. Now think of a high-scoring four-player game that uses the spinner a lot. By the end of the game, the segment display may have a stack of 5,000+ text entries, each one with its own event listener.

This stack consumes memory and can slow down a game, but even worse is at the end of the game. All of the event handlers subscribe to the end of the game to cleanly dispose of themselves, and when MPF encounters a stack of thosands of handlers for a single event, that can quickly bring the CPU to its breaking point.

Compatibility and Expected Behavior

I posit that the "replace" mental model is more intuitive and more likely to be implemented by an MPF user, and therefore would make sense to be the default behavior for segment displays. However the "stack" approach has been the default (and only) MPF implementation to date. If anybody is using segment displays and successfully leveraging the stack approach, their game will break if the default is switched to the replacement approach.

Therefore the changes in this PR default the update_method: to be "stack", with a warning that in a future version of MPF (major release only, i.e. 0.58) the default will change to "replace". This allows us to flag it as a breaking change for users upgrading to that version while eventually settling in on the (presumably) more intuitive behavior as default.

Also colors

This PR also adds a minor tweak to the FAST segment display code to track the current segment colors and only send color updates if they change. The color update payload is over 7x that of a normal text payload, and explicitly defined colors on a text update in rapid succession (once again, spinners) can cause buffer overloads. Eliminating the unnecessary color payload reduces the bandwidth requirements and alleviates the risk of malformed text on the display.

STACKS

Copy link

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

@avanwinkle avanwinkle merged commit da81b89 into missionpinball:dev Mar 16, 2024
15 checks passed
@cobra18t
Copy link
Collaborator

Thanks for working on this! This has tripped me up and many other that I know of. Hopefully the new option simplifies it for users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants