diff --git a/crates/rendering/src/layers/text.rs b/crates/rendering/src/layers/text.rs index fe4e40ed557..4a368f71cb0 100644 --- a/crates/rendering/src/layers/text.rs +++ b/crates/rendering/src/layers/text.rs @@ -1054,6 +1054,85 @@ mod gpu_tests { assert_eq!(after_edit, fresh(&frame)); } + #[test] + #[ignore = "requires a working wgpu adapter"] + fn reused_layouts_follow_each_shaping_input() { + type Edit = (&'static str, fn(&mut PreparedText)); + let (device, queue) = device().expect("A working wgpu adapter is required for this test"); + let base = PreparedText { + color: [0.0, 0.0, 0.0, 1.0], + background_color: None, + content: "Hi yo\ngq".to_string(), + font_size: 16.0, + line_height: 1.25, + ..text([4.0, 4.0, 60.0, 60.0], [0.0; 4]) + }; + // Family, weight, and italic only change pixels when the host has a + // distinct face. The cache key still has to include them: a miss leaves + // the previous buffer unused. + let face_edits: [Edit; 3] = [ + ("font family", |text| { + text.font_family = "monospace".to_string() + }), + ("font weight", |text| text.font_weight = 800.0), + ("italic", |text| text.italic = true), + ]; + let layout_edits: [Edit; 5] = [ + ("font size", |text| { + text.font_size = 20.0; + text.line_height = 1.0; + }), + ("letter spacing", |text| text.letter_spacing = 3.0), + ("alignment", |text| text.align = TextAlign::Right), + ("line height", |text| text.line_height = 2.0), + ("wrap width", |text| text.bounds[2] = 24.0), + ]; + let render_fresh = |text: &PreparedText| { + let mut layer = TextLayer::new(&device, &queue); + layer.prepare( + &device, + &queue, + (OUTPUT, OUTPUT), + std::slice::from_ref(text), + ); + render_pixels(&device, &queue, &layer) + }; + let base_pixels = render_fresh(&base); + assert!(base_pixels.chunks_exact(4).any(|pixel| pixel != [255; 4])); + + let check = |name: &str, edit: fn(&mut PreparedText), require_pixel_change: bool| { + let mut edited = base.clone(); + edit(&mut edited); + let expected = render_fresh(&edited); + if require_pixel_change { + assert!( + expected != base_pixels, + "{name} edit should change the output" + ); + } + + let mut reused = TextLayer::new(&device, &queue); + reused.prepare(&device, &queue, (OUTPUT, OUTPUT), &[base.clone()]); + reused.prepare(&device, &queue, (OUTPUT, OUTPUT), &[edited]); + assert_eq!( + reused.previous_buffers.len(), + 1, + "{name} edit reused the previous layout" + ); + assert_eq!( + render_pixels(&device, &queue, &reused), + expected, + "{name} edit drew a stale layout" + ); + }; + for (name, edit) in face_edits { + check(name, edit, false); + } + for (name, edit) in layout_edits { + check(name, edit, true); + } + } + #[test] #[ignore = "requires a working wgpu adapter"] fn text_background_timeline_preserves_order_and_shrinks_without_stale_layers() {