feat: llimphi standalone — framework UI soberano extraído del monorepo
Motor gráfico Llimphi como workspace independiente: bucle Elm (input→update→view→layout→raster→present) sobre wgpu+vello+taffy+parley. Núcleo (hal/raster/layout/text/ui/theme/surface/motion/icons) + ~40 widgets + módulos, sin dependencias al resto del monorepo. cargo check --workspace pasa (64 crates). Puerta de entrada: cargo run -p llimphi-ui --example counter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
//! Fase 1 de Llimphi: ventana gris plomo a la frecuencia máxima del display.
|
||||
//!
|
||||
//! Corre con: `cargo run -p llimphi-hal --example clear_screen --release`.
|
||||
//!
|
||||
//! Imprime fps por stderr cada segundo. En un panel de 144 Hz con AutoVsync
|
||||
//! debe estabilizarse cerca de 144; en uno de 60 Hz, cerca de 60.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use llimphi_hal::winit::application::ApplicationHandler;
|
||||
use llimphi_hal::winit::dpi::LogicalSize;
|
||||
use llimphi_hal::winit::event::WindowEvent;
|
||||
use llimphi_hal::winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use llimphi_hal::winit::window::{Window, WindowAttributes, WindowId};
|
||||
use llimphi_hal::{wgpu, Hal, Surface, WinitSurface};
|
||||
|
||||
const LEAD_GRAY: wgpu::Color = wgpu::Color {
|
||||
r: 0.235,
|
||||
g: 0.239,
|
||||
b: 0.247,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
struct State {
|
||||
window: Arc<Window>,
|
||||
hal: Hal,
|
||||
surface: WinitSurface,
|
||||
}
|
||||
|
||||
struct App {
|
||||
state: Option<State>,
|
||||
frames: u64,
|
||||
last_report: Instant,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.state.is_some() {
|
||||
return;
|
||||
}
|
||||
let window = event_loop
|
||||
.create_window(
|
||||
WindowAttributes::default()
|
||||
.with_title("llimphi · clear_screen")
|
||||
.with_inner_size(LogicalSize::new(960u32, 540u32)),
|
||||
)
|
||||
.expect("create window");
|
||||
let window = Arc::new(window);
|
||||
let hal = pollster::block_on(Hal::new(None)).expect("hal");
|
||||
let surface = WinitSurface::new(&hal, window.clone()).expect("surface");
|
||||
window.request_redraw();
|
||||
self.state = Some(State {
|
||||
window,
|
||||
hal,
|
||||
surface,
|
||||
});
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let Some(state) = self.state.as_mut() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::Resized(size) => {
|
||||
state.surface.resize(size.width, size.height);
|
||||
state.window.request_redraw();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
let frame = match state.surface.acquire() {
|
||||
Ok(f) => f,
|
||||
Err(_) => {
|
||||
let (w, h) = state.surface.size();
|
||||
state.surface.resize(w, h);
|
||||
state.window.request_redraw();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut encoder =
|
||||
state
|
||||
.hal
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("clear_screen-encoder"),
|
||||
});
|
||||
{
|
||||
let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("clear_screen-pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: frame.view(),
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(LEAD_GRAY),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
}
|
||||
state.hal.queue.submit(std::iter::once(encoder.finish()));
|
||||
state.surface.present(frame, &state.hal);
|
||||
|
||||
self.frames += 1;
|
||||
let elapsed = self.last_report.elapsed();
|
||||
if elapsed.as_secs() >= 1 {
|
||||
let fps = self.frames as f64 / elapsed.as_secs_f64();
|
||||
eprintln!("llimphi · clear_screen — {fps:.1} fps");
|
||||
self.frames = 0;
|
||||
self.last_report = Instant::now();
|
||||
}
|
||||
state.window.request_redraw();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new().expect("event loop");
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
let mut app = App {
|
||||
state: None,
|
||||
frames: 0,
|
||||
last_report: Instant::now(),
|
||||
};
|
||||
event_loop.run_app(&mut app).expect("run app");
|
||||
}
|
||||
Reference in New Issue
Block a user