tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
feat: llimphi standalone — framework UI soberano extraído del monorepo
1//! llimphi-raster — Brocha Matemática.
2//!
3//! Traduce primitivas vectoriales (líneas, curvas de Bézier, texto) a
4//! píxeles via Compute Shaders. Backend: `vello`.
5//!
6//! Punto de entrada: [`Renderer`]. Recibe una [`vello::Scene`] y la pinta
7//! sobre un [`llimphi_hal::Frame`].
8
9use llimphi_hal::{Frame, Hal};
10pub use vello;
11pub use vello::kurbo;
12pub use vello::peniko;
13
14pub mod gpu;
15pub use gpu::{GpuBatch, GpuPipelines};
16
17/// Errores del rasterizador.
18#[derive(Debug)]
19pub enum RasterError {
20 Init(String),
21 Render(String),
22}
23
24impl std::fmt::Display for RasterError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::Init(s) => write!(f, "vello init: {s}"),
28 Self::Render(s) => write!(f, "vello render: {s}"),
29 }
30 }
31}
32
33impl std::error::Error for RasterError {}
34
35/// Rasterizador vectorial. Una instancia por surface (porque vello cachea
36/// resources contra un `surface_format` específico).
37pub struct Renderer {
38 inner: vello::Renderer,
39}
40
41impl Renderer {
42 /// Inicializa el rasterizador. Vello acepta cualquier textura compatible
43 /// (Rgba8Unorm / Bgra8Unorm) en `render`, así que no se fija un formato
44 /// en construcción.
45 ///
46 /// **`antialiasing_support`**: pedimos `area` solamente, no `all()`.
47 /// `area` es el único método que `render()` usa (`AaConfig::Area`
48 /// fijo). Pedir `all()` haría a vello compilar también pipelines
49 /// para `msaa8` y `msaa16` que nunca se invocan — en Mali-G57 eso
50 /// triplica el cold-start (medido: 3.7s vs ~1.2s). Si alguna app
51 /// futura necesita MSAA, agregamos un constructor explícito.
52 ///
53 /// **`num_init_threads: None`**: vello paraleliza la compilación
54 /// de shaders en `None` → todos los CPU cores. Mali-G57 viene en
55 /// SoCs octa-core ARM; con 1 thread tardamos 2.0s, con 8 esperamos
56 /// ~400-600ms. La compilación de shaders es 100% CPU (Rust →
57 /// SPIR-V), el GPU no participa, así que multi-thread escala
58 /// casi linealmente hasta saturar el queue del Naga compiler.
59 pub fn new(hal: &Hal) -> Result<Self, RasterError> {
60 let inner = vello::Renderer::new(
61 &hal.device,
62 vello::RendererOptions {
63 use_cpu: false,
64 antialiasing_support: vello::AaSupport {
65 area: true,
66 msaa8: false,
67 msaa16: false,
68 },
69 num_init_threads: None,
70 pipeline_cache: None,
71 },
72 )
73 .map_err(|e| RasterError::Init(e.to_string()))?;
74 Ok(Self { inner })
75 }
76
77 /// Renderiza `scene` sobre `frame` limpiando con `base_color`. AA fija
78 /// en area-sampling (precisión Δ < 10⁻⁹ rad del SDD).
79 pub fn render(
80 &mut self,
81 hal: &Hal,
82 scene: &vello::Scene,
83 frame: &Frame,
84 base_color: peniko::Color,
85 ) -> Result<(), RasterError> {
86 let (width, height) = frame.size();
87 self.render_to_view(hal, scene, frame.view(), width, height, base_color)
88 }
89
90 /// Como [`render`](Self::render) pero contra una vista de textura
91 /// explícita (mismo formato/tamaño que la intermedia). Lo usa el
92 /// compositor de overlay de `llimphi-ui` para rasterizar la capa de
93 /// overlay sobre fondo transparente en su propia textura. Ojo:
94 /// `render_to_texture` **limpia** el target con `base_color` y escribe
95 /// todos los píxeles — no compone sobre contenido previo.
96 pub fn render_to_view(
97 &mut self,
98 hal: &Hal,
99 scene: &vello::Scene,
100 view: &llimphi_hal::wgpu::TextureView,
101 width: u32,
102 height: u32,
103 base_color: peniko::Color,
104 ) -> Result<(), RasterError> {
105 self.inner
106 .render_to_texture(
107 &hal.device,
108 &hal.queue,
109 scene,
110 view,
111 &vello::RenderParams {
112 base_color,
113 width,
114 height,
115 antialiasing_method: vello::AaConfig::Area,
116 },
117 )
118 .map_err(|e| RasterError::Render(e.to_string()))
119 }
120}