Segment Displays: Stack vs Replace Option #1779
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.