tawasuyu

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 229TOML 101Markdown 82Shell 24052 Definiciones

⎇ main llimphi-ui/tests/invariante_mount_pool.rs Rust · 59 líneas · 2.5 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros d251eb32b · Sergio ·

1// Guardarraíl del reciclado de buffers (Bloque 21, palanca 1).
2//
3// La invariante: **nadie asigna `last_render = None` a mano**. El único camino
4// sancionado es `RuntimeState::invalidar_render`, que invalida la caché de
5// hit-test PERO se queda con el `Vec<MountedNode>` en `mount_pool` para que el
6// mount siguiente escriba encima (a 200k nodos, 432 MB por frame).
7//
8// Existe porque el agujero se escapó DOS veces seguidas, y las dos veces fue
9// invisible: el reciclado compilaba, corría, y no reciclaba nada. La primera vez
10// porque todo `update` tiraba la caché; la segunda porque quedó un sitio sin
11// convertir — `dispatch_model`, que se llama con `prim.`, no con `state.`, y por
12// eso no cayó en el barrido. Es el camino de TODO Msg del canal: el más caliente
13// que hay.
14//
15// Rust no sabe expresar "este campo se lee desde cualquier lado pero se escribe
16// sólo acá" (no hay visibilidad por campo dentro del mismo módulo), así que la
17// invariante se sostiene leyendo la fuente. Feo, pero atrapa exactamente lo que
18// se escapó.
19
20use std::fs;
21use std::path::Path;
22
23/// Único uso legítimo: la caché de una ventana **secundaria**
24/// (`SecRenderCache`), que es otro tipo y no alimenta el pool de la primaria.
25const PERMITIDO: &str = "sec.last_render = None";
26
27fn recorrer(dir: &Path, hallazgos: &mut Vec<String>) {
28    for entrada in fs::read_dir(dir).expect("leer src/") {
29        let ruta = entrada.expect("entrada").path();
30        if ruta.is_dir() {
31            recorrer(&ruta, hallazgos);
32        } else if ruta.extension().is_some_and(|e| e == "rs") {
33            let fuente = fs::read_to_string(&ruta).expect("leer fuente");
34            for (i, linea) in fuente.lines().enumerate() {
35                let l = linea.trim();
36                // Sólo asignaciones reales: descarta comentarios y doc.
37                if l.starts_with("//") {
38                    continue;
39                }
40                if l.contains("last_render = None") && !l.contains(PERMITIDO) {
41                    hallazgos.push(format!("{}:{}: {l}", ruta.display(), i + 1));
42                }
43            }
44        }
45    }
46}
47
48#[test]
49fn nadie_tira_el_mounted_a_mano() {
50    let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
51    let mut hallazgos = Vec::new();
52    recorrer(&src, &mut hallazgos);
53    assert!(
54        hallazgos.is_empty(),
55        "asignación cruda de `last_render = None` — usá `invalidar_render()`, que \
56         conserva los buffers del árbol montado en `mount_pool`:\n  {}",
57        hallazgos.join("\n  ")
58    );
59}