docs(mosaic): preserve commit-present input ordering probe evidence
This commit is contained in:
parent
6fc849d172
commit
c1f0e3621d
11 changed files with 688 additions and 1 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
### STEP20C experimental evidence
|
||||
|
||||
- Preserved and independently replayed a commit/present/input ordering probe. A passive private top-level listener and native headless traces found 20 applied input-region mutations but no natural mutation between an accepted output commit and its matching present; sampled hit and client delivery followed effective input. The temporal verdict is INCONCLUSIVE and the generation-contract decision remains UNDECIDED. A complete outside-Git raw evidence manifest now covers all 94 run/result files. No production or package files changed.
|
||||
- Preserved a reproducible mutation-coverage fixture and source audit. A fresh-clone replay rebuilt private SceneFX/Mosaic and verified pinned wlroots sources in its own lab. Bounded trace-only runs separated camera/clip, popup lifecycle, same-size client content and input-region hits; the latter changed hit testing at an unchanged partial epoch. A real private output-destroy path released one held reference, while backend rejected-commit cleanup remains untested. The architectural generation decision is UNDECIDED; STEP20C stays NEXT.
|
||||
- Preserved a private applied-scene/input probe. A separate corrective patch scopes retained output buffers to their owner, samples three buffers through present, and logs partial applied-scene epochs at build and real pointer hits with client delivery kept separate. The tested matrix is PARTIALLY_FEASIBLE; STEP20C remains NEXT and multi-output Mosaic unsupported.
|
||||
- Preserved a private output-buffer identity fixture and protocol. In fixed headless geometry, 32 GBM buffers were read through SceneFX FX and linked to native commits and presents; three second reads matched. Four references were released early by a cross-output scope defect in the private instrumentation, not by demonstrated same-output buffer replacement. Readback changed timing, so transaction/input coherence and production readiness remain unproven. STEP20C stays NEXT and multi-output Mosaic unsupported.
|
||||
|
|
|
|||
|
|
@ -171,6 +171,19 @@ candidate research boundaries; whether existing event-loop ordering alone
|
|||
is enough across build, present and input remains **UNDECIDED**. No new
|
||||
production generation or transaction contract is accepted by this probe.
|
||||
|
||||
The independently replayed [commit/present/input ordering probe](design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md)
|
||||
confirms in source that wlroots headless queues `present` as an idle callback
|
||||
after an accepted output commit, while an applied Wayland input-region
|
||||
commit changes the region consulted by later scene hits. In five bounded
|
||||
cycles per mode, all 20 applied target input mutations in private standard
|
||||
and Mosaic occurred after the preceding matching `present`; none landed
|
||||
inside a native commit-to-present interval. Input-only hit and client
|
||||
delivery matched the effective region in sampled states. This leaves the
|
||||
cross-time Mosaic clipping invariant **UNDECIDED** and adds no production
|
||||
generation contract. A later hit may legitimately use newer input than a
|
||||
previously committed output buffer; identity of these states must remain
|
||||
distinct in further probes.
|
||||
|
||||
## IPC
|
||||
|
||||
On a Mosaic workspace, `get_tree` reports `layout: "mosaic"` and adds a `mosaic` object with `active_row` and `rows`. Each row entry contains ordered child node IDs, `scroll_x`, and focused node ID or null. Existing Sway fields retain their shapes; normal workspace/window events and `get_workspaces` paths remain in use. This is an extension to the Sway layout vocabulary: strict IPC consumers with a closed layout enum must recognize `mosaic`. Additional rows in the model are not a promise of usable multi-row IPC behavior.
|
||||
|
|
|
|||
|
|
@ -129,6 +129,18 @@ boundary remain unqualified. The architecture decision is **UNDECIDED**;
|
|||
physical presentation and production multi-output Mosaic remain
|
||||
**NOT YET SUPPORTED**.
|
||||
|
||||
The independently replayed [commit/present/input ordering probe](design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md)
|
||||
is **INCONCLUSIVE** for the discriminating temporal case. The headless
|
||||
backend defers `present` to an idle callback, but none of 20 bounded
|
||||
input-region mutations in private standard/Mosaic fell between a native
|
||||
commit and its matching present. Sampled hits and client receipt followed
|
||||
the applied input state, with unchanged sampled pixels; this is not a
|
||||
demonstrated Mosaic clip defect. Client input-only commits need not cause
|
||||
output redraw. Popup clip changes in that window, physical presentation,
|
||||
rejected backend commit cleanup and broader output/effect paths remain
|
||||
**NOT TESTED**. Existing ordering sufficiency and any added-generation
|
||||
contract remain **UNDECIDED**.
|
||||
|
||||
The [isolated feasibility probe](design/STEP20C_FEASIBILITY_PROBE.md) is **PARTIALLY FEASIBLE**, not a release qualification. On two horizontal headless outputs, real Mosaic content leaked visually into an empty adjacent output; that earlier disposable candidate also delivered pointer enter from a partly visible client outside its viewport. Negative-origin, scaled and rotated arrangements showed visual leakage in that candidate. Popups, effects, saved buffers and complete damage correctness remain unqualified across the full matrix. The later probes above narrow individual cases without qualifying multi-output Mosaic, which remains **NOT YET SUPPORTED**.
|
||||
|
||||
The later [SceneFX subtree clipping spike](design/STEP20C_SCENEFX_CLIPPING_SPIKE.md) is also **PARTIALLY FEASIBLE**. Its private inherited tree clip stopped tested tiled pixels and pointer hits from crossing the output boundary, including a fully hidden cell, but a real GTK XDG popup rendered and received input on the neighboring output because its scene tree lives in the global popup layer. Dedicated subsurface, saved-buffer, nested-popup and complete effect/damage validation remain open. The spike changed no production library or compositor source; multi-output Mosaic remains **NOT YET SUPPORTED**.
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ The [root README](../README.md) is the public summary. For a specific topic, use
|
|||
[Applied-state source fixture](../tooling/probes/step20c-applied-state-input/README.md) rebuilds the disposable experiment.
|
||||
20. [STEP20C mutation coverage probe](design/STEP20C_MUTATION_COVERAGE_PROBE.md): source ownership inventory and bounded trace-only camera, popup, client-content and input-region scenarios; output-destroy cleanup sampled, rejected commit still untested.
|
||||
[Mutation source fixture](../tooling/probes/step20c-mutation-coverage/README.md) replays the disposable matrix without production integration.
|
||||
21. [STEP20A](STEP20A.md) and [STEP20B](STEP20B.md): historical evidence.
|
||||
21. [STEP20C commit/present/input ordering probe](design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md): independently replayed headless ordering evidence; 20 applied input-region mutations, no natural commit-to-present interleaving, temporal question INCONCLUSIVE.
|
||||
[Ordering source fixture](../tooling/probes/step20c-commit-present-input/README.md) rebuilds the passive private trace.
|
||||
22. [STEP20A](STEP20A.md) and [STEP20B](STEP20B.md): historical evidence.
|
||||
|
||||
## Project
|
||||
|
||||
|
|
@ -79,6 +81,7 @@ The [root README](../README.md) is the public summary. For a specific topic, use
|
|||
| [STEP20C output buffer identity probe](design/STEP20C_OUTPUT_BUFFER_IDENTITY_PROBE.md) | Experimental evidence | PARTIALLY_FEASIBLE; sampled true buffers were read and paired with headless presents; four early releases came from a private cross-output instrumentation defect, while readback perturbs timing and atomic scene/input coherence remains unproven |
|
||||
| [STEP20C applied state/input probe](design/STEP20C_APPLIED_STATE_INPUT_PROBE.md) | Experimental evidence | PARTIALLY_FEASIBLE; per-output holds survive cross-output repaint in the sampled matrix, and applied scene/hit/client streams are observed with a partial experimental epoch |
|
||||
| [STEP20C mutation coverage probe](design/STEP20C_MUTATION_COVERAGE_PROBE.md) | Experimental evidence | PARTIALLY_FEASIBLE overall; an input-region change alters the hit at unchanged partial epoch, client recolor changes pixels without geometry, and a held output reference is released on sampled output destruction; cross-time generation contract remains UNDECIDED |
|
||||
| [STEP20C commit/present/input ordering probe](design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md) | Experimental, independently replayed evidence | INCONCLUSIVE for the discriminating temporal window; headless input-region commits and hits agree in observed states, but none of 20 applied mutations occurred between a native output commit and its present |
|
||||
|
||||
A proposal does not supersede accepted canonical contracts or assert implementation status. Its open feasibility items must be resolved before implementation.
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,17 @@ a controlled hit-geometry mutation between one known output-state build
|
|||
and its deferred headless present, with hits and client delivery sampled
|
||||
on both sides. This remains scientific evidence, not production STEP20C.
|
||||
|
||||
The independently replayed [commit/present/input ordering probe](design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md)
|
||||
tested that gate without changing backend scheduling. Twenty real input
|
||||
region mutations applied in two private modes, but none occurred between
|
||||
an accepted output commit and its matching headless present; P2 is
|
||||
**INCONCLUSIVE**. The sampled hit and client delivery matched effective
|
||||
input state, and no distinct Mosaic clipping defect was reproduced. Before
|
||||
deciding whether an extra generation contract is necessary, the smallest
|
||||
next gate is passive event-loop phase tracing with a client request already
|
||||
pending during ordinary repaint, then a same-interval hit if naturally
|
||||
observable. STEP20C remains **NEXT** and multi-output **NOT YET SUPPORTED**.
|
||||
|
||||
## STEP20D — multi-row Spatial Canvas · PLANNED
|
||||
|
||||
**Goal:** connect rows to runtime tree, transactions, scene, input, and IPC. **Planned deliverables:** active-row navigation, row-local scroll/focus, deterministic insertion/reflow, in-row reorder, and between-row movement. **Exit criteria:** real tiled containers preserve identity and order across row changes; focus and camera restore correctly; headless multi-row tests pass. **Docs first:** settle exact interactions and row invariants in UX and Architecture. Unset key combinations remain TBD.
|
||||
|
|
|
|||
291
docs/design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md
Normal file
291
docs/design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# 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
|
||||
|
||||
1. Headless `present` is queued as a Wayland idle callback, not emitted in
|
||||
the backend commit call. The accepted `events.commit` signal is synchronous.
|
||||
2. Output destruction cancels the queued idle. A failed allocation in
|
||||
`output_defer_present` could 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.
|
||||
3. For the top-level fixture, an applied `wl_surface.commit` updates
|
||||
`surface->input_region` before the SwayFX top-level commit listener logs.
|
||||
Subsequent `wlr_scene_node_at` uses that effective region.
|
||||
4. 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.
|
||||
5. 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.
|
||||
6. 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.
|
||||
7. This fixture sought true client Wayland mutations without changing the
|
||||
backend. None landed in the commit-to-present interval.
|
||||
8. 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](../../tooling/probes/step20c-commit-present-input/README.md)
|
||||
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.
|
||||
62
tooling/probes/step20c-commit-present-input/README.md
Normal file
62
tooling/probes/step20c-commit-present-input/README.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# STEP20C commit/present/input ordering — EXPERIMENTAL
|
||||
|
||||
The [protocol](../../../docs/design/STEP20C_COMMIT_PRESENT_INPUT_ORDERING_PROBE.md)
|
||||
was written before this fixture. Everything here is a source fixture for a
|
||||
disposable private laboratory; no code enters the production compositor,
|
||||
SceneFX ABI, package, cumulative Mosaic patch or daily session.
|
||||
|
||||
## Replay
|
||||
|
||||
```sh
|
||||
prep=$(sh tooling/probes/step20c-commit-present-input/prepare.sh)
|
||||
LAB=$(printf '%s\n' "$prep" | sed -n 's/^LAB=//p')
|
||||
for mode in control private_standard private_mosaic; do
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-commit-present-input/run.py \
|
||||
"$LAB" "$mode" --cycles 5
|
||||
done
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-commit-present-input/analyze.py "$LAB"
|
||||
```
|
||||
|
||||
The prepare chain rebuilds the pinned private SceneFX/Mosaic fixtures and
|
||||
fetches wlroots 0.20.2 at commit
|
||||
`d783533489e1f75d6886c2ab5c5960090ef268f8` inside the new lab.
|
||||
Its two audited scene files are hash-checked by the Mutation Coverage
|
||||
preparator. `surface-ordering.patch` adds a passive top-level surface commit
|
||||
trace after wlroots applies the client input region. Existing private traces
|
||||
provide build, commit, present, hit and seat notification. The client uses
|
||||
ordinary `wl_surface.set_input_region` and `wl_surface.commit` requests.
|
||||
|
||||
The runner reuses the frozen headless runtime, explicit IPC socket, process
|
||||
group cleanup and geometry checks. It defaults to five bounded cycles and
|
||||
`ASI_MODE=0` (trace-only). Standard controls are unmodified SwayFX `splith`
|
||||
and private SwayFX `splith`. Native output events are uninstrumented in the
|
||||
unmodified control. Camera requests in the private Mosaic case supply
|
||||
ordinary scene damage; no backend scheduling is changed. A missing natural
|
||||
commit/mutation/present interleaving must be reported **INCONCLUSIVE**.
|
||||
|
||||
The runner is sequential: `command()` waits for IPC response,
|
||||
`client_command()` waits for a client log confirmation, and `inject()` waits
|
||||
for pointer confirmation. A Mosaic camera reply precedes submission of the
|
||||
next input-region request. Those process round trips can exceed the native
|
||||
commit-to-present interval. The executed callbacks and delivery remain valid;
|
||||
the timing question requires a separately recorded nonblocking experiment.
|
||||
|
||||
For a complete evidence manifest outside Git, hash every regular file under
|
||||
the three `runs/` directories plus the ten structured results, excluding
|
||||
the two older source/artifact manifests and the new manifest itself. The
|
||||
historical frozen lab's `raw-evidence-sha256.txt` contains 94 entries and
|
||||
hashes to `9b74584936d44be5accb1f3882482fbd1eb85bc5c696b4551d45076b76ff4f8d`.
|
||||
The independent replay manifest has the same scope and hashes to
|
||||
`7d4e5c3860cc5ea3b63668441efa58bf31ef3197c6d22890cbaccee70543d41a`.
|
||||
|
||||
Set `CPI_SURFACE_TRACE=0` before `run.py` for a paired workload with only the
|
||||
additional `ORDER_SURFACE_COMMIT` log site disabled. Keep these paired runs
|
||||
in a separate lab from the three analysis runs; `analyze.py` deliberately
|
||||
rejects ambiguous duplicate mode runs. The old private build, hit and
|
||||
commit/present traces still execute in this comparison.
|
||||
|
||||
Raw logs, screenshots, generated runner, binaries, build trees, sockets and
|
||||
structured JSON analysis remain in `LAB`, outside Git. Stop or terminate the
|
||||
runner normally to remove its private runtime. A killed runner may leave its
|
||||
lab and sockets for manual, targeted cleanup; never touch the live Sway
|
||||
socket. The real rejected-commit backend path remains **NOT TESTED**.
|
||||
168
tooling/probes/step20c-commit-present-input/analyze.py
Normal file
168
tooling/probes/step20c-commit-present-input/analyze.py
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
"""Order executed server callbacks; keep client receipt on its own clock."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('lab', type=Path)
|
||||
args = ap.parse_args()
|
||||
lab = args.lab.resolve()
|
||||
out = lab / 'commit-present-input-results'
|
||||
out.mkdir(exist_ok=True)
|
||||
markers = {
|
||||
'ORDER_SURFACE_COMMIT': 'surface_commit',
|
||||
'BI_STAGE': 'output_build',
|
||||
'PI_OUTPUT_COMMIT': 'output_commit',
|
||||
'PI_OUTPUT_PRESENT': 'output_present',
|
||||
'ASI_HIT': 'scene_hit',
|
||||
'ASI_DELIVERY': 'pointer_delivery',
|
||||
}
|
||||
|
||||
|
||||
def save(name, value):
|
||||
(out / name).write_text(json.dumps(value, indent=2) + '\n')
|
||||
|
||||
|
||||
events = []
|
||||
matrix = {}
|
||||
client_delivery = {}
|
||||
pixels = {}
|
||||
for mode in ('control', 'private_standard', 'private_mosaic'):
|
||||
matches = sorted((lab / 'runs').glob(f'{mode}-ordering-asi0-*'))
|
||||
if len(matches) != 1:
|
||||
raise RuntimeError(f'expected one {mode} run, found {len(matches)}')
|
||||
run = matches[0]
|
||||
status = json.loads((run / 'exit-status.json').read_text())
|
||||
if status['failure'] is not None:
|
||||
raise RuntimeError(f'{mode} runner failed: {status["failure"]}')
|
||||
for line_number, line in enumerate((run / 'compositor.log').read_text(errors='replace').splitlines()):
|
||||
for marker, kind in markers.items():
|
||||
if marker + ' ' not in line:
|
||||
continue
|
||||
fields = dict(re.findall(r'(\w+)=([^ ]+)', line.split(marker + ' ', 1)[1]))
|
||||
if 'ns' not in fields:
|
||||
continue
|
||||
events.append({'mode': mode, 'kind': kind, 'line': line_number,
|
||||
**fields, 'ns': int(fields['ns'])})
|
||||
break
|
||||
client = (run / 'client.log').read_text(errors='replace')
|
||||
client_delivery[mode] = {
|
||||
'run': str(run),
|
||||
'top_button_presses': len(re.findall(
|
||||
r'POINTER_BUTTON target=TOPLEVEL[^\n]*button=273 state=1', client)),
|
||||
'region_empty_commands': client.count('COMMAND region_empty_top DONE'),
|
||||
'region_full_commands': client.count('COMMAND region_full_top DONE'),
|
||||
'popup_grabs': client.count('GRAB_REQUEST'),
|
||||
'client_lines': [x for x in client.splitlines() if
|
||||
'COMMAND region_' in x or
|
||||
'POINTER_BUTTON target=TOPLEVEL' in x],
|
||||
}
|
||||
pixels[mode] = {}
|
||||
for tag in ('ordering-P0-reference', 'ordering-P1-empty', 'ordering-P4-restored'):
|
||||
pixels[mode][tag] = Image.open(run / f'left-{tag}.png').getpixel((342, 175))
|
||||
matrix[mode] = {'run': str(run), 'runner_failure': None,
|
||||
'duration_seconds': status['duration_seconds'],
|
||||
'captures': status['captures'],
|
||||
'pixels': pixels[mode], **{k: v for k, v in
|
||||
client_delivery[mode].items() if k != 'client_lines'}}
|
||||
|
||||
# Same-process timestamps can be ordered. Equal timestamps retain log order.
|
||||
events.sort(key=lambda e: (e['mode'], e['ns'], e['line']))
|
||||
target_mutations = [e for e in events if e['kind'] == 'surface_commit'
|
||||
and e.get('input_changed') == '1'
|
||||
and e.get('width') == '420' and e.get('height') == '320']
|
||||
with (out / 'event-order.jsonl').open('w') as dst:
|
||||
for event in events:
|
||||
dst.write(json.dumps(event) + '\n')
|
||||
for name, kind in (
|
||||
('surface-commits.json', 'surface_commit'),
|
||||
('output-commits.json', 'output_commit'),
|
||||
('presentations.json', 'output_present'),
|
||||
):
|
||||
save(name, [e for e in events if e['kind'] == kind])
|
||||
save('input-regions.json', target_mutations)
|
||||
save('client-delivery.json', client_delivery)
|
||||
|
||||
hits = []
|
||||
for hit in (e for e in events if e['kind'] == 'scene_hit'):
|
||||
prior_inputs = [e for e in target_mutations if e['mode'] == hit['mode']
|
||||
and e['ns'] <= hit['ns']]
|
||||
prior_commits = [e for e in events if e['mode'] == hit['mode']
|
||||
and e['kind'] == 'output_commit'
|
||||
and e.get('output') == hit.get('output') and e['ns'] <= hit['ns']]
|
||||
prior_presents = [e for e in events if e['mode'] == hit['mode']
|
||||
and e['kind'] == 'output_present'
|
||||
and e.get('output') == hit.get('output') and e['ns'] <= hit['ns']]
|
||||
next_presents = [e for e in events if e['mode'] == hit['mode']
|
||||
and e['kind'] == 'output_present'
|
||||
and e.get('output') == hit.get('output') and e['ns'] > hit['ns']]
|
||||
hits.append({**hit,
|
||||
'last_target_input': prior_inputs[-1] if prior_inputs else None,
|
||||
'last_output_commit_seq': prior_commits[-1].get('commit_seq')
|
||||
if prior_commits else None,
|
||||
'last_present_seq': prior_presents[-1].get('commit_seq')
|
||||
if prior_presents else None,
|
||||
'next_present_seq': next_presents[0].get('commit_seq')
|
||||
if next_presents else None,
|
||||
'input_identity_inferred': hit.get('surface') == '(nil)'})
|
||||
save('scene-hits.json', hits)
|
||||
|
||||
timelines = []
|
||||
for mode in ('control', 'private_standard', 'private_mosaic'):
|
||||
group = [e for e in events if e['mode'] == mode]
|
||||
commits = [e for e in group if e['kind'] == 'output_commit']
|
||||
presents = {(e.get('output'), e.get('commit_seq')): e for e in group
|
||||
if e['kind'] == 'output_present'}
|
||||
for commit in commits:
|
||||
present = presents.get((commit.get('output'), commit.get('commit_seq')))
|
||||
between = [e for e in group if present and
|
||||
commit['ns'] < e['ns'] < present['ns'] and
|
||||
e['kind'] in ('surface_commit', 'scene_hit', 'pointer_delivery')]
|
||||
timelines.append({'mode': mode, 'output': commit.get('output'),
|
||||
'commit_seq': commit.get('commit_seq'),
|
||||
'commit_ns': commit['ns'],
|
||||
'present_ns': present['ns'] if present else None,
|
||||
'presented': present.get('presented') if present else None,
|
||||
'gap_ns': present['ns'] - commit['ns'] if present else None,
|
||||
'between': between,
|
||||
'input_mutation_between': any(e in target_mutations
|
||||
for e in between),
|
||||
'hit_between': any(e['kind'] == 'scene_hit' for e in between)})
|
||||
save('timelines.json', timelines)
|
||||
for mode in matrix:
|
||||
group = [e for e in events if e['mode'] == mode]
|
||||
intervals = [x for x in timelines if x['mode'] == mode]
|
||||
matrix[mode].update({
|
||||
'surface_input_commits': sum(e in target_mutations for e in group),
|
||||
'native_commits': sum(e['kind'] == 'output_commit' for e in group),
|
||||
'native_presents': sum(e['kind'] == 'output_present' for e in group),
|
||||
'commit_present_interleavings': sum(x['input_mutation_between'] for x in intervals),
|
||||
'commit_mutation_hit_present': sum(x['input_mutation_between'] and
|
||||
any(e['kind'] == 'scene_hit' for e in x['between']) for x in intervals),
|
||||
'gap_ns_min': min((x['gap_ns'] for x in intervals if x['gap_ns'] is not None),
|
||||
default=None),
|
||||
'gap_ns_max': max((x['gap_ns'] for x in intervals if x['gap_ns'] is not None),
|
||||
default=None),
|
||||
})
|
||||
save('matrix.json', matrix)
|
||||
summary = {'modes': list(matrix), 'interleavings': sum(x['input_mutation_between']
|
||||
for x in timelines), 'commit_mutation_hit_present': sum(
|
||||
x['input_mutation_between'] and x['hit_between'] for x in timelines),
|
||||
'clock_contract': 'server timestamps only; client order separate',
|
||||
'p2': 'OBSERVED' if any(x['input_mutation_between'] for x in timelines)
|
||||
else 'INCONCLUSIVE', 'rejected_backend_commit': 'NOT TESTED'}
|
||||
save('summary.json', summary)
|
||||
with (out / 'source-sha256.txt').open('w') as dst:
|
||||
for p in (Path(__file__), Path(__file__).parent / 'run.py',
|
||||
Path(__file__).parent / 'prepare.sh',
|
||||
Path(__file__).parent / 'surface-ordering.patch'):
|
||||
dst.write(f'{hashlib.sha256(p.read_bytes()).hexdigest()} {p.resolve()}\n')
|
||||
with (out / 'artifact-sha256.txt').open('w') as dst:
|
||||
for p in sorted(out.glob('*.json')) + [out / 'event-order.jsonl',
|
||||
out / 'source-sha256.txt']:
|
||||
dst.write(f'{hashlib.sha256(p.read_bytes()).hexdigest()} {p}\n')
|
||||
print(json.dumps(summary))
|
||||
21
tooling/probes/step20c-commit-present-input/prepare.sh
Normal file
21
tooling/probes/step20c-commit-present-input/prepare.sh
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
# EXPERIMENTAL: reconstruct only private sources in a new temporary lab.
|
||||
set -eu
|
||||
repo=$(CDPATH= cd -- "$(dirname -- "$0")/../../.." && pwd)
|
||||
fixture="$repo/tooling/probes/step20c-commit-present-input"
|
||||
log=$(mktemp /tmp/cpi-prepare-XXXXXXXX)
|
||||
if ! sh "$repo/tooling/probes/step20c-mutation-coverage/prepare.sh" > "$log" 2>&1; then
|
||||
printf 'PRIVATE_BUILD_FAILED log=%s\n' "$log" >&2
|
||||
tail -40 "$log" >&2
|
||||
exit 1
|
||||
fi
|
||||
lab=$(sed -n 's/^LAB=//p' "$log")
|
||||
test -n "$lab" && test -d "$lab"
|
||||
trap 'rm -rf -- "$lab"' 0
|
||||
# CONTRACT: the additional listener only reads already-applied top-level
|
||||
# surface state. No installed wlroots, compositor or backend is changed.
|
||||
(cd "$lab/mosaic" && git apply --check "$fixture/surface-ordering.patch" &&
|
||||
git apply "$fixture/surface-ordering.patch")
|
||||
meson compile -C "$lab/mosaic-build" -j 8 >> "$log" 2>&1
|
||||
trap - 0
|
||||
printf 'LAB=%s\nBUILD_LOG=%s\n' "$lab" "$log"
|
||||
61
tooling/probes/step20c-commit-present-input/run.py
Normal file
61
tooling/probes/step20c-commit-present-input/run.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
"""Adapt the frozen headless runner for bounded input-only ordering attempts."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('lab', type=Path)
|
||||
ap.add_argument('mode', choices=('control', 'private_standard', 'private_mosaic'))
|
||||
ap.add_argument('--cycles', type=int, default=5)
|
||||
args = ap.parse_args()
|
||||
if not 1 <= args.cycles <= 5:
|
||||
ap.error('at most five region cycles')
|
||||
lab = args.lab.resolve()
|
||||
repo = Path(__file__).resolve().parents[3]
|
||||
original = (repo / 'tooling/probes/step20c-applied-state-input/run.py').read_text()
|
||||
old = "choices=('cycles', 'buffer_cycles', 'identity', 'outside', 'hidden_click', 'toggle')"
|
||||
assert original.count(old) == 1
|
||||
adapted = original.replace(old, old[:-1] + ", 'ordering')")
|
||||
anchor = " if args.kind == 'cycles':\n"
|
||||
assert adapted.count(anchor) == 1
|
||||
scenario = ''' if args.kind == 'ordering':
|
||||
# CONTRACT: all surface input changes originate in the normal client;
|
||||
# only private Mosaic gets a camera request to supply output damage.
|
||||
o = owner()
|
||||
camera = workspace()['mosaic']['rows'][0]['scroll_x'] if args.mode == 'private_mosaic' else 0
|
||||
x = int(o['rect']['x'] - camera + o['window_rect']['x'] + 100)
|
||||
y = int(o['rect']['y'] + o['window_rect']['y'] + 150)
|
||||
snapshot('ordering-P0-reference')
|
||||
inject('move', x, y)
|
||||
inject('right', x, y)
|
||||
wait(lambda: count('POINTER_BUTTON target=TOPLEVEL') >= 1)
|
||||
record('ordering_point', global_point=[x, y], owner_id=o['id'])
|
||||
for i in range(args.cycles):
|
||||
record('ordering_cycle_start', cycle=i + 1)
|
||||
if args.mode == 'private_mosaic':
|
||||
command('layout mosaic scroll 200')
|
||||
record('ordering_camera_request', cycle=i + 1, delta=200)
|
||||
client_command('region_empty_top')
|
||||
inject('move', x, y)
|
||||
inject('right', x, y)
|
||||
if i == 0:
|
||||
snapshot('ordering-P1-empty')
|
||||
if args.mode == 'private_mosaic':
|
||||
command('layout mosaic scroll -200')
|
||||
record('ordering_camera_request', cycle=i + 1, delta=-200)
|
||||
before = count('POINTER_BUTTON target=TOPLEVEL')
|
||||
client_command('region_full_top')
|
||||
inject('move', x, y)
|
||||
inject('right', x, y)
|
||||
wait(lambda: count('POINTER_BUTTON target=TOPLEVEL') > before)
|
||||
record('ordering_cycle_end', cycle=i + 1)
|
||||
snapshot('ordering-P4-restored')
|
||||
elif args.kind == 'cycles':
|
||||
'''
|
||||
adapted = adapted.replace(anchor, scenario)
|
||||
generated = lab / 'commit-present-input-run-generated.py'
|
||||
generated.write_text(adapted)
|
||||
raise SystemExit(subprocess.call([sys.executable, str(generated), str(lab),
|
||||
args.mode, 'ordering', '--cycles', str(args.cycles), '--asi-mode', '0',
|
||||
'--max-seconds', '120', '--max-captures', '12']))
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
--- a/sway/desktop/xdg_shell.c
|
||||
+++ b/sway/desktop/xdg_shell.c
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <float.h>
|
||||
+#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
+#include <time.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>
|
||||
@@ -293,6 +295,32 @@
|
||||
wl_container_of(listener, xdg_shell_view, commit);
|
||||
struct sway_view *view = &xdg_shell_view->view;
|
||||
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
|
||||
+ // CONTRACT: opt-out permits a same-workload trace-overhead comparison;
|
||||
+ // the switch changes only this log site, not the backend scheduler.
|
||||
+ if (getenv("CPI_SURFACE_TRACE") == NULL ||
|
||||
+ getenv("CPI_SURFACE_TRACE")[0] != '0') {
|
||||
+ struct wlr_surface *surface = xdg_surface->surface;
|
||||
+ struct timespec ordering_now;
|
||||
+ clock_gettime(CLOCK_MONOTONIC, &ordering_now);
|
||||
+ // WHY: this callback observes the applied Wayland input state, independently
|
||||
+ // of output submission and presentation. Surface IDs expire at destruction.
|
||||
+ // CONTRACT: record state only; never alter scheduling or current.input.
|
||||
+ // INVARIANT: an input-only commit may change hit without a new client buffer.
|
||||
+ const pixman_box32_t *input_bounds = pixman_region32_extents(&surface->input_region);
|
||||
+ sway_log(SWAY_INFO, "ORDER_SURFACE_COMMIT ns=%" PRIu64
|
||||
+ " surface=%p resource=%u seq=%" PRIu32 " committed=%" PRIu32
|
||||
+ " input_changed=%d buffer_changed=%d input_rects=%d"
|
||||
+ " input_bounds=%d,%d,%d,%d width=%d height=%d sample_accepts=%d",
|
||||
+ (uint64_t)ordering_now.tv_sec * UINT64_C(1000000000) + ordering_now.tv_nsec,
|
||||
+ (void *)surface, wl_resource_get_id(surface->resource), surface->current.seq,
|
||||
+ surface->current.committed,
|
||||
+ !!(surface->current.committed & WLR_SURFACE_STATE_INPUT_REGION),
|
||||
+ !!(surface->current.committed & WLR_SURFACE_STATE_BUFFER),
|
||||
+ pixman_region32_n_rects(&surface->input_region),
|
||||
+ input_bounds->x1, input_bounds->y1, input_bounds->x2, input_bounds->y2,
|
||||
+ surface->current.width, surface->current.height,
|
||||
+ wlr_surface_point_accepts_input(surface, 100, 150));
|
||||
+ }
|
||||
|
||||
if (xdg_surface->initial_commit) {
|
||||
if (view->xdg_decoration != NULL) {
|
||||
Loading…
Reference in a new issue