use egui_wgpu_backend::wgpu; use egui_wgpu_backend::RenderPass as EguiRenderPass; use egui_winit::winit::{ self, dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; use std::sync::Arc; mod render; mod ui; struct Application { window: winit::window::Window, device: Arc, queue: Arc, size: winit::dpi::PhysicalSize, surface: wgpu::Surface, config: wgpu::SurfaceConfiguration, egui_state: egui_winit::State, egui_ctx: egui::Context, egui_rp: EguiRenderPass, ui: ui::UserInterface, viewport: ui::ViewportWidget, render_state: render::RenderState, } impl Application { pub async fn new(window: winit::window::Window) -> Self { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY); let surface = unsafe { instance.create_surface(&window) }; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::LowPower, compatible_surface: Some(&surface), force_fallback_adapter: false, }) .await .unwrap(); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), label: None, }, None, ) .await .unwrap(); let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface.get_preferred_format(&adapter).unwrap(), width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, }; surface.configure(&device, &config); let device = Arc::new(device); let queue = Arc::new(queue); let egui_state = egui_winit::State::new(4096, &window); let egui_ctx = egui::Context::default(); let mut egui_rp = egui_wgpu_backend::RenderPass::new(&device, config.format, 1); let render_state = render::RenderState::new(device.clone(), queue.clone(), config.format, &mut egui_rp); let viewport_texture = render_state.resources.get::().unwrap().viewport.egui_texture; Self { window, device, queue, size, surface, config, egui_state, egui_ctx, egui_rp, ui: ui::UserInterface::new(), viewport: ui::ViewportWidget::new(viewport_texture), render_state, } } pub fn update(&mut self) { self.window.request_redraw(); } pub fn on_resize(&mut self, new_size: PhysicalSize) { if new_size.width > 0 && new_size.height > 0 { self.size = new_size; self.config.width = new_size.width; self.config.height = new_size.height; self.surface.configure(&self.device, &self.config); } } pub fn render(&mut self) { match self.surface.get_current_texture() { Err(wgpu::SurfaceError::Lost) => self.on_resize(self.size), Err(e) => panic!("Surface error: {:?}", e), Ok(surface_texture) => { puffin::GlobalProfiler::lock().new_frame(); let output = { puffin::profile_scope!("Draw egui"); let raw_input = self.egui_state.take_egui_input(&self.window); self.egui_ctx.run(raw_input, |ctx| self.ui.run(ctx, &mut self.viewport)) }; { puffin::profile_scope!("Main render"); self.render_state.update_viewport(&mut self.viewport); self.render_state.render(); } puffin::profile_scope!("Render egui"); self.egui_state.handle_platform_output( &self.window, &self.egui_ctx, output.platform_output, ); let meshes = self.egui_ctx.tessellate(output.shapes); let screen_desc = egui_wgpu_backend::ScreenDescriptor { physical_width: self.config.width, physical_height: self.config.height, scale_factor: 1.0, // TODO ??? }; self.egui_rp .update_buffers(&self.device, &self.queue, &meshes, &screen_desc); self.egui_rp .add_textures(&self.device, &self.queue, &output.textures_delta) .unwrap(); let output_view = surface_texture.texture.create_view(&Default::default()); let mut cmds = self.device.create_command_encoder(&Default::default()); self.egui_rp .execute(&mut cmds, &output_view, &meshes, &screen_desc, None) .unwrap(); self.queue.submit(std::iter::once(cmds.finish())); surface_texture.present(); self.egui_rp.remove_textures(output.textures_delta).unwrap(); } } } } fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("Cyborg Editor") .build(&event_loop) .unwrap(); let mut app = pollster::block_on(Application::new(window)); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; // println!("{:?}", event); match event { Event::RedrawRequested(window_id) if window_id == app.window.id() => { app.render(); } Event::MainEventsCleared => { app.update(); if app.ui.should_quit() { *control_flow = ControlFlow::Exit; } } Event::WindowEvent { event, window_id } if window_id == app.window.id() => { match &event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { app.on_resize(*physical_size); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { app.on_resize(**new_inner_size); } _ => {} } app.egui_state.on_event(&app.egui_ctx, &event); } _ => (), } }); }