tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros
1//! El `clear_screen` del navegador: pinta un `<canvas>` de gris plomo con
2//! [`CanvasSurface`], el HAL de wasm32.
3//!
4//! Es el gemelo web de `clear_screen.rs` y cumple la misma función que aquel:
5//! ser el consumidor mínimo del HAL, para que un cambio de API rompa acá y no
6//! meses después en una app. Ejercita el camino entero —canvas del DOM →
7//! `Hal::for_canvas` → `resize_to_css` → `acquire` → render pass → `present`—
8//! sin depender del event loop de `llimphi-ui`, que todavía no cruzó a web.
9//!
10//! Verificación (no linkea, no necesita navegador ni wasm-bindgen-cli):
11//!
12//! ```sh
13//! cargo check -p llimphi-hal --target wasm32-unknown-unknown --examples
14//! ```
15//!
16//! Para correrlo de verdad hace falta empaquetarlo con `wasm-bindgen-cli` y
17//! servir un HTML con un `<canvas id="llimphi">`. No hay script de eso todavía
18//! — ver `PORTING.md` §wasm32.
19
20// En cualquier target nativo este example no aplica: la `CanvasSurface` sólo
21// existe en wasm32. Un `main` vacío deja que `--examples` lo compile en el
22// smoke test del workspace sin ensuciar la salida.
23#[cfg(not(target_arch = "wasm32"))]
24fn main() {
25 eprintln!(
26 "llimphi · canvas_clear — este example es sólo para wasm32-unknown-unknown \
27 (usa CanvasSurface). En escritorio el equivalente es `clear_screen`."
28 );
29}
30
31#[cfg(target_arch = "wasm32")]
32fn main() {
33 wasm_bindgen_futures::spawn_local(async {
34 if let Err(e) = correr().await {
35 web_sys::console::error_1(&format!("llimphi · canvas_clear: {e}").into());
36 }
37 });
38}
39
40#[cfg(target_arch = "wasm32")]
41const LEAD_GRAY: llimphi_hal::wgpu::Color = llimphi_hal::wgpu::Color {
42 r: 0.235,
43 g: 0.239,
44 b: 0.247,
45 a: 1.0,
46};
47
48#[cfg(target_arch = "wasm32")]
49async fn correr() -> Result<(), String> {
50 use llimphi_hal::{wgpu, Hal, Surface};
51 use wasm_bindgen::JsCast;
52
53 let canvas: web_sys::HtmlCanvasElement = web_sys::window()
54 .ok_or("sin `window`")?
55 .document()
56 .ok_or("sin `document`")?
57 .get_element_by_id("llimphi")
58 .ok_or("no hay ningún elemento con id=\"llimphi\"")?
59 .dyn_into()
60 .map_err(|_| "el elemento #llimphi no es un <canvas>")?;
61
62 // Surface-first, igual que en Android: el adapter se pide contra ESTA
63 // surface. `for_canvas` es async porque en el main thread del navegador no
64 // se puede bloquear.
65 let (hal, mut surface) = Hal::for_canvas(canvas).await.map_err(|e| e.to_string())?;
66
67 // El backing store del canvas por `devicePixelRatio`. Sin esto el canvas se
68 // ve borroso en HiDPI (el navegador escala un buffer chico).
69 let (w, h) = surface.resize_to_css();
70 web_sys::console::log_1(&format!("llimphi · canvas_clear — surface {w}×{h} px físicos").into());
71
72 let frame = surface.acquire().map_err(|e| e.to_string())?;
73 let mut encoder = hal
74 .device
75 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
76 label: Some("canvas_clear-encoder"),
77 });
78 {
79 let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
80 label: Some("canvas_clear-pass"),
81 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
82 // La intermedia, no el swapchain: es el mismo contrato que en
83 // escritorio (vello pinta acá y `present` blittea).
84 view: frame.view(),
85 resolve_target: None,
86 depth_slice: None,
87 ops: wgpu::Operations {
88 load: wgpu::LoadOp::Clear(LEAD_GRAY),
89 store: wgpu::StoreOp::Store,
90 },
91 })],
92 depth_stencil_attachment: None,
93 timestamp_writes: None,
94 occlusion_query_set: None,
95 multiview_mask: None,
96 });
97 }
98 hal.queue.submit(std::iter::once(encoder.finish()));
99 surface.present(frame, &hal);
100 Ok(())
101}