use std::sync::Arc; use thiserror::Error; use winit::{ dpi::PhysicalSize, event::WindowEvent, event_loop::EventLoopWindowTarget, window::Window, }; #[derive(Error, Debug)] pub enum ContextError { #[error("Surface creation failed: {0}")] Surface(#[from] wgpu::CreateSurfaceError), #[error("Surface configuration failed: {0}")] SurfaceConfig(String), #[error("No compatible adapter found")] Adapter, #[error("Device request failed: {0}")] Device(#[from] wgpu::RequestDeviceError), } pub struct Context<'window> { pub window: Arc, pub instance: wgpu::Instance, pub size: PhysicalSize, pub surface: wgpu::Surface<'window>, pub surface_config: wgpu::SurfaceConfiguration, pub adapter: wgpu::Adapter, pub device: wgpu::Device, pub queue: wgpu::Queue, } impl<'window> Context<'window> { pub async fn new( window: Arc, backends: wgpu::Backends, required_features: wgpu::Features, required_limits: wgpu::Limits, ) -> Result { log::info!("Initialising WGPU context..."); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends, dx12_shader_compiler: Default::default(), ..Default::default() }); // To be able to start drawing we need a few things: // - A surface // - A GPU device to draw to the surface // - A draw command queue log::info!("Initialising window surface..."); let surface = instance.create_surface(window.clone())?; log::info!("Requesting GPU adapter..."); let adapter = match instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter: false, compatible_surface: Some(&surface), }) .await { Some(val) => val, None => return Err(ContextError::Adapter), }; log::info!("Checking GPU adapter meets requirements"); log::info!("Requesting GPU device..."); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { label: None, required_features, required_limits, }, None, ) .await?; log::info!("Configuring window surface..."); let size = window.inner_size(); let surface_config = match surface.get_default_config(&adapter, size.width, size.height) { Some(val) => val, None => { return Err(ContextError::SurfaceConfig( "Surface configuration unsupported by adapter".to_string(), )) } }; surface.configure(&device, &surface_config); Ok(Self { window, instance, size, surface, surface_config, adapter, device, queue, }) } pub fn resize_surface(&mut self, new_size: PhysicalSize) { if new_size.width > 0 && new_size.height > 0 { self.size = new_size; self.surface_config.width = new_size.width; self.surface_config.height = new_size.height; self.surface.configure(&self.device, &self.surface_config); } } pub fn handle_window_event( &mut self, event: &WindowEvent, elwt: &EventLoopWindowTarget<()>, ) -> bool { let mut handled = true; match event { WindowEvent::CloseRequested => { elwt.exit(); } WindowEvent::Resized(physical_size) => { self.resize_surface(*physical_size); } WindowEvent::ScaleFactorChanged { .. } => { self.resize_surface(self.window.inner_size()); } _ => handled = false, } handled } }