feat(vdisplay/mutter): pin the virtual output to the client's refresh (>60 Hz)
RecordVirtual without a "modes" property makes Mutter derive the virtual monitor's refresh from the PipeWire stream framerate and default to 60 Hz — so a 240 Hz client mode rendered at 60 (the encoder just padded to 240 with duplicate frames). Pass an explicit "modes" entry (size + refresh-rate + is-preferred) so Mutter creates the virtual monitor at the client's exact WxH@Hz. Mutter >= 47; older Mutter ignores the unknown key (60 Hz fallback, no regression). Confirmed first via raw D-Bus on the box, then validated end-to-end: the virtual output Meta-0 reports 1920x1080@240.00 and the host encodes 480 *immediate* (real, not paced) frames per 2 s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,7 +138,7 @@ fn session_thread(setup_tx: Sender<Result<u32, String>>, stop: Arc<AtomicBool>,
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = match connect().await {
|
let session = match connect(mode).await {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = setup_tx.send(Err(format!("{e:#}")));
|
let _ = setup_tx.send(Err(format!("{e:#}")));
|
||||||
@@ -190,7 +190,7 @@ struct MutterSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run the four-step handshake (see module docs).
|
/// Run the four-step handshake (see module docs).
|
||||||
async fn connect() -> Result<MutterSession> {
|
async fn connect(mode: Mode) -> Result<MutterSession> {
|
||||||
let conn = zbus::Connection::session()
|
let conn = zbus::Connection::session()
|
||||||
.await
|
.await
|
||||||
.context("connect session D-Bus")?;
|
.context("connect session D-Bus")?;
|
||||||
@@ -243,9 +243,18 @@ async fn connect() -> Result<MutterSession> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 3. The virtual monitor. Size/refresh follow the PipeWire format negotiation.
|
// 3. The virtual monitor, pinned to the client's exact mode via RecordVirtual's "modes"
|
||||||
|
// (explicit size + refresh-rate; Mutter ≥ 47). WITHOUT it Mutter derives the virtual monitor's
|
||||||
|
// refresh from the PipeWire stream framerate and defaults to **60 Hz** — so a >60 Hz client
|
||||||
|
// mode (e.g. 240) renders at 60 and only the encoder pads to 240 (duplicate frames). Older
|
||||||
|
// Mutter that doesn't know the key just ignores it and falls back to the 60 Hz default.
|
||||||
|
let mut vmode: HashMap<&str, Value> = HashMap::new();
|
||||||
|
vmode.insert("size", Value::from((mode.width, mode.height)));
|
||||||
|
vmode.insert("refresh-rate", Value::from(mode.refresh_hz as f64));
|
||||||
|
vmode.insert("is-preferred", Value::from(true));
|
||||||
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));
|
||||||
|
rec.insert("modes", Value::from(vec![vmode]));
|
||||||
let stream_path: OwnedObjectPath = sc_session
|
let stream_path: OwnedObjectPath = sc_session
|
||||||
.call("RecordVirtual", &(rec,))
|
.call("RecordVirtual", &(rec,))
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user