use std::sync::{Arc, Mutex}; use wgpu::{ naga::FastHashMap, Backends, CommandEncoder, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor, InstanceFlags, Queue, RequestAdapterOptions, Surface, SurfaceConfiguration, Texture, TextureFormat, TextureUsages }; use winit::{ dpi::PhysicalSize, event::{ElementState, KeyEvent, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window }; use crate::game::Game; pub struct Engine<'a> { surface: Surface<'a>, pub device: Arc<Device>, pub queue: Arc<Queue>, pub config: Arc<Mutex<SurfaceConfiguration>>, pub size: PhysicalSize<u32>, pub window: Arc<Window>, keys: Keys, state: Game, focused: bool, } #[derive(Debug, Default)] pub enum KeyState { #[default] Released, Pressed, Held, } impl KeyState { fn is_down(&self) -> bool { match self { Self::Released => false, Self::Pressed | Self::Held => true, } } } #[derive(Default)] pub struct Keys { values: FastHashMap<KeyCode, KeyState>, } impl Keys { fn update_key(&mut self, key: KeyCode, state: ElementState) { let key_state = self.values.entry(key).or_default(); if key_state.is_down() != state.is_pressed() { *key_state = if state.is_pressed() { KeyState::Pressed } else { KeyState::Released } } } fn tick_keys(&mut self) { for (_, state) in &mut self.values { if state.is_down() { *state = KeyState::Held; } } } pub fn is_key_down(&self, key: KeyCode) -> bool { if let Some(key_state) = self.values.get(&key) { key_state.is_down() } else { false } } pub fn was_key_released(&self, key: KeyCode) -> bool { !self.is_key_down(key) } pub fn was_key_pressed(&self, key: KeyCode) -> bool { matches!(self.values.get(&key), Some(KeyState::Pressed)) } } impl<'a> Engine<'a> { pub fn new(window: Arc<Window>) -> Self { let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), flags: InstanceFlags::advanced_debugging(), ..Default::default() }); let surface = instance.create_surface(window.clone()).unwrap(); let (adapter, device, queue) = async_std::task::block_on(async { let adapter = instance .request_adapter(&RequestAdapterOptions { compatible_surface: Some(&surface), ..Default::default() }) .await .unwrap(); let (device, queue) = adapter .request_device( &DeviceDescriptor { label: Some("device"), required_features: Features::empty(), ..Default::default() }, None, ) .await .unwrap(); (adapter, device, queue) }); let (device, queue) = (Arc::new(device), Arc::new(queue)); let size = window.inner_size(); let caps = surface.get_capabilities(&adapter); let format = caps.formats.iter().find(|f| matches!(f, TextureFormat::Bgra8Unorm)).cloned().unwrap_or(caps.formats[0]); let config = Arc::new(Mutex::new(SurfaceConfiguration { usage: TextureUsages::RENDER_ATTACHMENT, format, width: size.width, height: size.height, present_mode: caps.present_modes[0], desired_maximum_frame_latency: 2, alpha_mode: caps.alpha_modes[0], view_formats: vec![], })); surface.configure(&device, &config.lock().unwrap()); let keys = Keys::default(); let state = Game::new(&UpdateContext { device: &device, queue: &queue, window: &window, keys: &keys, config: &config.clone(), }); Self { surface, device, queue, config, size, window, keys: Keys::default(), focused: false, state, } } pub fn handle_event(&mut self, window_event: &WindowEvent) { match window_event { WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(key), state, repeat, .. }, .. } => { if *repeat { return; } self.keys.update_key(*key, *state); } WindowEvent::Focused(focused) => { self.focused = *focused; } _ => {} } } pub fn resize(&mut self, new_size: PhysicalSize<u32>) { if new_size.width > 0 && new_size.height > 0 { self.size = new_size; { let mut config = self.config.lock().unwrap(); config.width = new_size.width; config.height = new_size.height; self.surface.configure(&self.device, &config); } self.state.resize( &UpdateContext { device: &self.device, queue: &self.queue, keys: &self.keys, window: &self.window, config: &self.config, }, new_size, ); } } pub fn update(&mut self) { self.state.update(&UpdateContext { device: &self.device, queue: &self.queue, keys: &self.keys, window: &self.window, config: &self.config, }); self.keys.tick_keys(); } pub fn render(&self) -> Result<(), wgpu::SurfaceError> { let output = self.surface.get_current_texture()?; let mut encoder = self.device.create_command_encoder(&Default::default()); self.state.render(self, &output.texture, &mut encoder); self.queue.submit(std::iter::once(encoder.finish())); output.present(); Ok(()) } } pub struct UpdateContext<'a> { pub device: &'a Arc<Device>, pub queue: &'a Arc<Queue>, pub keys: &'a Keys, pub window: &'a Arc<Window>, pub config: &'a Arc<Mutex<SurfaceConfiguration>>, } pub trait EngineState { fn new(context: &UpdateContext) -> Self where Self: Sized; fn update(&mut self, context: &UpdateContext); fn resize(&mut self, context: &UpdateContext, new_size: PhysicalSize<u32>); fn render(&self, engine: &Engine, surface: &Texture, encoder: &mut CommandEncoder); }