Simplify documentation and website; expand shortcut guides

This commit is contained in:
Samuel Onoja committed 2026-09-30 07:00:20 +01:00
1 parent 46509f880a
commit 4d60c296a7
26 files changed
+1060 -1032

No files matched your search

+39 -206
View File
@@ -1,217 +1,50 @@
# Release performance baseline
# Performance measurements
Measured 2026-09-27 at commit `08a5866`, using
`cargo build --release --locked -p ferese -p ferese-shell -p feresectl`.
Use release builds and a fixed workload when comparing performance. Record the
commit, output size, scale, refresh rate, graphics driver, and whether the session
is nested or direct. Keep the nested preview visible: host occlusion changes
rendering behavior.
Two disposable nested sessions used separate runtime/config directories, default
configuration, the bundled wallpaper, and the release shell. Performance logging
was enabled with `FERESE_TRACE_PERFORMANCE=1`. The nested output reported
1422 × 1696 physical pixels, 2× scale and 60 Hz. The parent DRM output was a
2880 × 1800 eDP panel at 120 Hz and 2× scale.
## Collect a sample
## Measurements
```sh
cargo build --release --locked -p ferese -p ferese-shell -p feresectl
FERESE_TRACE_PERFORMANCE=1 target/release/ferese --backend nested \
--grant-effects --grant-shell-control -- target/release/ferese-shell
```
CPU is process CPU time from `/proc/PID/stat` divided by elapsed monotonic time,
expressed as a percentage of one core. First-run samples lasted 10 seconds;
second-run samples lasted 5 seconds. These are short observational samples,
not statistically established improvements or before/after comparisons.
Compare idle, several windows, overview, Settings navigation, translucent
popovers, and screen capture separately. Allow startup to settle and repeat each
sample. Record process CPU, child-process CPU, RSS, render attempts, and damaged
frames. Keep build work and unrelated activity outside the sampling interval.
| Scenario | Compositor CPU, run 1 / run 2 | Shell CPU, run 1 / run 2 |
CPU percentages below use one core as 100%. Short samples are observations,
not guarantees or hardware-session benchmarks.
## Historical results
On 2026-09-27 at `08a5866`, two nested runs used a 1422 × 1696 output at 2× scale,
reporting 60 Hz on a 120 Hz host. Samples lasted 5–10 seconds.
| Scenario | Compositor CPU, two runs | Shell CPU, two runs |
| --- | --- | --- |
| Idle shell, no application window | 5.3% / 5.8% | 1.5% / 1.6% |
| Idle, no application window | 5.3% / 5.8% | 1.5% / 1.6% |
| Overview with one terminal | 18.6% / 16.2% | 1.3% / 1.4% |
| After window transitions, terminal still open | 6.6% / 6.4% | 1.5% / 1.4% |
| After transitions | 6.6% / 6.4% | 1.5% / 1.4% |
Compositor RSS was approximately 81–82 MiB initially and 91–92 MiB after the
short exercise. This does not establish a leak or long-term memory stability.
Main-thread voluntary context switches were approximately 162–165/s at idle
and 498–505/s in overview; these are scheduling proxies, not hardware wakeups.
Compositor RSS increased from about 81–82 MiB to 91–92 MiB during the exercise.
These samples do not establish a leak or long-term stability. A follow-up had
lower rendering rates, so the original observations did not prove duplicate scheduling.
A five-second idle log interval contained 60 damaged frames and 531 no-damage
attempts, approximately 118 attempts/s. A later overview interval contained
591 damaged frames in 5001 ms. Nested frame scheduling and overview damage
therefore deserve investigation first. The parent runs at 120 Hz, so the
mismatch with the nested output's reported 60 Hz must be understood before
attributing these counts to duplicate scheduling.
A 20-second status-worker comparison removed five commands per poll (15 to 10).
Total measured CPU time including child processes fell from 2.41 to 1.90 seconds.
The timing wrapper was included, and this was one comparison.
## Behavioral exercise
For push status delivery (`4bf0757` to `b0583eb`), a synthetic transport test
measured median delivery delay of 129.398 ms before and 0.130 ms after. It excluded
queries, UI dispatch, and painting. Visible shell samples suggested lower typical
CPU, but an after-run spike raised the mean; they did not establish a robust
average CPU saving.
Both sessions exited with status 0. The probes created a terminal, entered and
left overview, and toggled maximize, fullscreen and floating twice each.
The second session exercised `theme.typography.font_family`, wallpaper fit/fill,
and replacement image paths through filesystem-driven reloads. Logs confirmed
accepted reloads and replacement wallpaper decoding. This verifies execution
and liveness, not pixel-perfect appearance or GPU allocation-failure recovery.
The first probe used an incorrect font key; only the second probe counts as
font reload coverage.
Raw local artifacts:
- `/tmp/ferese-profile-7cbrpopn/{results.json,session.log}`
- `/tmp/ferese-profile-h2ypepn6/{results.json,session.log}`
- Probe scripts: `/tmp/ferese-profile.py` and `/tmp/ferese-profile-reloads.py`
## Limits and next work
The installed DRM compositor is release `20260927-042025-168287`; its binary hash
differs from the tested release build. A read-only host sample measured 9.8%
compositor CPU and 1.7% shell CPU, but overlapped the nested workload and is not
an idle baseline or validation of the new DRM code. No session was replaced.
Only one monitor was connected, so multi-monitor behavior remains untested.
Next investigate nested refresh scheduling and repeated overview damage, using
these scenarios for before/after measurements. Preserve client frame callbacks,
animation pacing and idle responsiveness. Status command polling remains a
secondary target; blur invalidation needs a separate translucent-material
workload before ranking its cost. New-build DRM and multi-monitor validation
remain necessary before claiming hardware performance improvements.
## Follow-up investigation
A repeat baseline (`/tmp/ferese-profile-n8437l1i`) confirmed 60 Hz after startup
but ran near 60 attempts/s rather than 118. Compositor CPU was 3.0% idle, 9.2%
in overview and 4.6% after transitions. The earlier high-rate behavior is not
consistently reproduced, so it is not sufficient evidence of duplicate redraws.
The candidate refresh-period guard was discarded after testing.
The guard run (`/tmp/ferese-profile-llcrkvm5`) measured 4.0% idle, 8.6% overview,
and 3.6% after transitions. Render attempts fell slightly below the nominal
cadence. This did not establish a benefit, so no frame-pacing change was kept.
The next investigation is unconditional thumbnail damage in overview.
## Broader profiling (visible preview)
Artifacts: `/tmp/ferese-profile-all-0lhodit4/`; script:
`/tmp/ferese-profile-all.py`. This run used the unchanged release compositor,
release shell and release Settings. The user kept the preview visible after an
earlier hidden-window run proved that host occlusion invalidated render samples.
Six-second samples are exploratory, not statistically stable benchmarks.
The captured output was 2856 × 1696 pixels; the preview was resized during the
session, so these workloads must not be compared as if output size were fixed.
| Scenario | Compositor CPU | Shell CPU | Shell waited-child CPU | Settings CPU |
| --- | ---: | ---: | ---: | ---: |
| Idle shell | 4.33% | 1.33% | 7.50% | — |
| Three terminals | 6.67% | 1.50% | 11.33% | — |
| Overview, three terminals | 17.50% | 1.17% | 8.17% | — |
| Settings open, idle | 6.17% | 1.50% | 10.83% | 0.00% |
| After capture/window transitions | 8.50% | 1.33% | 11.00% | 2.50% |
Percentages use one CPU core as 100%. Child CPU is Linux's accumulated CPU time
for reaped children, separate from the shell process. This includes the timing
wrapper: a PATH-local `timeout` wrapper invokes GNU time and the real timeout
command. Read-only PipeWire queries use the host runtime socket; no status
control actions were sent. The native installed shell cross-check, without
instrumentation, measured 2.0% self CPU plus 8.8% waited-child CPU over ten
seconds. It is an older binary and a different scene, so this corroborates the
existence of subprocess overhead rather than providing an exact comparison.
The preview recorded 273 successful status command invocations across 21 polls:
13 per poll. The three login1 capability calls consumed 1.88 seconds total wall
time, about 90 ms/poll. Three nmcli queries consumed 1.35 seconds total; two wpctl
queries consumed 0.80 seconds. GNU time's hundredth-second granularity makes
per-command CPU totals unsuitable for precise attribution. Stable capability
queries, device metadata and frequently changing values currently share one
poll cycle. Separate refresh policies or service subscriptions deserve priority.
Settings mapped in 109 ms, measured from process launch until IPC reported it
focused (not first completed paint). Idle Settings RSS was 108068 KiB, with 38
threads and 65 descriptors. Zero measured CPU over six seconds means below this
sample's resolution, not a guarantee of zero work. Startup font enumeration was
not isolated separately. Settings interaction/search/page-switch profiling is
still outstanding.
Twenty `feresectl get-outputs` calls had median process-inclusive latency of
3.05 ms and maximum 3.95 ms. Five captures took 1.13 seconds total, including
client startup, transfer and PNG encoding. Capture was enabled only in the
isolated compositor with `FERESE_ENABLE_SCREENCOPY=1`. The first screenshot was
visually inspected and showed the shell and Settings rendered. It does not
measure GPU readback independently. Window maximize/fullscreen/layout toggles
completed, but there was no instrumented input-to-photon latency measurement.
The four-second visible overview perf recording (`cpu-clock:u`, 999 Hz) collected
471 samples with no lost samples. Shared-object attribution: 47.77% Ferese
executable (including statically linked dependencies), 30.36% Mesa Gallium,
14.23% libc, 2.97% Wayland client library. Approximately 96.4% of samples were in
the main compositor thread. Texture upload/memory-copy paths appear in the
sampled stacks. This is user-space CPU sampling, not GPU timing; short samples
and incomplete frame-pointer unwinding limit function-level attribution.
The earlier broad run (`/tmp/ferese-profile-all-boiq_r30`) had a 37-second render
reporting gap while hidden, no usable perf samples, and a disabled screencopy
protocol. Its render/overview and capture results are excluded. Its bare idle
sample was 1.0% CPU, but visibility differences preclude subtracting it from the
visible shell run.
Next priority: shell status subprocesses. Then investigate sustained overview
damage and texture uploads with controlled window visibility and dimensions.
Control-center interaction, widgets, translucent-material workloads, long-term
memory growth, new-build DRM and multi-monitor runs remain unprofiled. No source
changes or profiling-note commits were made during this profiling pass.
## Status-worker optimization comparison
Compared the production status module at `08a5866` with the new single-command
brightness reader and reusable native D-Bus connections. A standalone harness
started `Service::start(None)`, consumed updates every 250 ms, and ran for 20
seconds without a UI. Both variants used the same host runtime/services and
command timing wrapper. Artifacts: `/tmp/ferese-status-compare/`.
Both completed nine polls. Notifications were available in this comparison, so
the original worker launched 15 commands/poll and the new one launched 10:
135 versus 90 invocations. Four busctl launches and one brightnessctl launch per
poll were removed. When notifications are absent, the equivalent counts are
13 versus 8. Poll frequency, fresh capability queries and post-action generation
handling remain unchanged; no status-value caching was introduced.
GNU time measured total user+system CPU, including waited children: 2.41 seconds
before, 1.90 seconds after over 20.01 seconds each (about 21% lower in this single
comparison). Timing-wrapper overhead is included, so this is not a precise
uninstrumented CPU improvement estimate. Native D-Bus adds persistent sockets
and a runtime thread pool; UI samples observed roughly 26–28 shell threads and
48 descriptors versus roughly 18 threads and 42 descriptors previously.
One UI run showed a transient self-CPU spike; a subsequent perf recording
identified tiny-skia raster work rather than D-Bus queries among its hot paths.
The UI-free comparison avoids treating variable UI activity as polling cost.
Validation: workspace suite and explicit private-dbus-daemon test passed. The
private bus exercised live capability changes, yes/challenge/no/na handling,
notification-owner appearance/disappearance, error replies, missing methods,
and a reply delayed past the configured timeout. Brightness parser tests cover
rounding, zero brightness, invalid classes and malformed/nonfinite values.
### Push status delivery measurement (b0583eb)
Compared release binaries for 4bf0757 and b0583eb with the same nested compositor,
empty isolated config and a visible preview. Binary SHA-256 hashes differ and
were checked after forcing a shell rebuild to avoid shared-target artifact reuse.
No status-command timing wrappers were active. Discarded an initial baseline
that overlapped compilation. Retained after/before/after runs, each with three
10-second samples following a three-second startup settle. Artifacts and harness:
`/tmp/ferese-status-push-measure/` (`summary.json`, `ui.py`, `latency.rs`).
Shell CPU percentages of one core:
- Before: 1.5, 1.4, 1.6 (median 1.5).
- After run 1: 0.8, 1.1, 1.1.
- After run 2: 1.0, 1.1, 7.5 (combined after median 1.1).
The 7.5% sample coincided with a large damaged-pixel burst (44.3 million in a
five-second interval), higher RSS and two additional descriptors. Its cause was
not isolated; it is retained, not silently excluded. Combined after mean is
2.1%, versus 1.5% before, so these short runs do not establish a robust average
CPU saving. Typical steady-state samples suggest a 0.4 percentage-point reduction.
Status child CPU remained roughly 6–8% of one core, dominating shell self CPU.
Steady-state damaged frames were about 12/s before versus 6.6–7.6/s in the first
after run. The repeat had similar quiet intervals plus bursts at 15.6 and 22.7/s.
Total render attempts stayed around 60/s, confirming active rather than hidden
preview sampling. No claim is made about DRM-session CPU or total idle wakeups.
An isolated transport harness used the old bounded mpsc + 250ms polling path and
an extracted copy of the new Updates::stream implementation, with 40 synthetic
updates per path at 317ms intervals. Worker-to-consumer delay:
- Before: median 129.398ms, p95 239.957ms, maximum 245.881ms.
- After: median 0.130ms, p95 0.330ms, maximum 0.425ms.
This measures delivery only, excluding status queries, UI dispatch and painting.
The preview processes exited and the temporary source worktree was removed.
These results predate later changes. Run fresh comparisons for current claims,
and validate direct rendering and multiple monitors separately.