iris: a device change re-uploads its textures, and a mark is one texture per shape
The bench APK panicked on frame 1 on the emulator:
iris panic at iris/core/src/render/texture.rs:461:22:
texture slot 89 is not a live standalone image: None
widget::mark called Textures::add per widget, so a folded card per tool
call meant a standalone image, a bind group and a draw call each --
hundreds of copies of three pictures. Textures::reset, which the Android
surface-rebuild path calls for a genuinely new renderer, then threw the
slot numbering away with the pixels, leaving every one of those live
handles naming a slot nothing recognised. Its doc had said the only
standalone image in the workspace was tabs-ui's, "confirmed by grep" --
true when written, false the moment mark existed.
Textures::reupload replaces reset: queue every slot for upload again in
slot order, empty slots included, so the new device gets the same slot
numbering and a handle a widget has been holding still names its own
texture. The glyph atlas is no longer cleared on that path either, so an
app switch stops re-rasterising every glyph on screen.
Textures::shared(key, make) is one texture per description, keyed by a
SharedTextureKey the caller packs exactly rather than hashes. mark keys on
direction and colour: three mark textures for the screen, not one a card.
And the devlog can finally show a panic. After a crash, Dev Updater's
query starts the app process for the provider alone, so no activity ran,
so set_crash_dir never replayed the panic hook's file -- the Runtime tab
held one line, the provider announcing itself. DevLogProvider.nativeReady
takes the files directory and does the replay from onCreate; the hook also
saves the dying run's last 80 lines beside the panic, read through a new
non-blocking LogRing::try_tail_text so a panic holding the ring's lock
cannot deadlock the hook.
Verified on this checkout's emulator: opens clean, survives 33 full-screen
scrolls back through the fixture, image_bind_group_creates_prev=1; a real
panic replays into the next launch, and a hand-written last-panic.txt
replays in a process started by a provider query with no activity.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c8785b6091
commit
341b7a5922
9 files changed
+550
-57
No files matched your search
@@ -1370,3 +1370,55 @@ worth knowing because it changes what a build of the Android app *is*.
|
||||
both the desktop and the phone -- only the directory differs.
|
||||
`desktop-app --ca` is now the override for a link that carried no CA
|
||||
rather than a required flag.
|
||||
|
||||
## 2026-09-08: a new GPU device re-uploads its textures instead of forgetting them, and a mark is one texture per shape
|
||||
|
||||
Two defects with one cause: **`widget::mark` built a texture per widget**,
|
||||
so a transcript screen had one 48x48 standalone image, one bind group and
|
||||
one draw call *per folded card* rather than one per picture -- and the
|
||||
Android surface-rebuild path assumed no long-lived widget held a texture
|
||||
handle at all.
|
||||
|
||||
- **`Textures::reset` is gone; `Textures::reupload` replaces it.** A new
|
||||
GPU device holds none of the old one's textures, but this side still
|
||||
holds their pixels, so the answer is to queue every slot for upload
|
||||
again in slot order (empty slots included, as `PushFree`, so the
|
||||
indices after a hole still land where they were) rather than to throw
|
||||
the slot numbering away. Resetting left every live `TextureHandle`
|
||||
naming a slot nothing recognised: the first frame after the emulator's
|
||||
Vulkan-to-GLES fallback panicked with *"texture slot 89 is not a live
|
||||
standalone image: None"*, before anything had been touched.
|
||||
- **The glyph atlas is no longer cleared on that path either**, which
|
||||
falls out of the same change: its pages are slots here and their pixels
|
||||
are on this side, so re-uploading restores exactly the atlas that was
|
||||
there. An app switch no longer re-rasterises every glyph on screen.
|
||||
- **`Textures::shared(key, make)`** (new): the one texture for a
|
||||
description, built on the first ask and handed out again after, keyed
|
||||
by a `SharedTextureKey { owner, id }` the caller packs *exactly* rather
|
||||
than hashes. The map holds its own reference, so a shared slot is never
|
||||
freed and never recycled under a widget still drawing it. `mark()` is
|
||||
its first caller: three marks now exist for the whole transcript screen
|
||||
(open, closed, collapse) instead of one per card, and the rasterising is
|
||||
paid once.
|
||||
|
||||
## 2026-09-08: an app's own log survives the process that wrote it
|
||||
|
||||
`devlog`'s provider could only ever show the run that was still up. After
|
||||
a crash, Dev Updater's query starts the app process **for the provider
|
||||
alone** -- no activity runs, so `MainActivity.nativeSetFilesDir` never
|
||||
fired and the panic hook's file was never replayed. The Runtime tab
|
||||
therefore showed one line, `iris devlog: serving this app's log at ...`,
|
||||
which is exactly the run nobody needs.
|
||||
|
||||
- **`DevLogProvider.nativeReady` now takes the files directory too**, and
|
||||
`app_log::set_crash_dir` is called from whichever of the provider and
|
||||
the activity runs first (it deletes the file, so the second says
|
||||
nothing).
|
||||
- **The panic hook saves context, not just the panic**: the dying run's
|
||||
last 80 log lines go into the file with it, and are replayed into the
|
||||
new run's ring ahead of the panic line, so the Runtime tab reads
|
||||
chronologically -- what the app was doing, then what killed it, then
|
||||
this run. They are read with a new non-blocking
|
||||
`LogRing::try_tail_text`, because a panic raised while the ring's own
|
||||
lock was held would otherwise deadlock the hook and hang the process
|
||||
instead of aborting it.
|
||||
Reference in new issue
Block a user