Skip to content

video: 16:10 and 21:9 monitor profiles with an aspect ratio choice - #357

Merged
dormancygrace merged 3 commits into
mainfrom
video/monitor-aspects
Oct 21, 2026
Merged

dormancygrace merged 3 commits into
mainfrom
video/monitor-aspects

Conversation

@dormancygrace

@dormancygrace dormancygrace commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

What

An LT6911UXC also offers 16:10 and 21:9 monitors:

Profile Rates (Hz) Codecs
1920×1200 (16:10) 60, 50, 30 H.264, H.265, MJPEG
2560×1600 (16:10) 50, 30 H.264, H.265, MJPEG
2560×1080 (21:9) 60, 50, 30 H.264, H.265, MJPEG
3440×1440 (21:9) 40, 30 H.265 only (H.264 and MJPEG stop at 2560 wide)

Settings → Video → Aspect ratio (16:9, 16:10, 21:9) comes before Resolution, and the resolution list shows the sizes of that ratio. The default stays 16:9, and the quick video menu lists the sizes of the current ratio. Auto is still 16:9 1920×1080, so choosing Auto shows 16:9.

In a Cube or Lite the LT6911UXC takes them as it takes every profile there: with a physical power cycle after the write, and at the fastest rate of the size, because the monitor does not follow the stream rate there.

The LT6911C and LT6911D are measured up to 1080p60 only. They get the two sizes near 1920×1080, at the rates within the 1080p60 pixel clock of 148.5 MHz:

Profile Rates (Hz) Pixel clock
1920×1200 50, 30 127.8 / 75.9 MHz
2560×1080 30 89.7 MHz

The next faster rate is above the limit: 1920×1200@60 is 154.1 MHz and 2560×1080@50 is 150.4 MHz. None of these was tried on those receivers.

How

  • Monitor setting. It holds a profile id: the height of a 16:9 or 16:10 profile, the width of a 21:9 one, because its height is that of a 16:9 profile (2560 is 2560×1080, 3440 is 3440×1440). Saved settings and older clients keep working. /api/vm/video/capabilities lists each mode's id, size and aspect, plus monitor.aspects.
  • Server. server/common/monitor_profiles.go holds the size, aspect and rate table. Rates, profile paths, the refresh-follows-stream selection, validation, H.264/MJPEG input checks and diagnostics use it. Video memory checks count pixels: 2560×1600 and 3440×1440 need what 3840×2160 needs, which the shared 144 MiB pool provides. widerMonitorRatesFor is the one table of which receiver takes which wider profile and rate. Capabilities, the monitor setting, the apply path, the saved-profile fallback and the Cube first-start check all use it. A saved wider id the receiver does not take reads as Auto, for example after a card has moved. GET /api/vm/screen and the capabilities' monitor.selected both report that profile in effect (SelectedMonitorResolution), not the raw file.
  • EDID. scripts/build-wide-monitor-edids.py writes NanoKVM-monitor-<w>x<h>-<hz>.bin: CVT reduced blanking, the 800×600 fallback, a screen size of the right aspect and its own serial per size and rate. Of the stock EDID's established and standard timings, only those smaller than the profile in both directions stay. A mode as wide or as tall as the profile, such as the stock 1920×1080@60 next to 1920×1200, would be output by a GPU instead of the preferred timing; this is the same rule as for the 16:9 profiles. The build adds the RGB range block as for every profile.
  • Capture tiers. In Go and native: 2560×1600 at 50, 3440×1440 at 40. The native resolution list knows the four sizes, and its video memory check counts pixels.

Stand (NanoKVM PCIe, LT6911UXC, r6)

  • Stage A, EDIDs programmed directly, H.265 VI-direct:
    • 1920×1200@60: VI 60; H.265 about 50 next to MJPEG.
    • 2560×1080@60: H.265 60 alone, 34 next to MJPEG (see below).
    • 2560×1600@60: the capture delivered about 50 frames and H.265 15 fps. At 50: 41–49 next to MJPEG.
    • 3440×1440@50: H.265 25 (one frame takes the encoder a whole frame period). At 40: 40.
    • H.264 at 2560×1600@50: 50 fps through the 4:2:0 capture.
    • No CSI errors in any of them.
  • End to end, this branch's libs, server, web and EDID files, through POST /api/vm/video as the page sends it:
    • {monitor:2560,fps:60} → input 2560×1080@60, refreshHz 60.
    • {monitor:3440,fps:40} → 3440×1440@40, H.265 40 fps; MJPEG and H.264 reported unavailable.
    • {monitor:1600,fps:50} → 2560×1600@50.
    • {monitor:1200,fps:60} → 1920×1200@60, then {fps:30} → the 30 Hz file.
    • {monitor:1440,fps:60} → back to 2560×1440@60.
  • After the review fixes (762ab80), with the pruned EDID set: {monitor:1200,fps:60} gives input 1920×1200@60, and {monitor:2560,fps:60} gives 2560×1080@60; the NVIDIA source chose the preferred timing. Then {monitor:1440,fps:60} gives 2560×1440@60. Each time GET /api/vm/screen monitor and the capabilities' monitor.selected equal the posted id.

Not verified

  • H.264 picture at 2560×1600: frame rate only; the picture was not inspected.
  • Cube/Lite with an LT6911UXC, and the LT6911C/LT6911D: no hardware. The wider profiles there are covered by tests only.
  • Windows with the new monitor serials: behavior on the first apply on a real desktop is untested.

Follow-up, not in this PR

2560×1080@60 and 1920×1200@60 are below the presubmit threshold (fast_pixel_rate), so next to an MJPEG viewer their encoding runs serially (2560×1080: 34 fps). Fixed in #358 (2560×1080@60 next to MJPEG: 54–60 fps).

Tests

  • Go:
    • monitor_profiles_test.go: ids and sizes, files, rates within the capture tiers, offered profiles, rates and files per board and receiver (pcie, alpha, beta × ux, c, d), the saved profile in effect;
    • monitor_settings_test.go: the apply path for every board, receiver and profile;
    • monitor_first_start_test.go: a Cube with an LT6911C keeps a saved 1920×1200 through the first-start check;
    • video_test.go: 9 modes and their aspects, codecs by width, availability and monitor validation per receiver, and both screen APIs reporting the monitor in effect.
  • Python: tests/test-wide-monitor-edids.py, added to CI. It checks every size and rate (DTD, refresh, clock ≤270 MHz, unique serials, RGB range) and that the table matches the server's. The 16:9 profiles' validator (modes_above_preferred) runs over every advertised mode: without the pruning it fails on exactly the six 1920×1200 and 2560×1080 profiles. It also checks that the slow receivers' rates fit 148.5 MHz and that their next faster rate does not.
  • Web: 270 tests, including id/aspect choices and the 21:9 size in the MJPEG/H.264 checks; tsc, ESLint and the full Prettier check.
  • Native: contract tests and test-video-recovery-native.py.

Before merging

Per TRANSLATING.md, check the Weblate component first: seven locales (de, en, es, fr, ja, ru, zh) gained videoSettings.aspect.

🤖 Generated with Claude Code

An LT6911UXC with live EDID writes now also offers 1920x1200 and
2560x1600 (16:10) and 2560x1080 and 3440x1440 (21:9). Settings -> Video
gets an Aspect ratio choice before Resolution; 16:9 stays the default and
the quick video menu lists the sizes of the current ratio.

The monitor setting holds a profile id: the height of a 16:9 or 16:10
profile, the width of a 21:9 one, whose height is that of a 16:9 profile.
Saved settings and older clients keep working; the capabilities list
each mode's id, size and aspect. Pool checks count pixels, so 2560x1600
and 3440x1440 need what 3840x2160 needs.

The profiles are CVT reduced blanking with the 800x600 fallback
(scripts/build-wide-monitor-edids.py), one file per rate. Rates and the
capture tiers come from the stand, H.265 VI-direct: 1920x1200 and
2560x1080 at 60 Hz, 2560x1600 at 50 (at 60 the capture delivered about
50 frames and H.265 15 fps), 3440x1440 at 40 (at 50 H.265 delivered 25).
3440x1440 is H.265 only; H.264 and MJPEG stop at 2560 wide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dormancygrace dormancygrace left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 73df3bc, review only as requested. Found three P2 issues, attached inline with proposed fixes and regression coverage. Please address these before merging.

Validation: existing common/vm Go tests passed; 39 web model/i18n tests passed; all 4 wide-EDID tests passed; changed UI files pass Prettier; the existing 40 native pool cases and another 40 cases covering the four new sizes passed. Additional focused probes reproduced the two receiver/API issues using Go's overlay mechanism to redirect filesystem paths to temporary fixtures (production logic unchanged). Passing the resulting moved-card capabilities into the actual web model reproduces issues.monitor = receiver. The existing production EDID validator modes_above_preferred flags the six affected generated profiles.

CI currently has 14 successful checks; the full firmware build is still running. No device testing, source fixes or merge performed. The description's unverified H.264 picture/Windows behavior remains unverified by this review as well.

Comment thread server/service/vm/video.go Outdated
Comment thread scripts/build-wide-monitor-edids.py
Comment thread server/common/monitor.go Outdated
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Build outputs

Compared with the build of main at 3a5263d.

5 changed, 153 added, 143 removed of 551 outputs.

directory files
changed native native/libkvm.so
native/libkvm_mmf.so
changed opensbi opensbi/build-manifest.json (records the hash of platform/build.sh)
changed server server/NanoKVM-Server
changed web web/index.html
added tools tools/edid/NanoKVM-monitor-1920x1200-30.bin
tools/edid/NanoKVM-monitor-1920x1200-50.bin
tools/edid/NanoKVM-monitor-1920x1200-60.bin
tools/edid/NanoKVM-monitor-2560x1080-30.bin
tools/edid/NanoKVM-monitor-2560x1080-50.bin
tools/edid/NanoKVM-monitor-2560x1080-60.bin
tools/edid/NanoKVM-monitor-2560x1600-30.bin
tools/edid/NanoKVM-monitor-2560x1600-50.bin
tools/edid/NanoKVM-monitor-3440x1440-30.bin
tools/edid/NanoKVM-monitor-3440x1440-40.bin
added web 143 files
removed web 143 files
Hashes: outputs-sha256 artifact of this run.

dormancygrace and others added 2 commits October 21, 2026 15:04
…tor in effect

Review of #357:

- Which receiver takes which profile is one table. The LT6911UXC takes
  every 16:10 and 21:9 profile, in a Cube or Lite too, with the power
  cycle every profile needs there and at the fastest rate of the size.
  The LT6911C and LT6911D take those within the 148.5 MHz pixel clock of
  1080p60, the limit they are measured at: 1920x1200 at 50 and 30 Hz and
  2560x1080 at 30 Hz (not yet tried on them). Capabilities, the monitor
  setting, the apply path, the saved-profile fallback and the Cube first
  start check use it.
- The wide EDIDs kept the stock EDID's standard 1920x1080 at 60 Hz and
  its other established and standard timings. A GPU outputs a mode as
  wide or as tall as the preferred one instead of it. Those are cleared
  now; smaller ones stay as fallbacks, as in the 16:9 profiles, and the
  test runs the 16:9 profiles' validator over every advertised mode.
- GET /api/vm/screen and the capabilities' monitor.selected read the
  saved file directly, so a moved card reported a 16:10 or 21:9 id it
  cannot use. Both now report the profile in effect
  (SelectedMonitorResolution), Auto in that case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The LT6911C and LT6911D get the two sizes near 1920x1080 at the rates
within the 1080p60 pixel clock, not every profile within it (2560x1600
at 30 Hz would fit too). A Cube with an LT6911C keeps a saved 1920x1200
through the first-start EDID check and writes its 50 Hz file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dormancygrace dormancygrace left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed cfc3761, including the changes since 73df3bc. All three earlier findings are addressed:

  • Receiver/board policy is now explicit and shared through widerMonitorRatesFor/WiderMonitorProfileOffered. Cube/Lite + UXC support is a documented scope change, using the existing power-cycle flow and fastest profile; it no longer contradicts a live-write-only restriction. C/D get only the two explicitly listed slower sizes, and selection, apply, fallback and first-start handling agree.
  • Established/standard EDID timings are pruned as well as detailed modes. The existing all-modes validator now covers every generated wide profile, including the six previously failing combinations.
  • Both screen responses use SelectedMonitorResolution, so an unsupported saved wide profile reports Auto consistently.

Local verification on this head: go test -tags teststub,nomsgpack -count=1 ./common ./service/vm passed; 39 video-model/i18n tests passed; all 6 wide-EDID tests passed. No new actionable code findings in this re-review.

Limits: no device tests performed by me. The new C/D timings remain unverified on those receivers; fitting within the 1080p60 pixel clock is not hardware qualification. At review time, 13 CI jobs succeeded; full build/hash was still running. The aggregate CodeQL check was neutral ('3 configurations not found'), so that is not a clean all-configurations code-scanning result even though the individual Analyze jobs succeeded.

Review only, as requested for #357; no source edits or merge.

@dormancygrace
dormancygrace marked this pull request as ready for review October 21, 2026 12:19

@dormancygrace dormancygrace left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge-readiness follow-up: the user has now authorized merging. The head is unchanged from the re-review (cfc3761); all three earlier findings remain addressed, with no new code findings. All 14 build/test/Analyze jobs have completed successfully, including full build/hash. The aggregate CodeQL result remains neutral with the previously recorded missing-configuration warning; it is not being represented as a successful PR alert comparison.

Fetched Hosted Weblate's current export immediately before publication: identical to current origin/main, no outstanding translation commits; component unlocked. Repository-maintenance API is not accessible (403); no lock/flush was attempted or claimed. Merging with a regular merge, preserving translation history. Hardware limitations for the new C/D modes remain documented.

@dormancygrace
dormancygrace merged commit d43f86f into main Oct 21, 2026
15 checks passed
@dormancygrace
dormancygrace deleted the video/monitor-aspects branch October 21, 2026 12:37
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.

1 participant