Asset import: full content-hash pass on every project open, even with nothing changed
Symptom
Opening Studio on a previously-imported project (e.g. the Bistro sample, ~1.7GB / 1276+ assets) always shows an "Import assets: ..." progress bar with Cancel, as if reimporting everything from scratch, even when nothing on disk changed since the last session.
Root cause (already partly diagnosed)
editor/assets/AssetImporter.zig's importAll → importAsset → asset_meta.needsReimport/the inline hash check in importAsset always reads and FNV-1a-hashes the full byte contents of every source asset on every project open, to decide whether it's unchanged — there's no cheaper staleness signal (mtime, size) checked first. This is by design (see the project_multi_mesh_import-era decision to move importAll off the frame-1 blocking path onto ImportJob/background task — studio/services/ImportJob.zig — so the window renders immediately with a progress bar instead of blocking with a black screen). Moving it to a background job fixed the blocking problem, but not the underlying work: on a project with a lot of asset bytes, reading+hashing everything is genuinely slow work, it just no longer freezes the UI while doing it.
So the "caching" that exists (skip recook when the hash matches) does work — but the hash computation itself, run unconditionally on every launch, is indistinguishable from a real reimport at a glance, and costs real wall-clock time proportional to total asset size regardless of whether anything changed.
Compounding interaction (found while testing #135 (closed))
ProjectOps.openProject defers Documents.restore() (reopening previously-open scene tabs) until the import job's completion callback, specifically because restoring a scene tab whose mesh/material GUIDs aren't cooked yet fails to load and never retries. This means a large project's scene tabs stay closed for the entire duration of the full-hash pass, not just for genuinely-changed assets — the exact case the user is asking to avoid ("open a recently closed project with no importing at all").
Suggested direction
Add a cheap fast path before the full content hash: compare source file mtime (+ size) against a value stored in .meta at last successful import, and only fall back to full-content hashing when that's inconclusive (e.g. mtime unreliable across filesystems/clones) or when explicitly forcing a reimport. This turns "nothing changed" into an near-instant stat() pass instead of reading gigabytes of texture data.
Also worth checking while in this area
While investigating, a separate but related asset-identity problem showed up on the Bistro project specifically: a handful of texture .meta sidecars exist twice — once correctly nested (assets/Textures/Foo.dds.meta) and once as a flat file whose name literally contains a backslash (assets/Textures\Foo.dds.meta, a leftover from the FBX materials' Windows-style texture path strings). The flat copy's .meta never carries a real hash (source_hash: 0 even at HEAD) and gets reassigned a fresh random GUID on every full reimport. Not the main topic of this issue, but likely worth its own look — possibly ModelDerivedAssets.zig's FBX texture-path resolution creating a literal-path asset entry instead of normalizing/matching against the real nested file.
Acceptance
- Reopening a project with no changed assets shows no (or a near-instant) import pass.
- Scene tabs restore promptly on a clean reopen instead of waiting for a full-hash pass over the whole project.