use std::sync::Arc; #[derive(Clone)] pub struct ViewportInfo { pub output_format: wgpu::TextureFormat, pub depth_format: wgpu::TextureFormat, } pub struct ViewportViews<'a> { pub output: &'a wgpu::TextureView, pub depth: &'a wgpu::TextureView, } pub trait Viewport { fn get_info(&self) -> ViewportInfo; fn get_queue(&self) -> &wgpu::Queue; fn get_views(&self) -> ViewportViews; } pub struct WinitViewport { pub device: Arc, pub queue: Arc, pub size: winit::dpi::PhysicalSize, surface: wgpu::Surface, config: wgpu::SurfaceConfiguration, info: ViewportInfo, depth_texture: wgpu::Texture, depth_texture_view: wgpu::TextureView, surface_texture: Option, output_view: Option, } impl WinitViewport { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; pub async fn from_window(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_supported_formats(&adapter).first().unwrap(), width: size.width, height: size.height, present_mode: wgpu::PresentMode::Fifo, }; surface.configure(&device, &config); let device = Arc::new(device); let queue = Arc::new(queue); let info = ViewportInfo { output_format: config.format, depth_format: Self::DEPTH_FORMAT, }; let (depth_texture, depth_texture_view) = Self::make_depth_texture(&device, &config); Self { device, queue, size, surface, config, info, depth_texture, depth_texture_view, surface_texture: None, output_view: None, } } pub fn acquire(&mut self) -> Result<(), wgpu::SurfaceError> { if self.output_view.is_none() { let surface_texture = self.surface.get_current_texture()?; self.output_view = Some( surface_texture .texture .create_view(&wgpu::TextureViewDescriptor::default()), ); self.surface_texture = Some(surface_texture); } Ok(()) } pub fn resize(&mut self, new_size: winit::dpi::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); let (depth_texture, depth_texture_view) = Self::make_depth_texture(&self.device, &self.config); self.depth_texture = depth_texture; self.depth_texture_view = depth_texture_view; } } pub fn present(&mut self) { if let Some(surface_texture) = self.surface_texture.take() { surface_texture.present(); self.output_view.take(); } } fn make_depth_texture( device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, ) -> (wgpu::Texture, wgpu::TextureView) { let size = wgpu::Extent3d { width: config.width, height: config.height, depth_or_array_layers: 1, }; let desc = wgpu::TextureDescriptor { label: Some("Depth Texture"), size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, }; let texture = device.create_texture(&desc); let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); (texture, view) } } impl Viewport for WinitViewport { fn get_info(&self) -> ViewportInfo { self.info.clone() } fn get_queue(&self) -> &wgpu::Queue { &self.queue } fn get_views(&self) -> ViewportViews { let output = self.output_view.as_ref().unwrap(); ViewportViews { output, depth: &self.depth_texture_view, } } }