mod buffer; mod camera; mod light; mod model; mod pipeline; pub mod prelude; mod texture; pub use buffer::*; pub use camera::*; pub use light::*; pub use model::*; pub use pipeline::*; pub use texture::*; use anyhow::*; use cgmath::*; use std::time::{Duration, Instant}; use wgpu::util::{BufferInitDescriptor, DeviceExt}; use winit::event::*; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::{Window, WindowBuilder}; pub struct Display { surface: wgpu::Surface, pub sc_desc: wgpu::SwapChainDescriptor, pub swap_chain: wgpu::SwapChain, pub device: wgpu::Device, pub queue: wgpu::Queue, } impl Display { pub async fn new(window: &Window) -> Result { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let surface = unsafe { instance.create_surface(window) }; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::Default, compatible_surface: Some(&surface), }) .await .unwrap(); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), shader_validation: true, }, None, ) .await .unwrap(); let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, format: wgpu::TextureFormat::Bgra8UnormSrgb, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Fifo, }; let swap_chain = device.create_swap_chain(&surface, &sc_desc); Ok(Self { surface, sc_desc, swap_chain, device, queue, }) } pub fn resize(&mut self, width: u32, height: u32) { self.sc_desc.width = width; self.sc_desc.height = height; self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); } } /** * Holds the camera data to be passed to wgpu. */ #[repr(C)] #[derive(Copy, Clone)] pub struct UniformData { view_position: cgmath::Vector4, view_proj: cgmath::Matrix4, } unsafe impl bytemuck::Zeroable for UniformData {} unsafe impl bytemuck::Pod for UniformData {} pub struct Uniforms { data: UniformData, buffer: wgpu::Buffer, } impl Uniforms { pub fn new(device: &wgpu::Device) -> Self { let data = UniformData { view_position: Zero::zero(), view_proj: cgmath::Matrix4::identity(), }; let buffer = device.create_buffer_init(&BufferInitDescriptor { label: Some("Uniform Buffer"), contents: bytemuck::cast_slice(&[data]), usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, }); Self { data, buffer } } pub fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) { self.data.view_position = camera.position.to_homogeneous(); self.data.view_proj = projection.calc_matrix() * camera.calc_matrix() } pub fn update_buffer(&self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) { let staging_buffer = device.create_buffer_init(&BufferInitDescriptor { label: Some("Uniform Update Buffer"), contents: bytemuck::cast_slice(&[self.data]), usage: wgpu::BufferUsage::COPY_SRC, }); encoder.copy_buffer_to_buffer( &staging_buffer, 0, &self.buffer, 0, std::mem::size_of::() as _, ); } } /** * Holds the wgpu::BindGroupLayout and one wgpu::BindGroup for the * just the Uniforms struct. */ pub struct UniformBinding { pub layout: wgpu::BindGroupLayout, pub bind_group: wgpu::BindGroup, } impl UniformBinding { pub fn new(device: &wgpu::Device, uniforms: &Uniforms) -> Self { let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::UniformBuffer { dynamic: false, min_binding_size: None, }, count: None, }], label: Some("UniformBinding::layout"), }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)), }], label: Some("UniformBinding::bind_group"), }); Self { layout, bind_group } } pub fn rebind(&mut self, device: &wgpu::Device, uniforms: &Uniforms) { self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &self.layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)), }], label: Some("UniformBinding::bind_group"), }); } } pub trait Demo: 'static + Sized { fn init(display: &Display) -> Result; fn process_mouse(&mut self, dx: f64, dy: f64); fn resize(&mut self, display: &Display); fn update(&mut self, display: &Display, dt: Duration); fn render(&mut self, display: &mut Display); } pub async fn run() -> Result<(), Error> { let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title(env!("CARGO_PKG_NAME")) .build(&event_loop)?; let mut display = Display::new(&window).await?; let mut demo = D::init(&mut display)?; let mut last_update = Instant::now(); let mut is_resumed = true; let mut is_focused = true; event_loop.run(move |event, _, control_flow| { *control_flow = if is_resumed && is_focused { ControlFlow::Poll } else { ControlFlow::Wait }; match event { Event::Resumed => is_resumed = true, Event::Suspended => is_resumed = false, Event::RedrawRequested(wid) => { if wid == window.id() { let now = Instant::now(); let dt = now - last_update; last_update = now; demo.update(&mut display, dt); demo.render(&mut display); } } Event::MainEventsCleared => { if is_focused && is_resumed { window.request_redraw(); } else { // Freeze time while the demo is not in the foreground last_update = Instant::now(); } } Event::WindowEvent { event, window_id, .. } => { if window_id == window.id() { match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Focused(f) => is_focused = f, WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { display.resize(new_inner_size.width, new_inner_size.height); demo.resize(&mut display); } WindowEvent::Resized(new_inner_size) => { display.resize(new_inner_size.width, new_inner_size.height); demo.resize(&mut display); } _ => {} } } } _ => {} } }); }