mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
feat(display): compensate for the panel's scan order while scrolling (#634)
A 1:N-scan HUB75 panel lights the two rows either side of its middle at opposite ends of each refresh, so a scroll at one pixel per refresh shows a 1px step across the middle of every panel. While something scrolls at one frame per refresh, DisplayManager now shows the half whose seam row lights first one refresh behind the other (src/scan_order.py), which lines the two up again. Only for layouts whose row order is known; display.scan_order_compensation "off" disables it. Confirmed on hdpi before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -495,10 +495,9 @@ of roughly
|
||||
offset ≈ scroll speed × refresh period
|
||||
```
|
||||
|
||||
Each frame already reaches the panel whole (`SwapOnVSync` swaps complete frames
|
||||
between refreshes), so there is nothing to fix in the render path; the shift is
|
||||
created inside a single refresh. Other panel heights show it too, at the point
|
||||
where their two scan halves meet.
|
||||
Each frame reaches the panel whole (`SwapOnVSync` swaps complete frames between
|
||||
refreshes); the shift is created inside a single refresh. Other panel heights
|
||||
show it too, at the point where their two scan halves meet.
|
||||
|
||||
On the 2×128×64 chain above, which refreshes at about 130 Hz flat out
|
||||
(7.7 ms per pass):
|
||||
@@ -509,7 +508,37 @@ On the 2×128×64 chain above, which refreshes at about 130 Hz flat out
|
||||
| 100 px/s | ~0.8 px |
|
||||
| 150 px/s | ~1.2 px, plainly visible |
|
||||
|
||||
### What changes it
|
||||
### What the display does about it
|
||||
|
||||
At one pixel per refresh, the fastest crisp speed, the step is exactly one
|
||||
refresh's worth of motion, so it can be cancelled: show one half of the panel
|
||||
a refresh behind the other -- the half whose row at the seam lights at the
|
||||
start of each refresh. The two rows either side of the seam then show the same
|
||||
moment again. What is left is a
|
||||
lean of one pixel per half from top to bottom, continuous across the panel,
|
||||
which reads as nothing where the step read as a tear. `DisplayManager` does
|
||||
this while something scrolls at one frame per refresh
|
||||
(`display.scan_order_compensation`, `"auto"` by default, `"off"` to disable;
|
||||
the geometry is in `src/scan_order.py`). The lagging rows come from the
|
||||
previous frame the display presented, so it works for Vegas and every plugin
|
||||
ticker without knowing how they scroll.
|
||||
|
||||
Checked on hdpi (4×128×64 on one chain, rotated 180, 2026-09-24) before it was
|
||||
written: `scan_mode: 1` (interlaced) made the step vanish but turned moving
|
||||
edges grainy, and halving the speed halved it, so it is the scan and not a torn
|
||||
frame. With the compensation the step is gone at 90 px/s.
|
||||
|
||||
It is left off where the row order is unknown or the maths does not hold:
|
||||
|
||||
- **Slower speeds**, where each frame is held for two or more refreshes. The
|
||||
offset there is half a pixel or less, and cancelling it would need a lag of
|
||||
a fraction of a frame.
|
||||
- **Other layouts:** pixel mappers other than a 0 or 180 degree rotation
|
||||
(U-mapper, 90/270), non-zero `multiplexing`, interlaced `scan_mode`, and a
|
||||
canvas remapped to another height (double-sided mode).
|
||||
- **The emulator,** which has no scan order.
|
||||
|
||||
### When it cannot apply
|
||||
|
||||
Only a shorter scan period (a faster refresh) or a slower scroll. Measure what
|
||||
the panel actually achieves first. The library prints the rate with a carriage
|
||||
@@ -540,7 +569,7 @@ on its own output and set `parallel` to the number of outputs used and
|
||||
should roughly double the refresh rate and halve the offset. That is a cable
|
||||
change, so measure again afterwards.
|
||||
|
||||
Short of rewiring, keep fast scrolls moderate: at the default 50 px/s the
|
||||
Short of rewiring, keep fast scrolls moderate on those layouts: at 50 px/s the
|
||||
offset is under half a pixel.
|
||||
|
||||
## Rebuilding the binding
|
||||
|
||||
Reference in New Issue
Block a user