Performance diagnostics
Measure per-output frame, render, damage, and presentation cost without per-frame log spam.
Sweets has an opt-in per-output render profiler for comparing compositor workloads. It does not enable blur or any other visual effect.
Start Sweets with the profiler in its inherited environment:
SWEETS_PROFILE_RENDER=1 sweetsThe values 1 and true enable it. This is a startup diagnostic, not a Lua
option. Calling sweets.env() cannot enable it because that API changes the
environment inherited by programs Sweets launches, not Sweets itself.
Profiling changes the workload by collecting timestamps and, when the renderer supports them, GPU timer queries. Keep it disabled for normal use and compare runs with the same renderer, output mode, scale, clients, animations, and duration.
Reading a summary
Each active output emits at most one render-profile interval summary every
two seconds. Reports are driven by rendered frames; the profiler creates no
periodic timer and produces no interval log while the output is idle. A final
partial summary is written when an output is removed or Sweets exits.
Duration fields use this order:
mean/p50/p95/p99/max(n=observed/stored)The fixed sample store retains at most 512 values per metric. Means and extrema cover the full interval; percentiles cover the retained samples.
| Field | Meaning |
|---|---|
budget_ms | Nominal refresh interval, or unavailable when the backend does not report one |
animation_ms | CPU time advancing animation and layout preparation |
scene_ms | CPU time building the wlroots scene output state |
test_ms | CPU time testing a requested tearing page flip; unavailable when no tearing test ran |
commit_ms | CPU time submitting the output state |
combined_ms | Available wlroots pre-render plus renderer/GPU completion duration |
blur_combined_ms | The same combined duration split by partial and full blur-damage frames; each side is unavailable when no matching frame was presented |
damage_pct | Damaged output-buffer area as a percentage |
present_latency_ms | Frame-start to presentation time only when the backend supplies a hardware clock |
damage_pixels is shown over the total output pixels observed in the interval.
full_damage counts full-output fallback frames. present separates matched
frame commits, shown and not-shown updates, unrelated output commits, and the
presentation flags supplied by the backend.
unavailable is not zero. render_unknown, pending_expired,
damage_unknown, and timestamp_unknown count samples Sweets could not
measure authoritatively. Combined render timers are queried only after the
matching presentation event, never synchronously from the frame being
rendered.
tearing_rejected counts actual backend tests, not every later frame from the
same client. After a successful normal fallback, one rejection suppresses
repeat async tests until the fullscreen surface, its tearing eligibility, or
the output configuration changes. Later intervals therefore normally show no
test_ms samples and tearing_rejected=0 while that backoff remains active.
Direct-scanout transitions
The profiler also observes wlroots' public root-buffer sampling signal for
managed windows. It writes direct-scanout-audit records only when a buffer
enters or leaves direct scanout, plus one summary for a window that entered it;
there is no per-frame scanout log.
An event=enabled record includes the output, window mode, whether blur is
requested, and the active ordinary-window, close-snapshot, and layer blur-node
counts. blur_clear=1 means no live Sweets blur participant was present when
wlroots selected that buffer for direct scanout. A fullscreen transition while
blur is enabled increments fullscreen_restorations. Any direct sample with a
live blur participant increments blur_violations and should be reported as a
bug.
event=disabled records the return to normal composition or the end of the
buffer lifetime. After the window closes, its summary should have matching
enabled and disabled counts, blur_violations=0, and active=0.
Page-flip transitions
On the DRM/KMS backend, the profiler also correlates Sweets' committed page-flip
mode with wlroots' matching presentation event. page-flip-audit records are
transition-only; normal frames do not produce one log each.
event=tearing-enabled ... vsync=0 proves that a committed tearing request was
presented asynchronously. The first later vsynced normal commit emits
event=normal-restored ... vsync=1. If the driver rejects an async test, the
successfully presented normal retry emits event=normal-fallback ... vsync=1
instead. The existing tearing backoff bounds fallback records to eligibility
or output-state changes.
The output-lifetime summary reports attempted and rejected async tests,
tearing, fallback, normal, and vsynced-normal presentations, not-presented
updates, transitions, mode mismatches, and whether tearing remained active.
After leaving fullscreen and closing the client, expect mode_mismatch=0 and
active=0. A rejected fallback proves safe normal presentation, not that the
hardware supports tearing.
Multi-output blur isolation
Effects builds also audit blur storage ownership while profiling. Each output
emits one blur-output-isolation event=overlap-active record when it first
prepares a blur frame while another output also has live blur work. The record
reports the number of active outputs and whether the output-local cache and
renderer targets passed the isolation check.
The lifetime summary counts prepared blur frames, concurrent frames,
resource/cache checks, peer comparisons, and maximum concurrent outputs. An
exercised multi-output run should finish with nonzero concurrent_frames,
peer_checks, and cache_checks, plus invalid=0. The audit compares only
opaque identities; it never logs pointer values or creates additional renderer
resources.
Nested and headless backends do not expose authoritative DRM page-flip flags,
so the profiler startup record reports page_flip_audit=unavailable there and
does not emit page-flip audit results.
Blur recovery after output changes
Optimized blur owns one lazy renderer-qualified cache on each output. Output mode, scale, transform, power, hotplug, renderer, session, background content, or blur-strength changes rebuild only the affected output when it still has a consumer. Noise changes do not rebuild the source, and an output with no optimized effect retains no cache resources.
Preparation or sampling failure leaves that output unblurred and emits one warning for the unchanged failure generation. Sweets retries after relevant renderer, source-topology, output, clear-color, or strength changes. Animated content commits do not repeatedly retry an already-failed cache, and there is no repaint timer or retry loop.
Sweets skips preparation when bounded committed opaque regions in front of an optimized effect cover its complete physical mask. Rounded, translucent, unknown, or excessively fragmented coverage stays on the conservative render path. Hidden, minimized, clipped, empty, and off-output effects allocate no blur resources.
Native SWEETS_PROFILE_RENDER=1 summaries include interval deltas for
optimized_resource_sets, optimized_source_renders,
optimized_blur_renders, optimized_releases, optimized_failures, and
optimized_occlusion_culls. A settled static scene adds no source or blur
renders. A background commit can rerender and reblur without allocating a new
resource set. A noise-only edit changes none of those work counters. The
existing bounded summary is the only log site; there is no per-frame cache
message.
Animation lifecycle and snapshot ownership
The same opt-in setting enables one backend-neutral animation lifecycle profile summary at most every five seconds while frames are being processed.
It works on both nested and native backends and creates no timer or idle redraw.
The summary reports submitted, no-damage, and endpoint-cleanup frames;
transition starts and interruption retargets; active transition/output maxima;
renderer fallbacks; and offscreen animation-snapshot create, reuse, release,
failure, live-count, and pixel maxima. Separate close_snapshots_* fields cover
retained closing-window content. Counts are aggregate and never include window
IDs, titles, application IDs, or pixels.
max_failure_latches shows the largest renderer-local set of failed snapshot
qualifications. failure_overflow_latched=true means that set reached its hard
bound: unseen snapshot requests then take the ordinary non-animated fallback
until transition cleanup frees a latch slot. They are not retried once per
frame. A settled workload should show no new starts, snapshots, failures, or
submissions; cumulative creates and releases should converge after the final
cleanup frame.
Overhead and privacy
When disabled, the profiler allocates no per-output sample state or per-window scanout audit, creates no render timers, registers no profiling listeners, and writes no profiler log. When enabled, each output owns about 37 KiB of fixed aggregation state, a bounded ring of 16 pending render timers, and small page-flip and cursor-composition audit state. Each managed window owns one small scanout audit with two lifecycle-bound listeners. Work and log volume stay bounded.
Reports include connector names and aggregate timing/counter data. They do not include application IDs, window titles, commands, client pixels, or other client content. Scanout transition records include only the shell type and window mode.