18 KiB
STEP20C commit, present and input ordering probe
Status: EXPERIMENTAL / INCONCLUSIVE. STEP20C stays NEXT and multi-output Mosaic NOT YET SUPPORTED. All compositor and client changes for this probe belong to disposable private copies. No production, packaging, ABI or UX decision follows from this protocol.
Question and distinct states
The mutation coverage probe found T3 client pixels changing at fixed intended
geometry and T4 input hit changing while asi_applied_epoch=12 and sampled
pixels stayed fixed. T4 is a normal possible Wayland transition, not proof of
a clipping defect. The question here is whether a real client input-region
commit can execute between one accepted output commit and that commit's
headless present, and what the actual scene hit and client delivery then do.
Keep five identities separate: client content buffer; effective Wayland
input region; applied scene geometry and clip; native output
presentation (output, commit_seq); and pointer grab/delivery state.
A submitted output buffer is not changed retroactively when a later surface
commit changes input. A later pointer hit may legally consult newer input
than the pixels in that buffer. A mismatch alone is not a Mosaic defect.
No experimental epoch is treated as an atomic native generation.
Source audit and scheduling hypotheses
Audited pinned inputs are SwayFX/Mosaic based on SwayFX 0.6, private SceneFX
0.5 at 37ccd723bef49e6891156ffafce8f549f01446cc, and wlroots 0.20.2
at d783533489e1f75d6886c2ab5c5960090ef268f8. The frozen private
instrumentation is the Mutation Coverage chain. Wayland server API semantics
are checked against wayland-server-core.h and the executed callbacks.
sway/desktop/output.c:output_repaint_timer_handler checks
wlr_scene_output_needs_frame, logs private BI_STAGE before and after
wlr_scene_output_build_state, then calls wlr_output_commit_state.
wlroots types/output/output.c calls the backend commit, increments native
commit_seq, applies state, and emits events.commit synchronously on
acceptance. The private PI_OUTPUT_COMMIT listener runs in that callback.
The headless backend backend/headless/output.c:output_commit queues a
present with commit_seq + 1 through output_defer_present when enabled.
types/output/output.c puts this on a Wayland event-loop idle source. The
idle callback calls wlr_output_send_present; an output destroy removes
the idle source and cancels that pending present. Allocation failure could
also prevent queuing. Thus present is deferred in source but not guaranteed
for every commit. A deferred idle is not evidence that a client request
actually ran between commit and present.
types/wlr_compositor.c:surface_handle_set_input_region changes pending
state. The subsequent wl_surface.commit moves it to current state,
intersects it with current surface size into surface->input_region, and
emits the surface commit signal. A cached/synchronized surface can delay
application; the top-level used here is not synchronized beneath a parent.
wlr_surface_point_accepts_input consults effective input_region at hit
time. wlroots types/scene/surface.c installs that predicate on the scene
buffer. Private SceneFX scene_node_at_iterator applies scene clip and
calls it. SwayFX sway/input/cursor.c:node_at_coords performs the scene
lookup; seat button notification follows. XDG grabs can redirect effective
delivery, so the initial scenario has none and still checks the client log.
The scene surface commit callback calls surface_reconfigure. It schedules
a frame only if the surface has a pending frame callback and intersects an
enabled output; an input-only commit does not itself guarantee a new output
buffer. The Wayland idle callback is documented as running before blocking
in wl_event_loop_dispatch; the exact order of already-ready client fd
callbacks against that idle depends on the loop's executed dispatch order.
Passive same-process timestamps, not this source inference, will decide P2.
No recursive dispatch, backend delay, compositor sleep, forged present or
direct write to current.input is permitted.
Fixture, identities and lifetime
Use HEADLESS-1 (-800,0) and HEADLESS-2 (0,0), both 800×600, scale 1,
normal transform. Compare unmodified SwayFX splith, private Mosaic binary
splith, and private Mosaic binary mosaic. The T4 client keeps one mapped
420×320 colored top-level and changes its input region full → empty → full
through normal Wayland requests without a new client buffer. No popup or
grab is active in P0–P5. A stable visible point is used for all hits and
actual right-button deliveries. Each run owns a mode-0700 runtime, explicit
WAYLAND_DISPLAY and SWAYSOCK, process groups and private logs.
Passive server traces record monotonic nanoseconds for surface commit,
output build, accepted output commit, headless present, executed scene hit,
and seat notification. Output identity is valid only during its lifetime;
surface IDs are temporary. Use native output commit_seq and an observed
surface commit sequence when available. Client events retain their own
order; client and server clocks are not merged without calibration. The
runner must stop process groups and remove sockets. The earlier private
output lock has per-output ownership, one unlock per lock, and timer cleanup;
normal P0–P5 use trace-only mode. Rejected backend commit remains
NOT TESTED and will not be induced unsafely.
Bounded matrix and validity
- P0: verify two outputs, layout, mapped client, reference pixel, hit, real button receipt, and native commit/present availability where traced.
- P1: commit empty input before an identified later output build, if one occurs; observe applied region, hit, button and output timeline.
- P2: seek naturally
OUTPUT_COMMIT(N) → SURFACE_COMMIT(empty) → SCENE_HIT(P) → OUTPUT_PRESENT(N), retaining actual order if hit differs. Count only accepted native commits and a real Wayland client mutation. - P3: observe a mutation after a present as temporal control.
- P4: restore full input on the same surface, with stable sampled pixels, restored hit and actual button receipt.
- P5: repeat at most five full/empty cycles per mode. Preserve failed attempts; if no P2 interleaving occurs, mark P2 INCONCLUSIVE.
Only after the input-only matrix, consider a private Mosaic scroll
400 → 1280 → 400 with two valid XDG popups if source and observations show
it can distinguish a Mosaic clip invariant. An input region alone does not
exercise the Mosaic viewport clip. A clip changing after an output commit
cannot rewrite its already submitted buffer. No scaling, rotation, migration
or hotplug is part of this matrix.
Analysis, neutral measurement and verdict
Keep raw traces and derive event-order.jsonl, surface-commits.json,
output-commits.json, presentations.json, input-regions.json,
scene-hits.json, client-delivery.json, timelines.json, matrix.json
and summary.json in the lab. Each hit must identify the effective input
region, last accepted output commit, last observed present, next present,
actual client receipt, and whether any relation is inferred. Captures are
pixel samples, not native buffer reads. Measure log volume, commit/present
counts, commit-to-present gaps and scenario duration in standard and
private runs; headless gaps are not physical display latency.
FEASIBLE requires sufficient observed transitions with matching hit and client delivery inside the tested boundary. PARTIALLY_FEASIBLE retains useful observations with important gaps. INCONCLUSIVE applies when the natural commit-to-present window cannot be exercised or distinguished. BLOCKED needs a reproducible clipping, targeting or lifetime violation; normal input-only change without redraw is insufficient. The architecture answer remains UNDECIDED unless a concrete required invariant is either shown to hold within the tested boundary or violated by a real counterexample.
Executed source-audit answers
- Headless
presentis queued as a Wayland idle callback, not emitted in the backend commit call. The acceptedevents.commitsignal is synchronous. - Output destruction cancels the queued idle. A failed allocation in
output_defer_presentcould omit it. The retained private runs also lack the two initial sequence-2 presents per instrumented mode; the reason for those initial omissions is not assigned without further callback tracing. - For the top-level fixture, an applied
wl_surface.commitupdatessurface->input_regionbefore the SwayFX top-level commit listener logs. Subsequentwlr_scene_node_atuses that effective region. - An input-only commit does not necessarily schedule a frame. Scene surface code schedules one conditionally for pending client frame callbacks; camera, cursor and other activity can separately cause output commits.
- Only callbacks actually dispatched by the Wayland event loop can occur between a backend commit and its idle present. Client requests, pointer input or other timers are candidates, not guaranteed intervening events.
- The public idle API says the callback runs before blocking in dispatch; it does not by itself establish the order against every ready client fd. The executed traces are the authority for this bounded backend run.
- This fixture sought true client Wayland mutations without changing the backend. None landed in the commit-to-present interval.
- An empty interval proves only that these 20 attempts did not exercise the proposed temporal case. It cannot prove that the interval is impossible.
Replayed results
Runner timing limit
The historical run.py inserts the scenario into the older runner. Each
command() waits for a complete swaymsg IPC response. Each
client_command() writes a line to the client and waits until its COMMAND ... DONE log appears. Each inject() writes to the virtual pointer client
and waits for INJECT_DONE. In Mosaic, the camera command is therefore
acknowledged before the input mutation is submitted; the next pointer action
also waits for client confirmation. The full-to-empty and empty-to-full
transitions repeat this sequential order. These are process and IPC
round trips, whereas many private commit-to-present gaps were only tens of
microseconds. This fixture can thus miss an otherwise possible pending-client
request during that narrow interval. The timing limit does not invalidate
the native callbacks, actual applied mutations, hits, pixels or client
delivery recorded in the runs. It limits the conclusion to the order that
was executed, and motivates a separately documented nonblocking runner.
The final fixture was overlaid on a new clone of published main at
6fc849d172a1728a5a4b3b7fa31c7dc930736fe5; its private SceneFX,
Mosaic, wlroots source-audit input and client were rebuilt in
/tmp/swayfx-mosaic-nested-grab-D0AMNl62/. The three runs used the fixed
two-output geometry and five region cycles each. All reported
failure=null, eight captures, five empty and five full client commands,
six actual top-level right-button presses, zero popup grabs and no retained
probe buffer. The sampled (342,175) left-output pixel remained
(35,68,204) at P0, first empty P1 and restored P4 in every mode.
| Mode | Native commits/presents | Applied target input commits | Matching commit→mutation→present | Target-point hits after known input commit | Duration |
|---|---|---|---|---|---|
Unmodified SwayFX splith |
Uninstrumented | Uninstrumented | Unobservable | Client receipt only | 8.870 s |
Private splith |
62/60 | 10 | 0 | 25 empty: no target hit; 25 full: target hit | 8.906 s |
Private mosaic |
84/82 | 10 | 0 | 30 empty: no target hit; 29 full: target hit; 2 full hits at a changed camera position reached another node | 8.923 s |
Both instrumented modes had 10/10 target input mutations after the last matching observed present and before a later output build. This directly supports P1 and P3 in their observed ordering. P0 verified output geometry, native tracing, baseline pixels and button receipt. P4 restored the same surface's input and receipt. P5 stopped after five cycles. P2 had zero actual natural interleavings and is INCONCLUSIVE. The two unmatched commits per private mode were initial sequence 2 on the two outputs. The unmodified control's zero trace records mean no instrumentation, not zero native commits. The client and server clocks were kept separate; server nanoseconds establish all reported commit/present/mutation/hit ordering.
Private standard commit-to-present gaps ranged 20,779–24,620,493 ns (median 76,131 ns); private Mosaic ranged 21,580–24,875,267 ns (median 85,915 ns). These are headless event-loop gaps, not display latencies. A separate same-workload comparison in an earlier private lab disabled only the new surface-commit log site: private standard measured 62/60 commits/presents and 8.968 s with it, versus 63/61 and 8.893 s without it; private Mosaic measured 77/75 and 8.891 s with it, versus 83/81 and 8.920 s without it. Both variants delivered six presses. Native counts and gaps vary between runs; this pair does not isolate a causal logging cost or establish a scheduling-neutral measurement.
The input-only test did not modify the Mosaic viewport clip. A second
400 → 1280 → 400 popup transition could test clipping, but this campaign
did not produce the temporal window required to distinguish a newer clip
from an already submitted output buffer. Running that variant now would add
spatial coverage without answering P2, so it remains NOT TESTED in this
probe. No separate Mosaic clipping defect, crash or lifetime failure was
observed. Backend rejected commit remains NOT TESTED.
Structured evidence and verdict
The source fixture
produces event-order.jsonl, surface-commits.json,
output-commits.json, presentations.json, input-regions.json,
scene-hits.json, client-delivery.json, timelines.json, matrix.json
and summary.json outside Git. Scene-hit records carry the last known
target input mutation, last accepted native commit and surrounding present
sequences; a nil hit's target identity is explicitly an inference from the
fixed test point. Captures are sampled pixels, not exact submitted buffers.
The final patch SHA-256 is
0abdd61a45dc5cc63a59b5279fcd3d94a01b52ed394048259270edcc6ca582fc.
The final source and artifact manifests hash to
767755b02b505af2467ea9b40f67c250f9406ee720a29237d877f9c69dc2f3c2
and 7835941aaae98ca3e5a3484defdfe0e14f246ac4693bdb13ff437c1fa29ffbd1;
every manifest entry passed sha256sum -c. The matrix, summary and event
order hashes are 347f74ec86fb54691660dde6098effde2ee2a90d4055df3613f8e7810aebd9d1,
8202008e649c3b5782ebac2ae32e9e80875f5763ec76936ae6849c2c37598a39
and 399c9e52087e426d2c6448dfa2bbc761f23a348ace4275b0d6472cf409993100.
The historical artifact manifest contains 11 derived files and omits raw
run logs and captures. It remains unchanged. A second, outside-Git manifest
at commit-present-input-results/raw-evidence-sha256.txt in that lab
covers all 84 regular files under the three runs/ directories and the 10
structured result files, excluding the two historical manifests themselves.
This includes configs, IPC output, compositor/client/pointer logs, captures,
output states and exit codes. It has 94 entries, all verified, and SHA-256
9b74584936d44be5accb1f3882482fbd1eb85bc5c696b4551d45076b76ff4f8d.
It does not cover build trees, binaries or sockets.
Independent freeze replay
On 2026-10-12 the preserved fixture was reconstructed again in a new
disposable lab, /tmp/swayfx-mosaic-nested-grab-a7AsuMuV/, from the pinned
source preparation chain, not from the historical build. Pinned SceneFX
37ccd723bef49e6891156ffafce8f549f01446cc and wlroots
d783533489e1f75d6886c2ab5c5960090ef268f8 were checked out in the
new lab. The three five-cycle runs all had failure=null, eight captures,
six real top-level right-button receipts and the same sampled pixel at P0,
P1 and P4. The standard control remains uninstrumented. Private splith
again gave 62 commits, 60 presents and 10 applied target mutations; private
Mosaic gave 84, 82 and 10. Both had zero target mutation between matched
commit and present. The two sequence-2 commits without observed presents
remain UNEXPLAINED / NO PRESENT OBSERVED. Timing values and exact event
stream hashes differed from the historical run, as normal scheduling can
vary; the measured counts and classification did not. Meson mosaic-model
passed in the new private build.
The independent lab's outside-Git raw evidence manifest uses the same
94-file scope and passed every hash check. Its SHA-256 is
7d4e5c3860cc5ea3b63668441efa58bf31ef3197c6d22890cbaccee70543d41a.
The independent matrix and event stream SHA-256 values are
00a70483bd8ad1f30fd9e544f8253d516f4b571f4cb876dc2a960a474ee532dc
and 71cab46cac1e41d1a08ab0d1460599074a1a0924c27e64af6c98e1afa6180ef9.
Probe verdict: INCONCLUSIVE for the commit/present/input ordering question. The sampled steady-state transitions and delivery passed, but the discriminating temporal window was never exercised. Architecture: UNDECIDED. No observed invariant requires an added applied-generation contract, and these samples cannot prove existing ordering sufficient for all clipping mutations. The smallest follow-up is a passive event-loop phase trace with a client input request already pending during ordinary repaint, preserving the native backend and five-attempt bound, to determine whether fd dispatch can naturally precede the queued present. If that is observed, execute a hit and client receipt in the same interval; only then consider an owner-popup clip transition at the same output geometry.