aura-gui 0.2.2

UI nativa acelerada por GPU para escritorio, Android e iOS, en un solo crate: estilo tipo Tailwind, componentes, sombras, animaciones y agentes de IA. Reposo a cero. Licencia AGPL-3.0 o comercial.
//! Tiempo de un frame típico del editor pintado por CPU a 2560×1640 (Retina). Es una medición, no una prueba:
//! `cargo test --release -p aura_gpu --test cpu_frame -- --ignored --nocapture`.

use aura_ui::core::{Bounds, Corners, Edges, Point, Rgba, Size, px};
use aura_ui::gpu::cpu::CpuRenderer;
use aura_ui::gpu::{MonochromeSprite, Quad, Scene, Shadow};
use std::time::Instant;

fn bounds(x: f32, y: f32, w: f32, h: f32) -> Bounds {
    Bounds::new(Point::new(px(x), px(y)), Size::new(px(w), px(h)))
}

fn editor_like_scene() -> Scene {
    let mut scene = Scene::default();
    let screen = bounds(0.0, 0.0, 1280.0, 820.0);
    let gray = |level: f32| Rgba::new(level, level, level, 1.0);
    scene.insert_quad(Quad::new(screen, screen, gray(0.08)));
    for (x, width) in [(0.0, 250.0), (250.0, 460.0), (1030.0, 250.0)] {
        scene.insert_quad(
            Quad::new(bounds(x, 40.0, width, 760.0), screen, gray(0.12)).border(Edges::all(px(1.0)), gray(0.2)),
        );
    }
    for index in 0..120 {
        let row = bounds(10.0 + (index % 3) as f32 * 340.0, 60.0 + (index / 3) as f32 * 18.0, 120.0, 16.0);
        scene.insert_quad(Quad::new(row, screen, gray(0.18)).corners(Corners::all(px(4.0))));
    }
    scene.insert_shadow(Shadow::new(
        bounds(400.0, 100.0, 480.0, 300.0),
        screen,
        Rgba::new(0.0, 0.0, 0.0, 0.4),
        Corners::all(px(8.0)),
        16.0,
    ));
    // ~2000 glifos de 7×14 px lógicos en 50 líneas de texto.
    for line in 0..50 {
        for column in 0..40 {
            let glyph = bounds(260.0 + column as f32 * 8.0, 60.0 + line as f32 * 15.0, 7.0, 14.0);
            scene.insert_monochrome(MonochromeSprite::new(
                glyph,
                screen,
                [0.0, 0.0, 14.0 / 1024.0, 28.0 / 1024.0],
                gray(0.9),
            ));
        }
    }
    scene
}

#[test]
#[ignore]
fn frame_tipico_del_editor_por_cpu() {
    let mut renderer = CpuRenderer::new(None, 2560, 1640);
    let scene = editor_like_scene();
    let mut pixels = vec![0u32; 2560 * 1640];
    renderer.draw(&mut pixels, &scene, 2.0, Rgba::new(0.0, 0.0, 0.0, 1.0));
    let frames = 20;
    let started = Instant::now();
    for _ in 0..frames {
        renderer.draw(&mut pixels, &scene, 2.0, Rgba::new(0.0, 0.0, 0.0, 1.0));
    }
    let per_frame = started.elapsed().as_secs_f64() * 1000.0 / frames as f64;
    println!("frame por CPU a 2560×1640: {per_frame:.1} ms ({} primitivas)", scene.batches.len());
    renderer.resize(1280, 820);
    let mut small = vec![0u32; 1280 * 820];
    let started = Instant::now();
    for _ in 0..frames {
        renderer.draw(&mut small, &scene, 1.0, Rgba::new(0.0, 0.0, 0.0, 1.0));
    }
    println!("frame por CPU a 1280×820: {:.1} ms", started.elapsed().as_secs_f64() * 1000.0 / frames as f64);
}