Some time ago, I assembled a Retropie arcade machine, combining a Raspberry PI with this case:
The manual for the case includes instructions to clone the GPIONext repo.
We've found the arcade machine great fun to play, but some friends commented on a feeling of lag playing some of the games. After many false starts, investigation of that led to this PR.
With these changes in place, there is no perceptible lag - and I doubled my high score on an old favourite. 😁
The changes are:
Removing an extraneous sleep from device.py that can cause event processing to be significantly delayed.
Remove extra use of the button debounce delay, as the gpio library already handles that.
Add locks around access to the queue of events, to ensure no events are lost to thread concurrency.
My intuition is that the additional sleep() may have been added to try and prevent events from being lost. Given that python lists are not safe for concurrent modification, event loss was possible if pressEvents() and processQueue() both tried to modify queue at the same time. Adding locks around access to queue should prevent this from happening.
I've included below a detailed analysis of the worst-case lag and how that's addressed by these changes.
Analysis
This analysis is in two parts. The first documents current behavour of the master branch as of commit 2626e5c; the second shows how the changes in this PR reduce lag.
Assumptions
Combo delay using the default of 50ms (ref: gpionext.py L#17)
Button debounce using the default of 20ms (ref: gpionext.py L#26)
Event timer using the sum of combo delay and button debounce, 70ms (ref: gpionext.py L#88)
Button press events occurring every 130ms, this interval chosen to demonstrate worst case lag.
Current behaviour
Timestamp
Event
Actions
device.py
t
Button press B0
Event added to queue
L#247
70ms event timer started
L#254
t + 70ms
Event timer triggers
Event timer triggers processQueue()
L#259
B0 dispatched after 70ms
L#276
Sleep of 70ms starts
L#279
t + 130ms
Button press B1
Event added to queue
L#247
Timer already started
L#257
t + 140ms
Sleep of 70ms ends
Timer reset
L#283
t + 260ms
Button press B2
Event added to queue
L#247
70ms event timer started
L#254
t + 330ms
Event timer triggers
Event timer triggers processQueue()
L#259
B1 dispatched after 200ms
L#276
B2 dispatched after 70ms
L#276
Sleep of 70ms starts
L#279
t + 390ms
Sleep of 70ms ends
Timer reset
L#283
The 200ms delay in dispatching Button press B1 (1/5 of a second) is long enough to impair playability of arcade games in which players routinely use split second timing.
Some time ago, I assembled a Retropie arcade machine, combining a Raspberry PI with this case:
The manual for the case includes instructions to clone the GPIONext repo.
We've found the arcade machine great fun to play, but some friends commented on a feeling of lag playing some of the games. After many false starts, investigation of that led to this PR.
With these changes in place, there is no perceptible lag - and I doubled my high score on an old favourite. 😁
The changes are:
Removing an extraneous sleep from
device.py
that can cause event processing to be significantly delayed.Remove extra use of the button debounce delay, as the gpio library already handles that.
Add locks around access to the queue of events, to ensure no events are lost to thread concurrency.
My intuition is that the additional
sleep()
may have been added to try and prevent events from being lost. Given that python lists are not safe for concurrent modification, event loss was possible ifpressEvents()
andprocessQueue()
both tried to modifyqueue
at the same time. Adding locks around access toqueue
should prevent this from happening.I've included below a detailed analysis of the worst-case lag and how that's addressed by these changes.
Analysis
This analysis is in two parts. The first documents current behavour of the
master
branch as of commit2626e5c
; the second shows how the changes in this PR reduce lag.Assumptions
Combo delay using the default of 50ms (ref: gpionext.py L#17) Button debounce using the default of 20ms (ref: gpionext.py L#26) Event timer using the sum of combo delay and button debounce, 70ms (ref: gpionext.py L#88)
Button press events occurring every 130ms, this interval chosen to demonstrate worst case lag.
Current behaviour
The 200ms delay in dispatching Button press B1 (1/5 of a second) is long enough to impair playability of arcade games in which players routinely use split second timing.
New behaviour