feat(vdisplay/mutter): pin the client's refresh by default; drop PUNKTFUNK_MUTTER_VIRTUAL_REFRESH
The >60 Hz virtual-monitor path (RecordVirtual "modes" with the client's exact WxH@Hz) was gated behind PUNKTFUNK_MUTTER_VIRTUAL_REFRESH, default OFF, after a high-refresh virtual CRTC SIGSEGV'd gnome-shell on session teardown. That crash was since fixed by stopping the screencast before any monitor reconfig, so the gate is dead weight — and a silent footgun: every non-headless GNOME client was capped at Mutter's PipeWire-derived 60 Hz unless they knew the hidden flag. Make it the default: the custom-mode path now runs whenever mode.refresh_hz > 60 (≤60 Hz stays byte-identical to before — Mutter's 60 Hz default is already correct), and the virtual_refresh_enabled() env read is removed. Docs updated (configuration.md env table, vrr-plan.md reference). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -285,15 +285,16 @@ async fn connect(mode: Mode) -> Result<MutterSession> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 3. The virtual monitor. By DEFAULT we let Mutter derive the refresh from the PipeWire
|
// 3. The virtual monitor. For >60 Hz we pin the client's exact WxH@Hz via RecordVirtual's
|
||||||
// framerate (it defaults the virtual monitor to 60 Hz) — universally safe.
|
// "modes" (explicit size + refresh-rate; Mutter ≥ 47) — validated at 5120×1440@240 on Mutter 50
|
||||||
// PUNKTFUNK_MUTTER_VIRTUAL_REFRESH=1 pins the client's exact WxH@Hz via RecordVirtual's "modes"
|
// + NVIDIA. At ≤60 Hz we let Mutter derive the refresh from the PipeWire framerate (its 60 Hz
|
||||||
// (explicit size + refresh-rate; Mutter ≥ 47) for true >60 Hz — validated at 5120×1440@240 on
|
// default is already correct), so the custom-mode path only runs when it buys something.
|
||||||
// Mutter 50 + NVIDIA. (A high-refresh virtual CRTC used to SIGSEGV gnome-shell on teardown; the
|
// (A high-refresh virtual CRTC used to SIGSEGV gnome-shell on teardown, which is why this was
|
||||||
// stop-screencast-before-any-monitor-reconfig teardown below avoids that.)
|
// once gated behind PUNKTFUNK_MUTTER_VIRTUAL_REFRESH; the stop-screencast-before-any-monitor-
|
||||||
|
// reconfig teardown below fixed the crash, so pinning the client's refresh is now the default.)
|
||||||
let mut rec: HashMap<&str, Value> = HashMap::new();
|
let mut rec: HashMap<&str, Value> = HashMap::new();
|
||||||
rec.insert("cursor-mode", Value::from(CURSOR_EMBEDDED));
|
rec.insert("cursor-mode", Value::from(CURSOR_EMBEDDED));
|
||||||
if virtual_refresh_enabled() && mode.refresh_hz > 60 {
|
if mode.refresh_hz > 60 {
|
||||||
let mut vmode: HashMap<&str, Value> = HashMap::new();
|
let mut vmode: HashMap<&str, Value> = HashMap::new();
|
||||||
vmode.insert("size", Value::from((mode.width, mode.height)));
|
vmode.insert("size", Value::from((mode.width, mode.height)));
|
||||||
vmode.insert("refresh-rate", Value::from(mode.refresh_hz as f64));
|
vmode.insert("refresh-rate", Value::from(mode.refresh_hz as f64));
|
||||||
@@ -382,22 +383,6 @@ type CurrentState = (
|
|||||||
type ApplyMon = (String, String, HashMap<String, Value<'static>>); // connector, mode_id, props
|
type ApplyMon = (String, String, HashMap<String, Value<'static>>); // connector, mode_id, props
|
||||||
type ApplyLogical = (i32, i32, f64, u32, bool, Vec<ApplyMon>);
|
type ApplyLogical = (i32, i32, f64, u32, bool, Vec<ApplyMon>);
|
||||||
|
|
||||||
/// Opt-in: pin the virtual output to the client's exact refresh via RecordVirtual "modes" (true
|
|
||||||
/// above-60 Hz). Off by default — Mutter-derived 60 Hz is safe on every host; high-refresh virtual
|
|
||||||
/// CRTCs are validated on Mutter 50 + NVIDIA but behaviour can vary, so it stays opt-in. (The
|
|
||||||
/// teardown SIGSEGV that first motivated this gate is fixed by stopping the screencast before any
|
|
||||||
/// monitor-config change.)
|
|
||||||
fn virtual_refresh_enabled() -> bool {
|
|
||||||
std::env::var("PUNKTFUNK_MUTTER_VIRTUAL_REFRESH")
|
|
||||||
.map(|v| {
|
|
||||||
matches!(
|
|
||||||
v.trim().to_ascii_lowercase().as_str(),
|
|
||||||
"1" | "true" | "yes" | "on"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A DisplayConfig proxy on its own session-bus connection (owned, so it stays alive for the
|
/// A DisplayConfig proxy on its own session-bus connection (owned, so it stays alive for the
|
||||||
/// session — independent of the RemoteDesktop/ScreenCast connection).
|
/// session — independent of the RemoteDesktop/ScreenCast connection).
|
||||||
async fn display_config() -> Result<zbus::Proxy<'static>> {
|
async fn display_config() -> Result<zbus::Proxy<'static>> {
|
||||||
|
|||||||
+2
-2
@@ -24,8 +24,8 @@ on the host display stack. This sidesteps two otherwise-hard blockers entirely:
|
|||||||
displays as VRR-capable. Not fixable by us; the community IDD projects' "can we fake it" issue is
|
displays as VRR-capable. Not fixable by us; the community IDD projects' "can we fake it" issue is
|
||||||
open and unanswered.
|
open and unanswered.
|
||||||
- **KWin/Mutter/wlroots virtual outputs are fixed-mode** (KWin hardcodes 60 Hz + out-of-band
|
- **KWin/Mutter/wlroots virtual outputs are fixed-mode** (KWin hardcodes 60 Hz + out-of-band
|
||||||
`kscreen-doctor` custom modes, `vdisplay/linux/kwin.rs:101,138`; Mutter defaults 60 with the
|
`kscreen-doctor` custom modes, `vdisplay/linux/kwin.rs:101,138`; Mutter pins the client's exact
|
||||||
`PUNKTFUNK_MUTTER_VIRTUAL_REFRESH` opt-in, `mutter.rs:244-258`; Sway takes one
|
WxH@Hz via `RecordVirtual`'s custom modes for >60 Hz, `mutter.rs`; Sway takes one
|
||||||
`--custom WxH@Hz`, `wlroots.rs:93`).
|
`--custom WxH@Hz`, `wlroots.rs:93`).
|
||||||
|
|
||||||
What a true-VRR virtual display *would* add is confined to the source end, exactly two residuals:
|
What a true-VRR virtual display *would* add is confined to the source end, exactly two residuals:
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ picture.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `PUNKTFUNK_KWIN_VIRTUAL_PRIMARY` | `1` | Make the streamed per-session output the sole desktop so plasmashell + windows render on it (not on the headless bootstrap output). Set by the KDE appliance `host.env`. Superseded by the console's **Topology** setting. |
|
| `PUNKTFUNK_KWIN_VIRTUAL_PRIMARY` | `1` | Make the streamed per-session output the sole desktop so plasmashell + windows render on it (not on the headless bootstrap output). Set by the KDE appliance `host.env`. Superseded by the console's **Topology** setting. |
|
||||||
| `PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY` | `1` | GNOME/Mutter equivalent of the above. |
|
| `PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY` | `1` | GNOME/Mutter equivalent of the above. |
|
||||||
| `PUNKTFUNK_MUTTER_VIRTUAL_REFRESH` | `1` | Pin the client's exact WxH**@Hz** via `RecordVirtual`'s custom modes (needed for >60 Hz on Mutter). |
|
|
||||||
|
|
||||||
## Video quality
|
## Video quality
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user