M15.2: merge main (disk cache, 3D Camera Tracker); camera analysis in workers; no disk cache on wasm32

The 3D Camera Tracker analysis joins the offloaded jobs. The disk cache stays off in the
browser (std::env::temp_dir panics on wasm32 once a config store exists).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-03 00:03:43 -04:00
co-authored by Claude Opus 5.5
116 changed files with 18084 additions and 82 deletions
+38
View File
@@ -27,6 +27,7 @@ impl Session {
self.prefs.normalize();
self.layer_cache.set_budget(self.prefs.layer_cache_bytes());
self.footage.set_cache_budget(self.prefs.media_cache_bytes());
self.configure_disk_cache();
// Fewer undo levels apply right away.
let levels = self.prefs.general.undo_levels.max(1) as usize;
if self.history.undo.len() > levels {
@@ -36,6 +37,43 @@ impl Session {
self.prefs_revision += 1;
}
/// Apply Settings ▸ Media & Disk Cache ▸ Disk Cache: open (or close) the persistent cache
/// and attach it to the layer cache. Without a folder setting the platform cache folder is
/// used, but only by frontends with a settings store (headless sessions stay off disk).
pub fn configure_disk_cache(&mut self) {
use effectcraft_render::disk_cache::{DiskCache, default_folder, footage_salt};
let d = &self.prefs.disk;
let folder = if d.disk_cache_folder.trim().is_empty() {
(self.config.is_some() && !cfg!(target_arch = "wasm32")).then(default_folder)
} else {
Some(std::path::PathBuf::from(d.disk_cache_folder.trim()))
};
let max = d.disk_cache_max_gb.max(1) as u64 * (1 << 30);
// (no file system in the browser: the disk cache stays off there)
let Some(folder) = folder.filter(|_| d.disk_cache_enabled && !cfg!(target_arch = "wasm32")) else {
self.disk_cache = None;
self.layer_cache.set_disk(None);
return;
};
let dc = match &self.disk_cache {
Some(c) if c.folder() == folder.as_path() => {
c.set_max_bytes(max);
c.clone()
}
_ => match DiskCache::open(&folder, max) {
Ok(c) => c,
Err(e) => {
log::warn!("disk cache at {}: {e}", folder.display());
self.disk_cache = None;
self.layer_cache.set_disk(None);
return;
}
},
};
self.layer_cache.set_disk(Some((dc.clone(), footage_salt(&self.project))));
self.disk_cache = Some(dc);
}
/// Write settings to the config store.
pub fn save_prefs(&self) {
if let Some(c) = &self.config