use std::collections::BTreeMap; use rand::{Rng, SeedableRng}; use renderer::{ Renderer2, render_graph, swapchain::{Surface, SwapchainConfiguration}, }; use tracing::info; use tracing_subscriber::EnvFilter; use winit::{ application::ApplicationHandler, dpi::{LogicalSize, PhysicalSize}, event::ElementState, event_loop::EventLoop, raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}, window::{Window, WindowAttributes, WindowId}, }; struct WindowState { window: Window, egui_platform: egui_winit_platform::Platform, demo_app: egui_demo_lib::DemoWindows, scale_factor: f64, surface: Surface, } struct WinitState { last_resize_events: BTreeMap>, window_attrs: WindowAttributes, windows: BTreeMap, renderer: Renderer2, last_redraw: std::time::Instant, egui_state: renderer::EguiState, } impl WinitState { const BASE_WIDTH: u32 = 800; const BASE_HEIGHT: u32 = 600; fn new(window_title: String, display: DisplayHandle) -> WinitState { let renderer = Renderer2::new(display.as_raw()).expect("renderer"); Self { windows: BTreeMap::new(), last_resize_events: BTreeMap::new(), window_attrs: WindowAttributes::default() .with_title(window_title) .with_resizable(true) .with_inner_size(LogicalSize::new(Self::BASE_WIDTH, Self::BASE_HEIGHT)), // TODO: pass down this error and add some kind of error handling UI or dump egui_state: renderer::EguiState::new(renderer.device().clone()).unwrap(), renderer, last_redraw: std::time::Instant::now(), } } fn handle_final_resize(&mut self, window_id: WindowId, new_size: PhysicalSize) { _ = (window_id, new_size); info!("TODO: implement resize events"); if let Some(WindowState { surface, .. }) = self.windows.get(&window_id) { let config = surface .swapchain() .as_ref() .map(|swapchain| swapchain.config().clone()) .unwrap_or(SwapchainConfiguration::default()); surface .configure( self.renderer.device(), SwapchainConfiguration { extent: renderer::Extent2D { width: new_size.width, height: new_size.height, }, ..config }, ) .expect("swapchain recreation"); } } fn handle_draw_request(&mut self, window_id: WindowId) { _ = window_id; tracing::debug!( window_id = u64::from(window_id), "TODO: implement draw request {}ms", self.last_redraw.elapsed().as_millis() ); self.last_redraw = std::time::Instant::now(); if let Some(window) = self.windows.get_mut(&window_id) { // egui window.egui_platform.begin_pass(); window.demo_app.ui(&window.egui_platform.context()); let output = window.egui_platform.end_pass(Some(&window.window)); let textures_to_remove = output .textures_delta .free .iter() .filter_map(|id| self.egui_state.lookup_texture(*id)) .collect::>(); // rendering let render = self.renderer.draw_graph(&window.surface, |renderer, rg| { use renderer::rendering::{egui_pass, egui_pre_pass}; let framebuffer = rg.get_framebuffer().unwrap(); let dev = renderer.device().clone(); use renderer::ash::vk::Handle; let [r, g, b]: [f32; 3] = rand::prelude::StdRng::seed_from_u64(window.surface.raw().as_raw()).random(); render_graph::clear_pass(rg, renderer::util::Rgba([r, g, b, 1.0]), framebuffer); egui_pre_pass( &dev, rg, &mut renderer.texture_manager, &mut self.egui_state, &output, )?; let _ = egui_pass( &dev, rg, &mut renderer.texture_manager, &mut renderer.samplers, &mut self.egui_state, &window.egui_platform.context(), output, framebuffer, )?; renderer::Result::Ok(()) }); match smol::block_on(render).flatten() { Ok(_) => {} Err(e) => { tracing::error!("encountered error while rendering: {e}"); } } for tid in textures_to_remove { self.renderer.texture_manager_mut().remove_texture(tid); } window.window.request_redraw(); } } fn remove_window(&mut self, window_id: WindowId) { tracing::info!(window = u64::from(window_id), "window close requested"); self.windows.remove(&window_id); } fn create_window(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { let window = event_loop .create_window( self.window_attrs .clone() .with_window_level(winit::window::WindowLevel::AlwaysOnTop), ) .expect("new window"); let window_id = window.id(); tracing::info!(window = u64::from(window_id), "created new window"); let size = self .window_attrs .inner_size .map(|size| size.to_physical(1.0)) .unwrap_or(PhysicalSize::new(0, 0)); let extent = renderer::Extent2D { width: size.width, height: size.height, }; let surface = self .renderer .create_surface( window.window_handle().expect("window handle").as_raw(), extent, ) .expect("surface"); let scale_factor = window.scale_factor(); self.windows.insert( window_id, WindowState { window, scale_factor, demo_app: egui_demo_lib::DemoWindows::default(), egui_platform: egui_winit_platform::Platform::new( egui_winit_platform::PlatformDescriptor { physical_width: size.width, physical_height: size.height, scale_factor, ..Default::default() }, ), surface, }, ); } } impl ApplicationHandler for WinitState { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { tracing::debug!("winit::resumed"); self.create_window(event_loop); } fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { tracing::trace!("winit::about_to_wait"); for (&window, &resize) in self.last_resize_events.clone().iter() { self.handle_final_resize(window, resize); } self.last_resize_events.clear(); if self.windows.is_empty() { event_loop.exit(); } } fn window_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, window_id: winit::window::WindowId, event: winit::event::WindowEvent, ) { // if !matches!(event, winit::event::WindowEvent::Resized(_)) { // if let Some(resize) = self.last_resize_events.remove(&window_id) { // self.handle_final_resize(window_id, resize); // } // } if let Some(window) = self.windows.get_mut(&window_id) { window.egui_platform.handle_event(&event); } match event { winit::event::WindowEvent::Resized(physical_size) => { _ = self.last_resize_events.insert(window_id, physical_size); } winit::event::WindowEvent::CloseRequested | winit::event::WindowEvent::KeyboardInput { event: winit::event::KeyEvent { physical_key: winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::KeyQ), state: ElementState::Pressed, repeat: false, .. }, .. } => { self.remove_window(window_id); } winit::event::WindowEvent::KeyboardInput { event: winit::event::KeyEvent { physical_key: winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::Space), state: ElementState::Pressed, repeat: false, .. }, .. } => { self.create_window(event_loop); } winit::event::WindowEvent::KeyboardInput { device_id, event, is_synthetic, } => { _ = (device_id, event, is_synthetic); } winit::event::WindowEvent::CursorMoved { device_id, position, } => { _ = (device_id, position); } winit::event::WindowEvent::MouseInput { device_id, state, button, } => { _ = (device_id, state, button); } winit::event::WindowEvent::RedrawRequested => { self.handle_draw_request(window_id); } winit::event::WindowEvent::ScaleFactorChanged { scale_factor, .. } => { if let Some(window) = self.windows.get_mut(&window_id) { window.scale_factor = scale_factor; } } _ => {} // unhandled event } } } fn main() { _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .init(); let ev = EventLoop::new().unwrap(); ev.set_control_flow(winit::event_loop::ControlFlow::Poll); let display = ev.display_handle().expect("display handle"); let mut game = WinitState::new("Vidya".to_owned(), display); ev.run_app(&mut game).unwrap(); }