Skip to main content

llimphi_raster/
lib.rs

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