Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions docs/10-fps-limit.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ divisor cap reads its target exactly.
|---|---|---|---|
| mailbox (default) | none, guest asked for FIFO (in-game VSync on) | fifo (`source=guest`) | the display (FIFO) |
| mailbox (default) | none, guest asked for mailbox or immediate | mailbox | nobody |
| mailbox | at the display's rate | fifo (`source=cap-rate`) | gamescope's limiter (divisor 1), one FIFO commit per vblank; no gate, as on master |
| mailbox | at the display's rate, guest asked for FIFO (in-game VSync on) | mailbox (`source=cap-rate-vsync`) | the shim's acquire gate, every vblank; the present wait is answered when the guest queues the present |
| mailbox | at the display's rate, guest asked for mailbox or immediate | fifo (`source=cap-rate`) | gamescope's limiter (divisor 1), one FIFO commit per vblank; no gate, as on master |
| mailbox | below the display's rate | mailbox | the shim's acquire gate, on the display's vblank grid |
| fifo | none | fifo | the display (FIFO) |
| fifo | set | fifo | gamescope's divisor limiter |
Expand Down Expand Up @@ -167,7 +168,23 @@ divisor cap reads its target exactly.
2.5 ms boundary gave 59.0 with ~0.8 a second still replaced; FIFO on master
gives 59.22 on the same scene. Under FIFO gamescope latches one commit per
vblank at its own wakeup (`steamcompmgr.cpp` 8028-8049) and its limiter's
divisor at this cap is 1, which is master's behaviour in this cell. The
divisor at this cap is 1, which is master's behaviour in this cell. That holds
for a guest that asked for mailbox or immediate, which paces itself. A guest
that asked for FIFO (in-game VSync on) waits on each frame's presentation
before starting the next; declared FIFO, that wait is gamescope's `presented`
at its latch, so the whole frame has to fit between two latches, and with
~16 ms of work and three images it fell to 40 fps at 60 Hz (a 1280x960
device, DTD in game: 400 commits per 10 s, 400/600 publishes). That guest is
declared the container's mode with the gate every vblank instead
(`source=cap-rate-vsync`): its wait is answered when it queues the present
(`docs/15`), the gate paces it,
and the swapchain keeps the mailbox image count (Retroid Pocket 6, DTD,
VSync on, cap 60: 59.983 fps, no doubles; with the wait answered at queue,
42 fps before and 60.0 after in a GPU-heavy 1080p scene). This is the only
case answered at queue; every other mailbox or immediate presentation is
answered at the commit, as on master. A container set to
`MESA_VK_WSI_PRESENT_MODE=fifo` never reaches it, so setting a game's
container to fifo restores master's behaviour for that game. The
vblank-grid gate is not kept on top of it: FIFO's back-pressure already holds
the guest, and the two stacked turned a guest whose acquire-to-commit grew
past a period into a 40 fps session on a 60 Hz panel (DTD on a 1280x960
Expand Down Expand Up @@ -210,7 +227,9 @@ divisor cap reads its target exactly.
`present mode: declared=… requested=… source=env|guest|cap-rate|cap-compat
route=gamescope cap=… gate=vsync|clock|off lsfg=…` (`source=guest`: the guest's
own mode, no env or a FIFO request with no cap; `source=cap-rate`: declared FIFO
because the cap is the display's rate, with `gate=off`; `source=cap-compat`:
because the cap is the display's rate and the guest paces itself, with
`gate=off`; `source=cap-rate-vsync`: the container's mode with `gate=vsync`
because the cap is the display's rate and the guest asked for FIFO; `source=cap-compat`:
declared FIFO for want of the stamp lead, with `gate=off`; `gate=clock` is frame
generation's source rate or a cap above the display's rate). With
`GNDRM_LOG_VKSHIM=1` the same line repeats every 10 s with the counters a device
Expand All @@ -221,8 +240,9 @@ divisor cap reads its target exactly.
gate held; `gate_late`, acquires that started after their release point (not
the daemon's `late=` on its `vsync: check` line, which counts publishes that
missed their latch); `skipped`, release points moved past for them;
`slot_waits`, acquires that passed the gate and then waited on a compositor
release for a free image; `commit_lat_us`, the smoothed acquire-to-commit time;
`slot_waits`, acquires that passed the gate (if any) and then waited on a
compositor release for a free image; `commit_lat_us`, the smoothed
acquire-to-commit time (both counted whether or not the gate runs);
`spacing=[<15:… 15-16.2:… 16.2-17.2:… 17.2-20:… >20:…]`, the spacing of
consecutive commits in ms since the last line, the game's own cadence as it
reaches gamescope. Off the override route (the opt-in X11
Expand Down
20 changes: 15 additions & 5 deletions docs/15-fps-limiter-pacing-and-vsync.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ fifo, or no env and a FIFO guest), gamescope's limiter passes every
and holds the guest's commits to it. Declared mailbox, immediate or FIFO-relaxed (mailbox
is the container default), the limiter does not hold them and the shim's acquire gate
releases the guest's frames on the display's vblank grid. A container in one of those
modes under a cap at the display's rate is declared FIFO with no gate
(`source=cap-rate`), as on master: gamescope latches one commit per vblank at its own
modes under a cap at the display's rate is declared FIFO with no gate when the guest
asked for mailbox or immediate (`source=cap-rate`), as on master, and keeps its mode with
the gate every vblank when the guest asked for FIFO (`source=cap-rate-vsync`, `docs/10`).
For the former: gamescope latches one commit per vblank at its own
wakeup, which the shim cannot see under mailbox, and its limiter (divisor 1) paces the
guest (`docs/10`). Under frame generation the app
disarms gamescope's limiter and the shim's time gate holds the source rate instead
Expand Down Expand Up @@ -71,9 +73,17 @@ that rate is the display's.
periods after the commit when no release report arrives first.
- **Mailbox and immediate.** A guest that asked for either does not wait, and neither
would one whose frames go out as mailbox or immediate (frame generation, or the
container's env while the guest believes it has FIFO), so those waits are answered at
the commit rather than holding the guest's frame-latency loop for two periods
(`answers_present_wait_at_commit`).
container's env while the guest believes it has FIFO), so those waits are not held for
the compositor: they are answered at the commit, after the frame's render fence
(`present_wait_answer`). One case is answered earlier, when the guest queues the
present: `source=cap-rate-vsync` (a cap at the display's rate, a container mode other
than FIFO, a guest that asked for FIFO, declared mailbox with the vblank-grid gate).
That guest waits on every frame, and answered after the fence a latency-1 guest
alternated CPU and GPU: Retroid Pocket 6, DTD at 1080p, cap 60, VSync on, 60 Hz, 42 fps
with 23 ms from acquire to commit, the GPU at 65-69 % busy and 550 MHz because its
governor saw the bubbles. Answered at queue, as the X11 route's X server answers
PresentCompleteNotify at the PresentPixmap request, CPU and GPU overlap and the gate
holds the rate (60.0 fps there, 14.3 ms; `at_queue=` in the periodic census).
- **A replaced frame.** Under mailbox a frame can be replaced before it is shown. It is
never committed, released or latched under that id, so its wait is answered when it is
discarded instead of timing out.
Expand Down
16 changes: 16 additions & 0 deletions icd_shim/src/present.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,22 @@ pub(crate) fn committed(id: u64, sc: usize, commit_ns: u64, answer_now: bool) {
}
}

/// Waits answered when the guest queued the present (mailbox or immediate on
/// gamescope's route), for the census.
static AT_QUEUE_N: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);

/// A wait on `id` answered when the guest queued its present, before the
/// frame's render fence (see `proxy::present_wait_answer`).
pub(crate) fn answered_at_queue(id: u64, sc: usize, ns: u64) {
AT_QUEUE_N.fetch_add(1, Ordering::Relaxed);
record_for(id, sc, ns);
}

/// How many waits were answered at queue.
pub(crate) fn at_queue_count() -> usize {
AT_QUEUE_N.load(Ordering::Relaxed)
}

/// Whether the guest enabled each feature. The shim serves both, so the answer
/// is whichever the guest asked for at device creation.
static ID_ENABLED: AtomicBool = AtomicBool::new(false);
Expand Down
Loading