vnc: read only the dirty columns, not whole scanlines#3627
Closed
bitranox wants to merge 2 commits into
Closed
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Optimizes VNC dirty-rect reads to only fetch the dirty columns within dirty rows, instead of full scanlines, reducing VRAM traffic for partial updates.
Changes:
- Extends
Framebuffer::read_linetrait with anxstart-column parameter and updates all implementations. - Adds
View::read_line_atin the framebuffer device, withread_linekept as a whole-line wrapper. - Updates
UpdateStatedevice-dirty path to read only the dirty horizontal span and adds a unit test.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| workers/vnc_worker/vnc/src/update_state.rs | Read only dirty columns per dirty rect; new test asserting non-dirty pixels aren't read. |
| workers/vnc_worker/vnc/src/traits.rs | Updates read_line signature/docs to include start column x. |
| workers/vnc_worker/vnc/src/server.rs | Updates mock/forwarding read_line impls for new signature. |
| workers/vnc_worker/vnc/examples/vncserver.rs | Updates example read_line to support starting column. |
| workers/vnc_worker/src/lib.rs | Wires SharedView::read_line to View::read_line_at. |
| vm/devices/framebuffer/src/lib.rs | Adds read_line_at, refactors offset/length math, keeps read_line as wrapper. |
| Guide/src/reference/devices/vnc/architecture.md | Updates docs to describe dirty-column reads. |
smalis-msft
reviewed
Jun 3, 2026
smalis-msft
reviewed
Jun 3, 2026
Contributor
Author
|
@smalis-msft : sorry, I am a bit behind, I was chasing the whole day this nasty bug - skimming the new firmware and my own additions : #3646 |
Contributor
|
No rush! We're plenty busy too haha, take your time. |
When forwarding device dirty rectangles, the VNC server read only the dirty rows but the full screen width of each, ignoring the rectangle's horizontal extent. For a tall, narrow update (a dragged scrollbar, a text caret column, a vertical widget) that reads far more VRAM than needed, up to roughly screen width over rectangle width, which can approach a full-frame read for a tiny change. Full-width updates were already optimal. Read only the dirty columns. The non-dirty columns of a dirty row are already correct from the cur_fb/prev_fb swap (they did not change), and the encoder only emits the merged rectangles, so the result is identical, just with less VRAM traffic. - framebuffer: add `View::read_line_at(line, x, data)`; `read_line` delegates to it with x = 0, so existing callers are unchanged. - vnc `Framebuffer::read_line` gains a start-column `x`; the device-dirty loop reads `[r.x, r.x + r.w)` into the matching slice of cur_fb. The full-frame read and tile-diff fallback pass x = 0. - Add a test asserting only the dirty columns are read (a changed pixel outside the dirty rect on a dirty row is not picked up). - Correct the architecture doc, which described this partial read as if it were already implemented.
52f5381 to
01e74d6
Compare
Contributor
Author
|
merged into #3665 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
When forwarding device dirty rectangles, the VNC server read only the dirty
rows but the full screen width of each, ignoring the rectangle's horizontal
extent. For a tall, narrow update (a dragged scrollbar, a text caret column,
a vertical widget) that reads far more VRAM than needed, up to roughly screen
width over rectangle width, which can approach a full-frame read for a tiny
change. Full-width updates were already optimal.
This reads only the dirty columns. The non-dirty columns of a dirty row are
already correct from the cur_fb/prev_fb swap (they did not change), and the
encoder only emits the merged rectangles, so the output is identical, just
with less VRAM traffic.
What changed
framebuffer: addView::read_line_at(line, x, data);read_line(line, data)now delegates to it withx = 0.Framebuffer::read_linegains a start columnx. The device-dirtyloop reads
[r.x, r.x + r.w)into the matching slice ofcur_fb; thefull-frame read and the tile-diff fallback pass
x = 0.vncserverexample for the new signature. This also fixes alatent byte-offset bug in its
read_line: it indexed the byte view of theVec<u32>framebuffer with a pixel offset, so every line after the firstread the wrong region. Example only, no effect on shipping code or tests.
On the API shape
The shared
framebuffercrate keepsread_lineand addsread_line_at, soexisting callers (for example the screenshot capture in petri) do not change.
The vnc
Framebuffertrait is internal to the vnc crate, so it takes thestart column directly rather than carrying two methods. There is an inline
comment on
View::read_linenoting this.The byte arithmetic in
read_line_at, including the 4-bytes-per-pixel factorfor the 32bpp framebuffer, is carried over unchanged from the previous
read_line(which only read whole lines). The one addition is thestart-column offset that
read_linedid not allow.Testing
outside the dirty rectangle on a dirty row is not picked up.
cargo fmt --all -- --check,cargo clippy --workspace --all-targets, andcargo doc --workspace --no-depsare clean.correctly with the column-limited reads, no stale or torn columns around
updated regions.
Docs
Corrected the architecture guide, which described this partial read as if it
were already implemented.