mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-24 10:16:59 -06:00
105 lines
4.4 KiB
Rust
105 lines
4.4 KiB
Rust
//! Rendering cache identity and ownership regressions without allocator/RSS assumptions.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use designcraft_compose::Cache;
|
|
use designcraft_doc::{Document, ParaFormat, SpreadRef};
|
|
use designcraft_geom::Rect;
|
|
use designcraft_render::{RenderOptions, Renderer};
|
|
|
|
fn serial_renderer() -> Renderer {
|
|
let mut renderer = Renderer::new();
|
|
renderer.threads = 0;
|
|
renderer
|
|
}
|
|
|
|
fn document() -> (Document, designcraft_doc::StoryId) {
|
|
let mut doc = Document::new(&Default::default());
|
|
let layer = doc.default_layer();
|
|
let (_, story) = doc
|
|
.add_text_frame(
|
|
SpreadRef::Doc(0),
|
|
Rect::new(36.0, 36.0, 500.0, 700.0),
|
|
layer,
|
|
&"A cached glyph path owns its outline. ".repeat(80),
|
|
ParaFormat::default(),
|
|
)
|
|
.unwrap();
|
|
(doc, story)
|
|
}
|
|
|
|
#[test]
|
|
fn glyph_paths_do_not_own_expired_composed_story_buffers() {
|
|
let (doc, sid) = document();
|
|
let cache = Cache::new();
|
|
let mut renderer = serial_renderer();
|
|
renderer.threads = 0;
|
|
let layout = cache.get(&doc, sid, None);
|
|
let lifetime = Arc::downgrade(&layout);
|
|
let initial = renderer.render_page(&doc, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
let frames = renderer.cached_frames();
|
|
assert!(frames > 0, "the regression requires actual cached glyph paths");
|
|
drop(layout);
|
|
assert!(lifetime.upgrade().is_some(), "the composition cache still owns this layout");
|
|
cache.clear();
|
|
assert!(lifetime.upgrade().is_none(), "glyph-path identity must not keep all story layout buffers alive");
|
|
assert_eq!(renderer.cached_frames(), frames, "do not achieve release by evicting cached paths");
|
|
let fresh = serial_renderer().render_page(&doc, &Cache::new(), 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
let rebuilt = renderer.render_page(&doc, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
assert_eq!(rebuilt.pixels, initial.pixels);
|
|
assert_eq!(rebuilt.pixels, fresh.pixels);
|
|
}
|
|
|
|
#[test]
|
|
fn repeated_cache_generations_preserve_pixels_and_do_not_pin_old_layouts() {
|
|
let (doc, sid) = document();
|
|
let cache = Cache::new();
|
|
let mut renderer = serial_renderer();
|
|
renderer.threads = 0;
|
|
let reference = serial_renderer().render_page(&doc, &Cache::new(), 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
for _ in 0..16 {
|
|
let layout = cache.get(&doc, sid, None);
|
|
let lifetime = Arc::downgrade(&layout);
|
|
let cold = renderer.render_page(&doc, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
let frames = renderer.cached_frames();
|
|
let warm = renderer.render_page(&doc, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
assert_eq!(cold.pixels, reference.pixels);
|
|
assert_eq!(warm.pixels, reference.pixels);
|
|
assert_eq!(renderer.cached_frames(), frames, "warm render must reuse existing glyph entries");
|
|
drop(layout);
|
|
cache.clear();
|
|
assert!(lifetime.upgrade().is_none());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn edit_style_decoration_and_snapshot_restoration_match_fresh_pixels() {
|
|
let (original, sid) = document();
|
|
let mut edited = original.clone();
|
|
edited.story_mut(sid).unwrap().insert(0, "é changed text. ");
|
|
assert!(edited.story(sid).unwrap().text.starts_with("é changed text. "));
|
|
let mut styled = edited.clone();
|
|
for style in &mut styled.styles_mut().paragraph {
|
|
style.chars.font_family = Some("Source Sans 3".into());
|
|
style.chars.underline = Some(true);
|
|
style.chars.strikethrough = Some(true);
|
|
}
|
|
let cache = Cache::new();
|
|
let mut renderer = serial_renderer();
|
|
renderer.threads = 0;
|
|
let mut states = Vec::new();
|
|
for doc in [&original, &edited, &styled, &edited, &original, &edited, &styled] {
|
|
doc.check().unwrap();
|
|
let warm = renderer.render_page(doc, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
let fresh = serial_renderer().render_page(doc, &Cache::new(), 0, 1.0, false, &RenderOptions::default()).unwrap();
|
|
assert_eq!(warm.pixels, fresh.pixels);
|
|
states.push(warm.pixels);
|
|
}
|
|
assert_ne!(states[0], states[1], "text edit must affect the viewport");
|
|
assert_ne!(states[1], states[2], "font and decorations must affect the viewport");
|
|
assert_eq!(states[0], states[4], "original snapshot restored exactly");
|
|
assert_eq!(states[1], states[3]);
|
|
assert_eq!(states[1], states[5]);
|
|
assert_eq!(states[2], states[6], "styled snapshot restored exactly");
|
|
}
|