mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
- memory::budget (default min(25% RAM, 1.5 GiB); LIGHTCRAFT_MEMORY_MB, app.memoryBudget): half for engine caches (sources + rendered previews, LRU across them via a global use tick; the photo on screen stays), a quarter for decodes in flight (WorkGate: thumbnails, prefetch and import probes wait; loupe/export loads never wait), an eighth for the GPU free pool. - GPU: best-fit buffer reuse (<= 25% slack), buffers released outside a render are pooled at once (no stranded retired lists), pool trimmed after 3 s idle. - Decodes drop file bytes and raw samples early, colour/crop/orient in place. - macOS: apps install malloc_zone_pressure_relief as the release hook (after each decode, import and idle trim): freed large blocks no longer stay resident. 14 raws + loupe + 12 steps (M4 Pro): peak RSS 1.9-2.3 GB -> 0.83-0.99 GB, footprint 2.5-2.6 GB -> 0.91-0.96 GB, heap peak 930 -> 656 MB; stepping median ~13 ms. Co-Authored-By: Claude Opus 5.5 <[email protected]>
42 lines
1.9 KiB
Rust
42 lines
1.9 KiB
Rust
//! Device-memory bookkeeping: a view's stages are counted, released buffers are pooled within
|
|
//! the pool limit, and trimming frees them. Skips when no GPU adapter exists.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use lightcraft_develop::DevelopSettings;
|
|
use lightcraft_pipeline::{RenderRequest, SourceInfo, StageCache};
|
|
|
|
#[test]
|
|
fn stages_are_counted_and_the_pool_is_bounded_and_trimmed() {
|
|
if !lightcraft_gpu::available() {
|
|
eprintln!("skipped: no GPU adapter");
|
|
return;
|
|
}
|
|
let limit = 64u64 << 20;
|
|
lightcraft_gpu::set_pool_limit(limit);
|
|
let info = SourceInfo { raw: true, ..Default::default() };
|
|
let mut s = DevelopSettings::default();
|
|
s.effects.clarity = 30.0;
|
|
let stages = StageCache::default();
|
|
// photos of slightly different sizes, like stepping through a library
|
|
for (i, (w, h)) in [(1200usize, 800usize), (1190, 810), (1210, 790), (800, 1200)].into_iter().enumerate() {
|
|
let src = Arc::new(lightcraft_scenes::demo_library()[i].render(w, h));
|
|
let r = lightcraft_gpu::render(&src, &info, &s, &RenderRequest::fit(w, h), Some(&stages)).expect("gpu render");
|
|
assert_eq!((r.image.width, r.image.height), (w, h));
|
|
let m = lightcraft_gpu::memory();
|
|
assert!(m.pooled <= limit, "pool {} over its limit", m.pooled);
|
|
assert_eq!(m.retired, 0, "a finished render leaves nothing retired");
|
|
}
|
|
let held = lightcraft_gpu::stage_bytes(&stages);
|
|
assert!(held >= 1200 * 800 * 3 * 4, "the view's stages hold at least its resampled source: {held}");
|
|
let before = lightcraft_gpu::memory().allocated;
|
|
stages.clear();
|
|
let m = lightcraft_gpu::memory();
|
|
assert_eq!(lightcraft_gpu::stage_bytes(&stages), 0);
|
|
assert!(m.pooled <= limit && m.allocated <= before);
|
|
lightcraft_gpu::trim_pool(0);
|
|
let m = lightcraft_gpu::memory();
|
|
assert_eq!(m.pooled, 0);
|
|
assert!(m.allocated < before, "trimming frees the pooled buffers: {} → {}", before, m.allocated);
|
|
}
|