mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-10 09:06:36 +00:00
docs(display): the mid-panel tear can be compensated; say how, and when it cannot
main's new section called the 1px step at mid-height a panel-scan effect with nothing to fix in the render path. The scan explanation holds, but at one pixel per refresh the step is one refresh of motion and lagging one half cancels it, which this branch does. Rewrite that paragraph, list what compensation covers and what it leaves to the old advice (held frames, other layouts, the emulator), and add a CHANGELOG entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -194,6 +194,12 @@ floor on the release that ships them):
|
|||||||
`api_extractors`). No known plugin imports it. A plugin that does must use
|
`api_extractors`). No known plugin imports it. A plugin that does must use
|
||||||
`src.common` or its own copy of the code.
|
`src.common` or its own copy of the code.
|
||||||
|
|
||||||
|
- `display.scan_order_compensation` (`"auto"` by default): while something
|
||||||
|
scrolls at one pixel per refresh, one half of each panel is shown a refresh
|
||||||
|
behind the other, which removes the 1px step a 1:N-scan panel shows across
|
||||||
|
its middle. Only for layouts whose row order is known; `"off"` disables it.
|
||||||
|
See `docs/SCROLL_PERFORMANCE.md`, "A tear across the middle on fast scrolls".
|
||||||
|
|
||||||
## 3.5.0
|
## 3.5.0
|
||||||
|
|
||||||
New modules a plugin may import via `src.*` (floor on 3.5.0):
|
New modules a plugin may import via `src.*` (floor on 3.5.0):
|
||||||
|
|||||||
@@ -325,10 +325,9 @@ of roughly
|
|||||||
offset ≈ scroll speed × refresh period
|
offset ≈ scroll speed × refresh period
|
||||||
```
|
```
|
||||||
|
|
||||||
Each frame already reaches the panel whole (`SwapOnVSync` swaps complete frames
|
Each frame reaches the panel whole (`SwapOnVSync` swaps complete frames between
|
||||||
between refreshes), so there is nothing to fix in the render path; the shift is
|
refreshes); the shift is created inside a single refresh. Other panel heights
|
||||||
created inside a single refresh. Other panel heights show it too, at the point
|
show it too, at the point where their two scan halves meet.
|
||||||
where their two scan halves meet.
|
|
||||||
|
|
||||||
On the 2×128×64 chain above, which refreshes at about 130 Hz flat out
|
On the 2×128×64 chain above, which refreshes at about 130 Hz flat out
|
||||||
(7.7 ms per pass):
|
(7.7 ms per pass):
|
||||||
@@ -339,7 +338,37 @@ On the 2×128×64 chain above, which refreshes at about 130 Hz flat out
|
|||||||
| 100 px/s | ~0.8 px |
|
| 100 px/s | ~0.8 px |
|
||||||
| 150 px/s | ~1.2 px, plainly visible |
|
| 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
|
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
|
the panel actually achieves first. The library prints the rate with a carriage
|
||||||
@@ -370,7 +399,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
|
should roughly double the refresh rate and halve the offset. That is a cable
|
||||||
change, so measure again afterwards.
|
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.
|
offset is under half a pixel.
|
||||||
|
|
||||||
## Rebuilding the binding
|
## Rebuilding the binding
|
||||||
|
|||||||
Reference in New Issue
Block a user