Commit Graph
17 Commits
Author SHA1 Message Date
irisandClaude Fable 5.1 167862ca1b iris: the composer scrolls on a finger -- a dp cap worth zero, a stale mask slot, a hit box moved twice
Wrapping the composer's field in .scrollable().masked() needed three
layout defects fixed first, each with a headless regression test that was
confirmed to fail without its fix:

- MaxSize/Sized reported a caller's declared dp length unresolved, and
  Span places a child from the abs/rel of what it reported, so dp(168)
  was worth zero: the bar got a slot of nothing the moment its content
  passed six lines and the Scroll inside measured its container at -63px
  (container=-63 content=415.8 amt=478.8 on the emulator). Len::fold_dp,
  used on the way out, plus a debug_assert in draw_inner that a reported
  Size carries no dp -- the rule is about every widget, not those two.
- Masked allocated a fresh mask slot per draw, and draw_inner's
  unchanged-region fast path does not revisit descendants, so they kept
  clipping against a box the bar had moved away from: four live mask
  entries, none of them current, and the field drew nothing.
  ActiveData::own_mask, allocated once and rewritten in place.
- mov updates active.region and accumulates the same delta on the move
  slot, and resolved_region added both, so a panned widget's own hit box
  sat at twice the pan -- the composer's field was untappable after a
  drag. ActiveData::move_applied.

Scroll itself measured the right number by a misleading route; it is
written against painter.px_size() now and still reports its content's
size, since reporting the container makes the answer a function of
itself.

Verified on this checkout's emulator: swipe 540 1200 -> 540 1460 moved
the field's Message box 31,1041..1048,1509 -> 31,1131..1048,1651 with its
height unchanged at 468px.

run-bench.sh polled logcat for a prefix copy_report also logs at startup,
so it printed a report that had never been run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 17:17:42 -04:00
irisandClaude Fable 5.1 fb6b459c2c iris: Scroll pans on a finger drag; a vertical drag in a focused field scrolls rather than selects
IRIS_TODO.md's "the composer has no touch-drag scroll". `Scroll::drag`
takes its pan from the same `sense::DragGesture` `List` is driven by --
arbitration, DRAG_SLOP, velocity and pointer capture all stay in sense.rs
and only what a committed pan *means* is decided per caller -- and
`WidgetLike::scrollable()` registers it beside the wheel handler it already
registered, so every scroll area pans on a finger with nothing added at the
call site. No fling: `Scroll` has no per-frame tick to animate one and the
areas it wraps are at most a screenful. `Scroll::amt()` exposes the pan
position.

`attr.rs`'s `on_press` treated an already-focused field as the plain
click_or_drag case, so every Pressing frame extended a selection. It now
applies the same DRAG_SLOP rule its unfocused branch already did: a press
past the slop vertically abandons its pending selection for the rest of the
gesture, so the scroll area around the field wins it. That is Android
EditText's own behaviour and it is what lets a swipe up over the composer
scroll instead of dragging a highlight through what you typed.

Also fixed, found doing it: `ActiveData::mask` stored the mask a widget
*set* rather than the one it was drawn *under*, and `redraw` feeds that
field back in as the inherited mask -- so a targeted redraw of any `Masked`
handed it its own mask and aborted on `set_mask`'s nested-mask assert. A
real abort on the emulator, `assertion failed: self.mask == MaskIdx::NONE`.

And the per-frame orphan guard from 76b1f99 is now a count comparison
(O(active widgets)); the O(primitives) walk only runs to build the failure
message, because running it per frame made a debug build on the emulator too
slow to finish a bench run at all.

Tests: four in scroll.rs (pan past the slop, a tap inside it, a horizontal
drag, the end clamp), `a_finger_drag_over_a_scroll_area_pans_it` in
sense_tests.rs driving the whole registration/dispatch/capture path (fails
with "got 0" without the new registration), and
`redrawing_a_masked_widget_does_not_nest_its_own_mask` in layout_tests.rs
(aborts on the pre-fix code).

The composer itself is deliberately still not `.scrollable()`: `Scroll`
measures against the window rather than its own offered box, so inside the
`MaxSize` capping it at six lines it pans the field out of the bar --
measured, reverted and written down in RUST.md and DECISIONS.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 16:45:56 -04:00
iris 3e72a4ef19 docs: the defect pass's findings -- RUST.md boxes, IRIS_TODO ticks, DECISIONS and IRIS entries 2026-09-06 13:47:28 -04:00
irisandClaude Fable 5.1 a27fbdb029 docs: close I5's three blocked verifications (24/24-swipe, backend isolation, cold-boot bench)
Ran three clean iris-scroll.sh passes on a cold -gpu host boot (all
24/24 swipes confirmed scrolling via clustered render() timestamps, not
inferred from frame count) and retook the host-GPU table's iris row as
a best-of-three. EMU_GPU=software + force-gles still cannot produce a
GLES number on this hardware -- after the earlier compute-limit crash
was fixed, device creation now aborts on max_storage_buffer_binding_size
instead (SwiftShader ES 3.0 has no SSBOs, and shader.wgsl reads four
var<storage> buffers unconditionally), so the SwiftShader-Vulkan-vs-GLES
question is closed as structurally unanswerable rather than answered.
A fresh cold-boot run-bench.sh reading for P0's bench build is in line
with the earlier warm-AVD readings, closing that box's own caveat too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:39:27 -04:00
iris 800da46188 Merge remote-tracking branch 'origin/rustify' into worktree-agent-a27094a7db775552a
# Conflicts:
#	docs/IRIS.md
2026-09-05 21:37:04 -04:00
irisandClaude Fable 5.1 00767eed4d docs: P0's iris half done -- bench feature, emulator smoke run, APK
RUST.md's P0 box gets the iris-half account: the fixture, the scroll/stream
mechanism, the report fields, build commands (all clean), packaging (no
cargo xtask apk yet, so a new Gradle release build type on top of cargo
ndk), and the emulator smoke run's report next to Compose's own. Used a
second, differently-named AVD rather than contend with the session already
on this checkout's own emulator.

DECISIONS.md's P0 entry gets a matching summary bullet. IRIS.md records
AndroidAppState::platform_ready. IRIS_TODO.md notes the one gap found:
no read-only selectable text primitive, so the bench report's TextEdit
picks up a keyboard on tap it has nothing to type into.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:35:51 -04:00
irisandClaude Fable 5.1 d01c105037 iris: stop requesting compute-shader limits nothing uses
adapter.request_device asked for Limits::default(), which requests
desktop-tier compute-shader limits unconditionally even though nothing in
iris/iris-core creates a ComputePipeline or writes a @compute stage. That
crashed device creation outright on a downlevel GL adapter reporting
OpenGL ES 3.0 (no compute at all) -- the Android emulator's
EMU_GPU=software/force-gles path, and any real GLES-3.0-only device.

New iris_core::device_limits(), shared by both platform backends, zeros
exactly the six max_compute_* fields rather than switching to a downlevel
Limits preset -- downlevel_webgl2_defaults() also zeros
max_storage_buffers_per_shader_stage, which shader.wgsl's vertex stage
needs. rigs/gpu-probe's own mirrored limits were updated to match.

Not verified against the actual SwiftShader-ES-3.0 crash on-device this
pass: the cold boot needed would have force-restarted this checkout's
emulator while another session had its own app running on it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:18:30 -04:00
irisandClaude Fable 5.1 68b48cfd14 docs: record P0's Compose half (bench build, fixture, smoke run)
RUST.md's P0 box gets the emulator smoke run's report and what's done vs.
left; DECISIONS.md gets a dated summary entry; AGENTS.md's "Checking your
work" and "The rigs" get one paragraph each on the bench build type and
app/bench-fixture/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:04:07 -04:00
irisandClaude Fable 5.1 bfe93c4188 RUST.md, DECISIONS.md: P0, the phone benchmark gate Iris asked for before P1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:31:20 -04:00
irisandClaude Fable 5.1 5b7dc0e4e2 RUST.md, DECISIONS.md, IRIS_TODO.md: the port plan, P1-P7, after iris-over-Masonry
Adds "The port, in order (decided 2026-09-05)" to RUST.md: seven ordered
steps building the app on iris now that the framework is decided, each
naming the Kotlin files it replaces, the client-core pieces it needs
(and which are not yet covered and must be ported first), the missing
iris widgets it needs (recorded in IRIS_TODO.md's new "Build (for the
port)" section), and a pass condition a later agent can run. Ordered by
risk to the daily-use path: session screen parity, then the shell merge
and a real phone install, then root tabs, the file explorer,
settings/enrolment, desktop parity, and the cutover itself.

Crate-shape decision recorded in DECISIONS.md: one UI crate, app-ui,
grown out of transcript-ui rather than started beside it, with
desktop-app/android-app as thin entry points over it and platform-only
code staying in the E3/E5 Java shell.

Updates RUST.md's "Where things stand" and "For the next agent" to point
at P1 rather than the now-closed framework decision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:30:02 -04:00
irisandClaude Fable 5.1 621f08d725 DECISIONS.md, RUST.md: Iris decided iris over Masonry, 2026-09-05
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:25:43 -04:00
irisandClaude Fable 5.1 e49d0e606f RUST.md, DECISIONS.md, IRIS.md: iris's host-GPU frame time, 2026-09-05
Takes the -gpu host pair the earlier software-mode comparison flagged as
missing. Under real GPU rendering (--features force-gles: the default
Vulkan backend has no adapter at all under plain host-GPU boot, confirmed
by the exact wgpu error), iris's median frame (15.0ms) is faster than
Compose's (20.0ms) on the same session content -- the opposite shape from
the software-mode table. The new redraw-to-submit/submit-to-present split
shows iris's own CPU work is a median 0.2ms per frame; almost the whole
frame is time handing off to the driver, consistent with (but not proof
of) the software-mode gap being mostly SwiftShader's CPU rasterisation
cost rather than iris-specific slowness.

A same-mode software force-gles run, meant to isolate the backend, hit a
third distinct crash instead (SwiftShader's GL path reports itself as
OpenGL ES 3.0, which has no compute shaders, and iris's device request
assumes them unconditionally) -- real scope to fix, not done here, so the
software-mode question stays open. A real intermittent touch-scroll
dropout was also reproduced (six consecutive swipes produced zero
redraws while taps kept working; an identical retry then succeeded) and
is not explained. The idle-redraw and virtualised-culling findings from
the software-mode pass were confirmed to hold under real GPU rendering
too.

DECISIONS.md's DEFERRED item carries the updated table; the iris-vs-
Masonry choice itself is still Iris's to make. IRIS.md records the
FrameReport::record_split/FrameStats::cpu_p50/gpu_wait_p50 API from the
prior commit (e2a1fad), which this pass's measurement used.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:07:20 -04:00
irisandClaude Fable 5.1 0e4629361b docs: I5's clean scroll comparison between Compose and iris, one session
Same sandbox session content, same emulator, EMU_GPU=software: Compose
(debug, in-app report) 1102 frames/99.0% late/p50 33.8ms/p99 79.5ms vs
iris (release -- debug SIGSEGVs on this emulator) FrameReport 299
frames/94.65% janky/p50 79.1ms/p99 117.8ms (repeat: 233/94.42%/p50
109.3ms). Ticks I5 [x]; states plainly what's not comparable (build
profile forced asymmetric, three different jank definitions, both are
software-rasterised emulator numbers). The two "zero frames" attempts
that preceded the clean runs traced to this session's own script bug
(a cd into /tmp changed which emulator ui-trace targeted), not a
reproduction of the previously-suspected touch-delivery dropout; a
sampler ran the whole session and saw load rise during the gesture
without correlating to any failure. DECISIONS.md's DEFERRED item gets
the same table so Iris can decide iris-vs-Masonry from it -- that
choice is left to her.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 15:21:45 -04:00
irisandClaude Fable 5.1 1e7b1cddb7 RUST.md, IRIS.md, IRIS_TODO.md, DECISIONS.md: record I5's frame report and holddrag results
FrameReport gave a real, measured on-device number (frames=34,
janky%=61.76, p50=26.5ms p90=48.0ms p99=98.1ms worst=98.1ms) and
long-press-then-drag-to-select is now confirmed on-device (logcat plus a
screenshot of the highlighted selection). Neither closes I5's box to [x]
yet: the frame number is real but not the clean single 24-swipe loop
comparable to Compose's, because gestures against this checkout's
EMU_GPU=software emulator intermittently delivered zero touch input this
session -- a new, separately named finding (candidate cause: the
emulator's own software rasterisation measured at ~78% of a CPU core
continuously), not yet root-caused. DECISIONS.md's DEFERRED item is
updated with these numbers rather than a decision made here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 15:00:29 -04:00
irisandClaude Fable 5.1 7ae53ad797 iris: FrameReport, a per-frame wall-time report of iris's own render path
dumpsys gfxinfo cannot see a SurfaceView's own GPU-drawn frames at all
(RUST.md's I5 box), so iris needs its own equivalent of Compose's
render-report button before item 3 of the recommendation can be decided
by a number. FrameReport (iris/core/src/render/frame_report.rs) records
each frame's wall time -- from render()'s redraw start to after
queue.submit + present() -- into a fixed 4096-entry ring, and reports
total frames, janky % (>16.7ms, gfxinfo's own budget), P50/P90/P99 and
the worst. Wired into AndroidUiState and android/view.rs's render(), and
exposed as two named controls ("Frame report", "Reset frame report") on
iris-android-app's transcript screen, logged under the crate's fixed tag
so a script can grep "iris frame report" the way transcript-bench.sh
greps "ai-app render report".

6 new unit tests for the ring/percentile math. cargo fmt/clippy/test
--workspace clean; cargo ndk (iris, transcript-ui, and
iris-android-app --features transcript-screen) all clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 14:23:51 -04:00
irisandClaude Fable 5.1 d17040b601 RUST.md, IRIS.md, IRIS_TODO.md, DECISIONS.md: record I5's Android integration and measurements
I5's transcript screen now runs on-device against a real ai-server on
iris-android-app's new transcript-screen feature (extends I2's shell
rather than a third one), with real scrolling, real touch-drag panning
and tap-by-name accessibility all confirmed by screenshot/log evidence.
I4's own emulator-side check (tap-by-name on the tabs demo) closed the
same session, so its box ticks [x] now.

Still [~], not [x]: the render-time number RUST.md's recommendation
wants for iris couldn't be produced this pass, for a precise and
recorded reason rather than a vague one -- dumpsys gfxinfo cannot see a
SurfaceView's own GPU-drawn frames at all (0 frames reported across a
gesture loop that visibly scrolled), and a SurfaceFlinger --latency
fallback gave no per-frame history either on this Android version. The
Compose side of the same loop did produce a real number under identical
conditions (8.96% janky, 99th percentile 150ms), so this is now a
one-sided number rather than a missing one on both sides.

Also found and recorded: the AVD's saved snapshot carries a GPU config
across restarts, so switching between the documented Vulkan boot
recipes needs a cold boot (clearing snapshots/) that the emu wrapper
does not force -- cost three different-looking crashes before the
pattern was the snapshot, not the code.

DECISIONS.md's DEFERRED item is updated with the numbers Iris needs to
weigh the iris-vs-Masonry call; the call itself stays hers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 14:09:04 -04:00
irisandClaude Fable 5.1 9a33cb5384 docs/: move the design and working documents out of the repo root (CLAUDE.md and AGENTS.md stay, harnesses read them there)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 13:03:42 -04:00