use egui_wgpu::{RendererOptions, ScreenDescriptor}; use egui_winit::egui::{self, Context}; use pixels::{Pixels, SurfaceTexture}; use std::time::{Duration, Instant}; use winit::{ application::ApplicationHandler, event::{ElementState, MouseButton, WindowEvent}, event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, window::Window, }; const WIDTH: u32 = 320; const HEIGHT: u32 = 240; const FPS: u64 = 60; const FRAME_DURATION: Duration = Duration::from_micros(1_000_000 / FPS); pub type Error = Box; pub type Result = std::result::Result; struct Clicked { x: usize, y: usize, s: f64, } struct App { // core egui_renderer: Option, egui_state: Option, egui_context: Option, window: Option<&'static Window>, pixels: Option>, last_frame: Instant, // input last_mouse_pos: Option<(f64, f64)>, // experiemnts clicked: Vec, // debug red_level: u8, green_level: u8, blue_level: u8, } impl App { fn set_px(f: &mut [u8], x: u32, y: u32, r: u8, g: u8, b: u8, a: u8) { let idx = ((y * WIDTH + x) * 4) as usize; f[idx] = r; f[idx + 1] = g; f[idx + 2] = b; f[idx + 3] = a; } } impl Default for App { fn default() -> Self { Self { egui_context: None, egui_state: None, egui_renderer: None, window: None, pixels: None, last_frame: Instant::now(), last_mouse_pos: Some((0.0, 0.0)), clicked: Vec::new(), red_level: 0xFF, green_level: 0xFF, blue_level: 0xFF, } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = event_loop .create_window(Window::default_attributes().with_title("pxs")) .unwrap(); let size = window.inner_size(); let window_ref: &'static Window = Box::leak(Box::new(window)); let surface = SurfaceTexture::new(size.width, size.height, window_ref); let pixels = Pixels::new(WIDTH, HEIGHT, surface).unwrap(); let egui_context = Context::default(); let egui_state = egui_winit::State::new( egui_context.clone(), egui_context.viewport_id(), window_ref, None, None, None, ); let egui_renderer = egui_wgpu::Renderer::new(pixels.device(), pixels.render_texture_format(), { RendererOptions { msaa_samples: 1, ..RendererOptions::default() } }); self.egui_context = Some(egui_context); self.egui_state = Some(egui_state); self.egui_renderer = Some(egui_renderer); self.window = Some(window_ref); self.pixels = Some(pixels); } fn window_event( &mut self, event_loop: &ActiveEventLoop, _: winit::window::WindowId, event: WindowEvent, ) { if let Some(pixels) = &mut self.pixels && let Some(window) = self.window && let Some(egui_context) = &self.egui_context && let Some(egui_state) = &mut self.egui_state && let Some(egui_renderer) = &mut self.egui_renderer { // TODO how to use the response? let _ = egui_state.on_window_event(window, &event); match event { WindowEvent::CursorMoved { position, .. } => { self.last_mouse_pos = Some((position.x, position.y)); } WindowEvent::MouseInput { state, button, .. } => { if state == ElementState::Pressed && button == MouseButton::Left && let Some(last_mouse_pos) = self.last_mouse_pos { let coords = pixels .window_pos_to_pixel((last_mouse_pos.0 as f32, last_mouse_pos.1 as f32)) .unwrap(); self.clicked.push(Clicked { x: coords.0, y: coords.1, s: 25.0, }); } } WindowEvent::Resized(size) => { // Important: resize the surface when the window's size change pixels.resize_surface(size.width, size.height).unwrap(); } WindowEvent::CloseRequested => { event_loop.exit(); } WindowEvent::RedrawRequested => { // pixels logic let frame = pixels.frame_mut(); frame.fill(0); for y in 0..HEIGHT { for x in 0..WIDTH { let active = x % 2 == 0 && y % 2 == 0 && self.clicked.iter().all(|c| { (((x as i64) - (c.x as i64)).pow(2) + ((y as i64) - (c.y as i64)).pow(2)) .isqrt() as f64 > c.s }); Self::set_px( frame, x, y, if active { self.red_level } else { 0x00 }, if active { self.green_level } else { 0x00 }, if active { self.blue_level } else { 0x00 }, 0xFF, ); } } for i in 0..self.clicked.len() { self.clicked[i].s -= 0.3; if self.clicked[i].s <= 0.0 { self.clicked.remove(i); break; } } // egui logic let raw_input = egui_state.take_egui_input(window); let full_output = egui_context.run_ui(raw_input, |ui| { ui.heading("Debug"); ui.label( self.clicked .iter() .map(|c| format!("x={x},y={y},s={s}", x = c.x, y = c.y, s = c.s)) .collect::>() .join("\n"), ); ui.add(egui::Slider::new(&mut self.red_level, 0..=120).text("Red")); ui.add(egui::Slider::new(&mut self.green_level, 0..=120).text("Green")); ui.add(egui::Slider::new(&mut self.blue_level, 0..=120).text("Blue")); }); egui_state.handle_platform_output(window, full_output.platform_output); let clipped_primitives = egui_context.tessellate(full_output.shapes, full_output.pixels_per_point); let pixels_per_point = full_output.pixels_per_point; let size = window.inner_size(); let screen_descriptor = ScreenDescriptor { size_in_pixels: [size.width, size.height], pixels_per_point, }; let queue = pixels.queue(); let device = pixels.device(); for (id, delta) in &full_output.textures_delta.set { egui_renderer.update_texture(&device, &queue, *id, delta); } let _ = pixels.render_with(|encoder, render_target, context| { // 1. pixels' own blit of the framebuffer to the surface context.scaling_renderer.render(encoder, render_target); // 2. prep egui's GPU-side buffers for this frame's draws egui_renderer.update_buffers( device, &queue, encoder, &clipped_primitives, &screen_descriptor, ); // 3. open a pass on the same target, preserving the blit let mut pass = encoder .begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("egui pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: render_target, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, // keep the blit, don't clear store: wgpu::StoreOp::Store, }, depth_slice: None, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, multiview_mask: None, }) .forget_lifetime(); // bridge wgpu 30's pass-lifetime requirement egui_renderer.render(&mut pass, &clipped_primitives, &screen_descriptor); drop(pass); Ok(()) }); } _ => {} } } } fn about_to_wait(&mut self, _: &ActiveEventLoop) { let now = Instant::now(); // Limit to 60 FPS if now - self.last_frame >= FRAME_DURATION { self.last_frame = now; self.window .expect("Bug - Window should exist") .request_redraw(); } } } fn main() -> Result<()> { env_logger::init(); let event_loop = EventLoop::new()?; event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); event_loop.run_app(&mut app)?; Ok(()) }