Skip to content

Uc8279X4Driver: implement displayWindow (regional refresh) - #91

Open
webmatze wants to merge 1 commit into
Free-Ink:mainfrom
webmatze:fix/uc8279x4-display-window
Open

Uc8279X4Driver: implement displayWindow (regional refresh)#91
webmatze wants to merge 1 commit into
Free-Ink:mainfrom
webmatze:fix/uc8279x4-display-window

Conversation

@webmatze

@webmatze webmatze commented Sep 8, 2026

Copy link
Copy Markdown

Uc8279X4Driver does not override displayWindow(), so it inherits
PanelDriver's base implementation, which repaints the whole panel with
RefreshMode::Fast. A windowed request therefore compiles, runs, looks correct
and silently does nothing of what the caller asked for.

The driver already issues PTIN -> PTL -> DRF -> PTOUT with a hardcoded
full-panel rectangle, so this routes that existing PTL descriptor through an
optional per-refresh window and keeps the full-panel values as the default.
Behaviour is unchanged when no window is active. Gate coordinates keep the
120-gate visible offset, so framebuffer row y maps to gate line
gateOffset + y.

It falls back to the whole-panel Fast path whenever a differential DU would be
invalid: no synced OLD plane, a pending full clear, out of bounds, or a
non-byte-aligned x/w. displayFinish() still re-syncs the OLD plane from the
full framebuffer, so a later partial diffs against a complete baseline.

Measured on an X4 Pro (UC8279)

A window does not save time — the controller scans all 600 addressed gate
lines regardless of PTL:

window rows refresh vs. full panel
32 514 ms 92%
128 523 ms 94%
480 556 ms 100%

What it does save is area, and that turned out to matter more. A DU partial
accumulates residual charge wherever it lands. Refreshing only the changed line
per keystroke, instead of the whole page, kept residue off the rest of the
display and let the periodic ghost-scrub interval go from every 10 partials to
every 40. That is the difference between a scrub flash the writer notices
constantly and one they rarely see.

Found while building a writing firmware for the X4 Pro
(webmatze/pocket-x-writer), where
the change is running on hardware.

🤖 Generated with Claude Code

@itsthisjustin

Copy link
Copy Markdown
Contributor

I'll take a look at this and compare it to an upcoming fix i was going to push today for the same thing. Thanks for the PR!

@itsthisjustin

Copy link
Copy Markdown
Contributor

Can you compare against the latest code now?

Without an override the base PanelDriver::displayWindow repaints the whole
panel with RefreshMode::Fast, measured at ~554 ms on an X4 Pro -- so windowed
requests silently cost the same as full ones and per-keystroke updates are
unusable.

The driver already issued PTIN -> PTL -> DRF -> PTOUT with a hardcoded
full-panel rectangle. This routes the PTL descriptor through an optional window
set for the duration of one refresh, keeping the existing full-panel values as
the default so behaviour is unchanged when no window is active. Gate
coordinates keep the 120-gate visible offset, so framebuffer row y maps to gate
line gateOffset + y.

Falls back to the whole-panel Fast path whenever a differential DU would be
invalid: no synced OLD plane, a pending full clear, out-of-bounds, or a
non-byte-aligned x/w. displayFinish still re-syncs the OLD plane from the full
framebuffer, so a later partial diffs against a complete baseline.
@webmatze

Copy link
Copy Markdown
Author

Rebased onto 4f6a7a2 and had a look.

As far as I can tell nothing on main covers the changes from my PR yet:

  • PanelDriver::displayWindow still defaults to display(..., RefreshMode::Fast, ...), i.e. a whole-panel repaint.
  • Uc8279X4Driver still doesn't override it — only PaperMonoDriver and Ssd1677Driver do.
  • The PTL (0x90) descriptor in Uc8279X4Driver::displayStart is still the hardcoded full-panel rectangle (0..w-1, gateOffset..gateOffset+h-1).

Your changes from 39606d5 pulls in the same direction as this MR but from the other side.

My measurements said the window buys no time, because the DRF scans all 600 addressed gate lines and the two full-plane uploads are a fixed floor.

Your beginTxn/rawWriteBytes batching in streamPlane attacks the upload half of that floor, and the window path goes through streamPlane unchanged in both places — so the two compose rather than overlap.

So I think the changes here are still needed.

@webmatze
webmatze force-pushed the fix/uc8279x4-display-window branch from 6a395d3 to adc7310 Compare September 10, 2026 09:55
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