mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 03:27:14 -06:00
#323: a window does not keep the whole source uploaded with its stages
Profiling a slider drag at 100 % and 400 % found each tick of the window render taking about 60 ms: the 24 MP source (288 MB) uploaded to the device was kept with the view's stages, counted against the stage budget, and the stages cleared every frame, so every tick uploaded and resampled again. A source over 96 MB is now uploaded for the render only; the stages keep the window's own sampled pixels. A tick costs 7-12 ms instead. equivalence.rs covers it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
35c9b67944
commit
2f7d248d61
@@ -414,6 +414,9 @@ fn warp_params(
|
||||
/// ~20 % faster than 8 at 6000 × 4000 (fewer interval evaluations, few more edge pixels).
|
||||
const COVER_ROWS: usize = 16;
|
||||
|
||||
/// The largest source (bytes on the device) a view's stages keep uploaded between renders.
|
||||
const RETAIN_SOURCE_BYTES: usize = 96 << 20;
|
||||
|
||||
/// The reference framing decision ([`Warp::covers`](lightcraft_pipeline::optics::Warp::covers)) for every
|
||||
/// output pixel, one bit per pixel, rows padded to 32-bit words. Blocks of 32 × [`COVER_ROWS`] pixels whose
|
||||
/// interval bounds place them clearly inside or outside the image are filled at once
|
||||
@@ -552,9 +555,11 @@ pub fn render(
|
||||
Some(e) => e.sampled.clone(),
|
||||
None if gpu.fits(src.data.len() * 3) => {
|
||||
let upload = || gpu.upload(rgb_words(src));
|
||||
// (a source too big to keep is uploaded for this render only: a window is cut from the
|
||||
// photo's own pixels, and what its stages keep is its own sampled pixels)
|
||||
let src_buf = match stages {
|
||||
Some(c) => c.source(src, upload),
|
||||
None => Arc::new(upload()),
|
||||
Some(c) if src.data.len() * 12 <= RETAIN_SOURCE_BYTES => c.source(src, upload),
|
||||
_ => Arc::new(upload()),
|
||||
};
|
||||
sample(&mut cx, src, src_buf, &plan)
|
||||
}
|
||||
|
||||
@@ -701,3 +701,29 @@ fn windows_match() {
|
||||
let corner = RenderRequest { window: Some(PixelWindow { x: 2360, y: 1520, w: 640, h: 480 }), ..RenderRequest::fit(3000, 2000) };
|
||||
check("window: vignette corner", &src, &info, &s, &corner);
|
||||
}
|
||||
|
||||
/// Issue #323: a window of a 24 MP photo is cut from a source of 288 MB on the device. Keeping that
|
||||
/// upload with the view's stages counted it against the stage budget on every tick of a drag and
|
||||
/// got the stages cleared, and the upload and resampling redone: the window's stages are its own
|
||||
/// pixels, not the photo's.
|
||||
#[test]
|
||||
fn a_window_does_not_keep_the_whole_source_on_the_device() {
|
||||
if !gpu() {
|
||||
return;
|
||||
}
|
||||
use lightcraft_pipeline::PixelWindow;
|
||||
let src = scene(1, 6000, 4000);
|
||||
let info = SourceInfo { raw: true, ..Default::default() };
|
||||
let stages = StageCache::default();
|
||||
let s = DevelopSettings::default();
|
||||
let req = RenderRequest { window: Some(PixelWindow { x: 2000, y: 1500, w: 640, h: 480 }), ..RenderRequest::fit(6000, 4000) };
|
||||
let _ = lightcraft_gpu::render(&src, &info, &s, &req, Some(&stages)).expect("gpu render");
|
||||
let held = lightcraft_gpu::stage_bytes(&stages);
|
||||
assert!(held < 100 << 20, "the view's GPU stages hold {} MiB for a 640×480 window", held >> 20);
|
||||
// …and the next tick of a drag reuses what is there
|
||||
let mut s2 = s.clone();
|
||||
s2.light.exposure = 0.5;
|
||||
let again = lightcraft_gpu::render(&src, &info, &s2, &req, Some(&stages)).expect("gpu render");
|
||||
assert_eq!((again.image.width, again.image.height), (640, 480));
|
||||
assert!(lightcraft_gpu::stage_bytes(&stages) < 100 << 20);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user