feat(frontend): wire the View -> Hide Overscan toggle#115
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements the 'Hide Overscan' feature to crop the trailing 15 scanlines of the SNES's extended 239-line display back to 224 lines on the presentation side, including configuration options, UI integration, documentation, and unit tests. The reviewer identified a state desynchronization bug where capturing overscan_active before emulation runs can cause visual glitches during resolution transitions. To resolve this, the reviewer suggested checking dims.1 % 239 == 0 directly at the presentation site and provided code suggestions to update the check and its documentation.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR implements the frontend wiring for View → Hide Overscan, a presentation-only option that crops the SNES 239-line “overscan” mode down to 224 lines at the point where framebuffer bytes are finalized for display (covering both synchronous and emu-thread render paths).
Changes:
- Add
video.hide_overscanto the frontend config (defaultfalse) and expose it in the View menu UI. - Apply overscan cropping during the final present-path framebuffer handling via a new
crop_overscanhelper and add unit tests for native + HD-pack-scaled cases. - Document the feature in
docs/frontend.mdand add an Unreleased changelog entry.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| docs/frontend.md | Documents the new View → Hide Overscan behavior and its determinism boundary. |
| crates/rustysnes-frontend/src/ui_shell.rs | Adds the View menu checkbox that toggles cfg.video.hide_overscan. |
| crates/rustysnes-frontend/src/config.rs | Introduces VideoConfig::hide_overscan with docs and default false. |
| crates/rustysnes-frontend/src/app.rs | Implements the crop and wires it into the present path; adds unit tests. |
| CHANGELOG.md | Adds an Unreleased entry describing the new overscan-crop toggle. |
1ed9651 to
580868a
Compare
Crops the trailing "overscan" scanlines a real 4:3 CRT wouldn't reliably show. SETINI (rustysnes_ppu) already distinguishes the standard 224-line display from a game-opted-in 239-line extension; crop_overscan crops exactly that extra 15-line extension back off, once per frame, at the point where both the synchronous and emu-thread render paths converge on the bytes actually being presented - after HD-pack compositing, run-ahead, and the PresentBuffer handoff have all already run. Crops a fraction (15/239) of the current height rather than a fixed pixel count, so it stays exact under an HD-pack integer upscale too. Presentation-only, additive, false by default. 3 real unit tests. Phase A.3 of the new UI/UX-parity ladder.
emu.fb_dims() was read before run-ahead/HD-pack/the emu-thread PresentBuffer handoff settle on the actual presented (fb, dims), so a resolution switch mid-frame could desync the two, cropping a 224-line frame or skipping the crop on a 239-line one. Check dims.1 % 239 == 0 against the finalized dims at the crop site instead (found in review: gemini-code-assist and Copilot both independently flagged this). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI's --features emu-thread lint job (a combination not covered by --features full, which excludes emu-thread) flagged clippy::manual_is_multiple_of on the dims.1 % 239 == 0 overscan check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
db56724 to
3a58070
Compare
Summary
Phase A.3 of the new UI/UX-parity plan (RustySNES ↔ RustyNES).
show. The SNES's own
SETINIregister (rustysnes_ppu) already distinguishes the standard224-line display from a game-opted-in 239-line extension;
app.rs's newcrop_overscancropsexactly that extra 15-line extension back off.
emu-threadrender pathsconverge on the bytes actually being presented — after HD-pack compositing, run-ahead, and the
PresentBufferhandoff have all already run, so it covers both build variants correctly.15/239) of the current height rather than a fixed pixel count, so itstays exact under an HD-pack integer upscale too (
239 * scale * 15 / 239reduces to exactly15 * scale, no rounding).falseby default —byte-identical to every prior release when unchanged.
Test plan
cargo test --workspace --exclude rustysnes-android— all green, including 3 new real unittests (native resolution, HD-pack-scaled resolution, kept-bytes-untouched)
cargo clippy --workspace --exclude rustysnes-android --all-targets -- -D warnings— cleancargo clippy -p rustysnes-frontend --features full -- -D warnings— cleancargo clippy -p rustysnes-frontend --no-default-features --features wasm-winit,emu-thread— cleancargo clippy -p rustysnes-frontend --target wasm32-unknown-unknown --features wasm-winit -- -D warnings— clean (only a known, pre-existing, unrelatedsave_states.rsfinding remains, confirmed present on a cleanorigin/maincheckout too)cargo fmt --check— cleanRUSTDOCFLAGS="-D warnings" cargo doc --workspace --exclude rustysnes-android --no-deps— clean🤖 Generated with Claude Code