makepad/examples/text_atlas_stress/tests/ui.rs
Admin 45e65e69db draw+widgets: a Splash keeps its host's walk across a body rebuild, a dock redraws its panels, boxed labels center on their ink, hsv2rgb compiles, and slider/tip/dropdown catch up
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: video goes NV12 end to end, and the GPU does the unpacking
- widgets: boxed labels center on their ink, not on the font's line box
- metal: a fresh texture forgets nothing it never had — reallocated vec textures upload whole
- fab: a 3D creation shell and the viewer built on it
- widgets: a dock redraws its panels, not just its own frame
- draw: hsv2rgb takes vector bounds, so it compiles
- texcomp: the block codec and the container every texture will travel in
- widgets: a Splash keeps the walk its host declared across a body rebuild, and logs a body that fails to evaluate instead of drawing nothing
- vj: DJ-tab scroll knobs, title-click reset, crossfade fix, stem headroom — plus the pre-Ampere CUDA fix (#1193)

Co-authored-by: Rogier de Leeuw <vjroger@gmail.com>
2026-08-26 08:49:45 +02:00

87 lines
3.6 KiB
Rust

//! Glyphs cached before a heavy frame must still draw after it.
//!
//! Phase 0 renders four rows of text; the screenshot is the reference. Then
//! `stress` opens four rows in a second font (fresh glyph outlines, so the
//! append-only glyph atlas grows past its first rows) while `burst` creates
//! and binds 48 new textures inside every draw for eight frames. The original
//! rows must come out pixel-identical — a single glyph dropped from the
//! atlas shows as a change in that row's ink — and the new rows must carry
//! their own ink.
use makepad_test::{run_with_config, Selector, TestApp, TestConfig, TestError};
use makepad_zune_core::{bytestream::ZCursor, result::DecodingResult};
use makepad_zune_png::PngDecoder;
use std::path::Path;
/// Rows are stacked from the window's top edge, `ROW_H` points each, full
/// width (`MAKEPAD_HEADLESS_DPI=1` makes points equal screenshot pixels):
/// rows 0-3 regular, 4-7 bold. A two-point inset keeps neighbours apart.
const ROW_H: usize = 60;
const WIDTH: usize = 900;
fn row_rect(row: usize) -> (usize, usize, usize, usize) {
(0, row * ROW_H + 2, WIDTH, ROW_H - 4)
}
/// Lit pixels (any channel > 96) inside a rect of a screenshot.
fn ink(png: &Path, rect: (usize, usize, usize, usize)) -> usize {
let bytes = std::fs::read(png).expect("screenshot bytes");
let mut decoder = PngDecoder::new(ZCursor::new(&bytes));
let pixels = match decoder.decode().expect("png decode") {
DecodingResult::U8(p) => p,
_ => panic!("unexpected png sample type (expected 8-bit)"),
};
let (width, height) = decoder.dimensions().expect("png dimensions");
let channels = pixels.len() / (width * height);
let (x0, y0, w, h) = rect;
let mut count = 0;
for y in y0..(y0 + h).min(height) {
for x in x0..(x0 + w).min(width) {
let p = (y * width + x) * channels;
if pixels[p..p + 3].iter().any(|&c| c > 96) {
count += 1;
}
}
}
count
}
#[test]
fn glyphs_survive_a_texture_burst_frame() {
let mut config = TestConfig::current_package(
env!("CARGO_MANIFEST_DIR"),
env!("CARGO_PKG_NAME"),
"ui::glyphs_survive_a_texture_burst_frame",
)
.unwrap();
config.env.insert("MAKEPAD_HEADLESS_DPI".to_string(), "1".to_string());
run_with_config(config, |app: TestApp| -> Result<(), TestError> {
app.locator(Selector::id("row_3")).wait_visible();
app.locator(Selector::id("status")).wait_text("phase 0 frame 3");
let rows: Vec<_> = (0..4).map(row_rect).collect();
let before = app.screenshot();
let ink_before: Vec<usize> = rows.iter().map(|r| ink(&before, *r)).collect();
for (i, n) in ink_before.iter().enumerate() {
assert!(*n > 200, "row_{i} drew no text before the stress ({n} lit pixels)");
}
app.locator(Selector::id("stress")).wait_visible().click();
app.locator(Selector::id("row_b3")).wait_visible();
app.locator(Selector::id("status")).wait_text("phase 1 frame 8");
let after = app.screenshot();
let ink_after: Vec<usize> = rows.iter().map(|r| ink(&after, *r)).collect();
assert_eq!(
ink_after, ink_before,
"text rows changed across the stress frame (a glyph left the atlas): {before:?} vs {after:?}"
);
for i in 0..4 {
let bold = ink(&after, row_rect(4 + i));
assert!(
bold * 10 >= ink_before[i] * 7,
"row_b{i} lost glyphs: {bold} lit pixels against {} in row_{i} ({after:?})",
ink_before[i]
);
}
Ok(())
})
.unwrap();
}