Skip to content

Say why the tray waits while shaders compile, and for how long - #384

Merged
drehtuer merged 4 commits into
feature/designer-material-and-roundnessfrom
feature/shader-loading-screen
Oct 5, 2026
Merged

drehtuer merged 4 commits into
feature/designer-material-and-roundnessfrom
feature/shader-loading-screen

Conversation

@drehtuer

@drehtuer drehtuer commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Shows a "Preparing the dice" plate over the tray while a dice material compiles. The plate carries a progress bar and an estimate of the time left, so the wait after an install or update has a visible reason.

Based on #383 (feature/test-cleanup), stacked as .claude/CLAUDE.md asks.

Why

Filament's materials are compiled on the phone for the driver that is there (decision 46). They are kept in codeCacheDir, which Android empties on every update. So the first launch after an install or update spent about 3.2 s on a black tray. A translucent die, the glass table or a textured table then stalled about 2 s mid-throw the first time it was needed. Nothing said why.

How

  • FilamentEngine reports a compile only when the material isn't on disk. It calls started before the compile and finished in a finally, so the plate can't get stuck if a compile throws. A normal launch reads the cache and shows nothing.
  • ShaderWork / ShaderProgress (plain Kotlin, JVM-tested):
    • libfilamat gives no progress, so the bar fills with elapsed time against an estimate.
    • It is capped at 95 % until the compile actually ends.
    • The text counts down "About N seconds left", then says "Almost done" once the estimate has run out.
  • ShaderTimings sets where the estimate comes from:
    • Defaults are the Pixel 10a's figures: opaque 2.4 s and resin 2.0 s measured; glass and table 2.0 s assumed.
    • What this phone actually took is saved to noBackupFilesDir/shader-timings.txt, which survives updates. The next update's bar is then this phone's own figure.
    • A garbled line falls back to its own default.
  • Plumbing:
    • RollThread.shaders: StateFlow<ShaderWork> → Tray.shaders, which defaults to never compiling, so fakes and the power-saving tray are unchanged.
    • TrayDriver forwards it to RollScreen.
    • Power-saving mode makes no engine, so the plate never shows there.
  • UI (PreparingTheDice):
    • A Plate centred over the tray, under the controls, that ignores touches.
    • The title names what is being prepared: dice, translucent dice, glass table or table.
    • TalkBack gets one polite live announcement, and the bar has progress semantics in whole per cent.
    • All strings are resources.
  • Docs:
    • Decision 95.
    • docs/architecture.md: threading, with a mermaid sequence diagram, and the storage layout.
    • docs/physics-and-rendering.md: "Preparing the dice".
    • Prototype option 1za in design/, the design/README.md map, and README's rendering bullet.

Tests and coverage

  • ShaderWorkTest (14), ShaderTimingsTest (10) and PreparingTheDiceTest (11, Robolectric, including RollScreen integration).
  • render/filament: function 76.6 → 77.5 %, branch 73.3 → 74.6 %.
  • feature/roll: function 94.5 → 94.6 %, branch 70.7 → 71.2 %.
  • test, detekt, ktlintCheck and lint are green for render/filament, feature/roll and app. render/filament's device-test sources compile.

Device check — Pixel 10a, 2026-10-05

Debug build of this branch, fresh install:

  • Cold compile. After run-as … rm -rf code_cache/materials and a relaunch, "Preparing the dice" (opaque) shows, then "Preparing the table" (textured felt). Each has a moving bar and "About N seconds left". The plate goes once the felt is drawn.
  • Learnt timings. no_backup/shader-timings.txt holds opaque=2437, table=2243 after the first launch and 2451 / 2180 after the second. The figures are stable, so the defaults are about right.
  • Warm relaunch. No plate at all.
  • Fresh install. The first-launch welcome covers the whole screen during the compile, so the plate is not seen there. The compile finishes behind the welcome text.
  • render/filament device suite: 60 tests. 58 passed and 0 failed. 2 declined (RenderGalleryTest and RenderedHarnessTest are opt-in and skip themselves without -e gallery / -e harness.rolls).

For the owner to judge: the startup compile is shown as two plates in a row ("dice" then "table"), each with its own bar starting from empty. Is that fine, or should it be one bar across both? Also whether the wording reads right.

🤖 Generated with Claude Code

@drehtuer
drehtuer added this pull request to stack #385 October 5, 2026 06:36
Base automatically changed from feature/test-cleanup to feature/designer-material-and-roundness October 5, 2026 06:39
drehtuer and others added 4 commits October 5, 2026 08:39
The first launch after an install or update spends about 2.4 s on the
Pixel 10a compiling the opaque material with libfilamat, and a
translucent die or a textured or glossy table costs about 2 s more the
first time it is drawn. The roll thread is blocked for all of it, so
nothing on that thread can tell the player why the tray is black.

FilamentEngine now tells a ShaderListener when a material has to be
compiled - inside MaterialCache's miss path only, so a launch whose
packets were on disk reports nothing. ShaderProgress turns that into a
ShaderWork on a StateFlow (written on the roll thread, read anywhere),
which RollThread exposes and Tray hands on with a default of "never",
so power saving and every fake need no change.

libfilamat gives no progress, so the fraction is an estimate: time
since the start against what this phone took last time, capped at 95 %
until the compile actually ends. The figures start at the Pixel 10a's
(opaque 2.4 s, resin 2 s measured; glass and table assumed 2 s) and
are learnt into noBackupFilesDir, not codeCacheDir, because the update
that empties the material cache is exactly when they are wanted. A
missing or garbled file reads as the defaults.

render/filament gains kotlinx-coroutines-core as an api dependency for
the StateFlow; it is already in the build for dicesets/install and on
the app's classpath through Compose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner asked for a loading screen on startup when shaders compile,
saying why the wait happens and how long it may take. The roll screen
now collects Tray.shaders and, while a compile runs, puts a plate in
the middle of the tray: what is being prepared (the dice, the
translucent dice, the glass table, the table), one sentence of why,
the counting plate's progress rule filled by the estimate, and "About
N seconds left" or "Almost done" once the estimate has run out.

It is the same plate for the startup compile and the lazy ones a
translucent die or a textured table trigger mid-roll, sits under every
control, and takes no touch. For TalkBack the title and reason are one
polite live region, heard once; the rule carries progress semantics in
whole per cent and does not announce itself, so the compile is not
narrated frame by frame.

ProgressRule becomes internal and takes a tag and a modifier so the
counting plate and this one share one drawing of the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The prototype gains a rollState value, preparing, and a canvas option
1za, so the plate is specified by a screen as well as by words.
docs/physics-and-rendering.md describes what the plate says, how the
estimate behaves and where the figures are kept; docs/architecture.md
adds the threading note with a sequence diagram, the codeCacheDir and
noBackupFilesDir entries to the storage layout, and decision 95.
design/README.md carries the map entry and the new hand edit, and the
README's feature list says what a first launch after an update shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@drehtuer
drehtuer force-pushed the feature/shader-loading-screen branch from d55917e to 1492d8b Compare October 5, 2026 06:39
@drehtuer
drehtuer merged commit d6daef6 into feature/designer-material-and-roundness Oct 5, 2026
5 of 7 checks passed
@drehtuer
drehtuer deleted the feature/shader-loading-screen branch October 5, 2026 06:46
@sonarqubecloud

sonarqubecloud Bot commented Oct 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

drehtuer added a commit that referenced this pull request Oct 5, 2026
#384 was merged into `feature/designer-material-and-roundness` with two
checks red. This PR fixes both.

- **SonarCloud quality gate: reliability rating 2 against 1.** The issue
was `kotlin:S899` at `ShaderTimings.kt:113` and `:115`:
`ShaderTimingStore.save` ignored what `File.delete()` returned when it
gave up a partial write. It now uses a `discard` helper, the same one
`MaterialCache` has, which falls back to `deleteOnExit`. The existing
`ShaderTimingsTest` cases (a directory in the way, partial-file cleanup)
already exercise both paths.
- **Documentation: mermaid parse error** in the shader plate's sequence
diagram in `docs/architecture.md`. Mermaid ends a statement at a `;`
even inside a `Note`, so `plate up; bar follows …` became `plate up, bar
follows …`.

Checks, run locally in the devcontainer:
- `:render:filament` test, detekt, ktlint and lint are green.
- The Documentation job's mermaid step (same `mermaid-cli` image) parses
all 21 diagrams.
- markdownlint-cli2 reports 0 errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant