Unpack the bundled models on a worker, not on the way to the first frame
Launching v0.10.0 on the tablet produces an ANR: "Waited 5000ms for MotionEvent", 5,827 ms on one input sequence, over a window that has never painted. The app recovers and sits at 0% afterwards, so it is a startup cost rather than a hang. The structural fact behind it is that `android_main` runs with the activity's input channel unserviced. Nothing drains it until Slint reaches `poll_events`, and Slint does not reach `poll_events` until `dr_ui::run` calls `window.run()` on its last line. Every millisecond before that is a millisecond the input dispatcher waits on, so five thousand of them is an ANR whatever the work happens to be. The largest single piece of that work was here. `install_bundled_models` copies 41 MB on the first launch after an install — 24.9 MB of scene model, 13.6 MB of embedder, 2.5 MB of detector — each read whole out of the APK into a `Vec` and written to `/data`, in a loop, on that thread. v0.10.0 is the release that added the scene model, which is 60% of that total, and it is the release the ANR appeared in. The 8,010 minor faults in the report are about what 41 MB of freshly touched pages costs. So it moves to a detached thread and the function returns as soon as the thread is running. Nothing on the launch path wanted the result: the only two things that read these files are the People screen and the scene tab, both of which are reached by hand, minutes later, from workers of their own. What that costs is a window in which a model looks absent. `library::face_models` and `library::scene_model` decide availability on `is_file()`, so during the copy both report their feature unavailable — which is the same answer they give a build shipping no weights at all, the ordinary case both were written around. Briefly pessimistic rather than wrong, and the temporary-name-then-rename that was already there is what keeps it from being worse than that: a lookup never sees a half-written file, only an absent one. Both call sites now say so. A completion line reports the bytes copied and the milliseconds taken, including when it is zero, so the second launch after an install can be told from the first in a log rather than by inference. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3808,8 +3808,14 @@ pub fn face_models_dir(account: &Account) -> PathBuf {
|
||||
/// `faces.model_id` (catalog.md §10.1) and not by who is signed in, so two
|
||||
/// accounts have no reason to hold two 15 MB copies of the same weights. This
|
||||
/// is also the only directory an Android build can populate for itself — the
|
||||
/// entry point extracts the APK's bundled copy here before any store opens,
|
||||
/// and at that moment no session exists to key a per-account path off.
|
||||
/// entry point extracts the APK's bundled copy here, and no session exists at
|
||||
/// that point to key a per-account path off.
|
||||
///
|
||||
/// **That extraction races the first seconds of a launch and is meant to.** It
|
||||
/// is 41 MB of copying and it used to happen before the first frame, which on a
|
||||
/// tablet is an ANR (`darkroom-android`'s `install_bundled_models`). So a
|
||||
/// lookup here can answer "absent" for a model that is on its way; each file is
|
||||
/// renamed into place, so what a lookup never sees is a half-written one.
|
||||
pub fn shared_face_models_dir() -> PathBuf {
|
||||
data_root().join("models")
|
||||
}
|
||||
|
||||
@@ -733,8 +733,11 @@ fn load_scene_model() -> Result<dr_segment::SceneModel, String> {
|
||||
{
|
||||
// Account-independent, like `shared_face_models_dir` and for the same
|
||||
// reason: this runs on a worker with no session in hand. Android
|
||||
// unpacks the APK's copy to exactly this directory before any store
|
||||
// opens.
|
||||
// unpacks the APK's copy to exactly this directory, on a worker of its
|
||||
// own started at launch — so on the first launch after an install this
|
||||
// can report the model missing for the couple of seconds the 25 MB copy
|
||||
// takes. See `shared_face_models_dir` for why it is no longer done
|
||||
// before the first frame.
|
||||
let dir = crate::library::shared_face_models_dir();
|
||||
dr_segment::SceneModel::from_path(
|
||||
dir.join("yolo26s-sem-ade20k.onnx"),
|
||||
|
||||
Reference in New Issue
Block a user