mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 02:07:09 -06:00
Fix #2820: images drawn small redraw from their kept mips while being moved
With image mips on (the canvas always sets them), drawing an image fetched its full decoded pixels first, decoding them again when the shared image store no longer held them, and only then looked up the mip it draws. The full pixels are only peeked, so they are the least recently used entries and the first the store drops over its budget (1 GiB on macOS and Windows). Once a document's full-size images exceed it (20 photos of 6000 x 4000 px are 1.9 GB as RGBA), every frame decoded every image again, though only their small mips were drawn: moving one image redrew seconds later. The store now remembers the size each blob's pixels decode to, so the mip level is found without the pixels, and a mip the store holds is drawn (and marked used) without fetching or decoding them. Without the mip, at full size, or with mips off (exports) drawing is unchanged. Measured on an Apple M4 (load average 15-24 from other work), 20 noise PNGs of 6000 x 4000 px fitted in a 2880 x 1800 view, one moved 5 pt per frame: before, 20 decodes and 1.1-3.0 s every frame; after, the first frame decodes 20 and the later ones none, in 3-5 ms.
This commit is contained in:
committed by
bflatastic
parent
4444450a44
commit
4e0cbfeede
@@ -110,6 +110,29 @@ fn image_mips_are_opt_in_and_change_nothing_off() {
|
||||
assert!(Renderer::new().render(&d, 48, 48, near, &on).pixels == Renderer::new().render(&d, 48, 48, near, &off).pixels);
|
||||
}
|
||||
|
||||
/// #2820: with mips on, an image drawn at a quarter of its size draws its kept mip in later frames
|
||||
/// without its full pixels. Those go first when the store is over budget (only the mip is marked
|
||||
/// used), and were decoded again in every frame: dragging one of 20 large photos took seconds a
|
||||
/// frame.
|
||||
#[test]
|
||||
fn kept_mips_draw_without_decoding_the_full_pixels_again() {
|
||||
let _fonts = fonts_steady();
|
||||
let d = doc_with_image("img", png(64, 64, 104), 64, 64);
|
||||
let on = RenderOptions { image_mips: true, ..Default::default() };
|
||||
let quarter = Affine::scale(0.25);
|
||||
let mut r = Renderer::new();
|
||||
let _ = r.render(&d, 32, 16, quarter, &on);
|
||||
let full = r.image_pixmap(&d, "img", false, false).unwrap().0;
|
||||
images::store().remove(&full);
|
||||
// The image dragged 4 pt (a device pixel) right.
|
||||
let mut moved = d.clone();
|
||||
let id = moved.layers[0].children().unwrap()[0].id;
|
||||
moved.node_mut(id).unwrap().transform(Affine::translate((4.0, 0.0)), false);
|
||||
let frame = r.render(&moved, 32, 16, quarter, &on);
|
||||
assert!(images::store().peek(&full).is_none(), "the full pixels were decoded again");
|
||||
assert!(frame.pixels == Renderer::new().render(&moved, 32, 16, quarter, &on).pixels);
|
||||
}
|
||||
|
||||
/// A document of `n` point type objects reading `word`, in rows.
|
||||
fn text_doc(n: usize, word: &str) -> Document {
|
||||
use vectorcraft_doc::text::{CharStyle, TextObject};
|
||||
|
||||
Reference in New Issue
Block a user