use std::iter; use wgpu::util::DeviceExt; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; #[repr(C)] #[derive(Copy, Clone, Debug)] struct Vertex { position: [f32; 3], color: [f32; 3], } unsafe impl bytemuck::Pod for Vertex {} unsafe impl bytemuck::Zeroable for Vertex {} impl Vertex { fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { wgpu::VertexBufferDescriptor { stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[ wgpu::VertexAttributeDescriptor { offset: 0, shader_location: 0, format: wgpu::VertexFormat::Float3, }, wgpu::VertexAttributeDescriptor { offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, shader_location: 1, format: wgpu::VertexFormat::Float3, }, ], } } } const VERTICES: &[Vertex] = &[ Vertex { position: [-0.0868241, 0.49240386, 0.0], color: [0.5, 0.0, 0.5], }, // A Vertex { position: [-0.49513406, 0.06958647, 0.0], color: [0.5, 0.0, 0.5], }, // B Vertex { position: [-0.21918549, -0.44939706, 0.0], color: [0.5, 0.0, 0.5], }, // C Vertex { position: [0.35966998, -0.3473291, 0.0], color: [0.5, 0.0, 0.5], }, // D Vertex { position: [0.44147372, 0.2347359, 0.0], color: [0.5, 0.0, 0.5], }, // E ]; const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4]; struct State { surface: wgpu::Surface, device: wgpu::Device, queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, render_pipeline: wgpu::RenderPipeline, size: winit::dpi::PhysicalSize, // NEW! vertex_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer, num_indices: u32, } impl State { async fn new(window: &Window) -> Self { let size = window.inner_size(); // The instance is a handle to our GPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU 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, // Trace path ) .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); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), bind_group_layouts: &[], push_constant_ranges: &[], }); let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), layout: Some(&render_pipeline_layout), vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, clamp_depth: false, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { format: sc_desc.format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, vertex_state: wgpu::VertexStateDescriptor { index_format: wgpu::IndexFormat::Uint16, vertex_buffers: &[Vertex::desc()], }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), contents: bytemuck::cast_slice(VERTICES), usage: wgpu::BufferUsage::VERTEX, }); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), contents: bytemuck::cast_slice(INDICES), usage: wgpu::BufferUsage::INDEX, }); let num_indices = INDICES.len() as u32; Self { surface, device, queue, sc_desc, swap_chain, render_pipeline, vertex_buffer, index_buffer, num_indices, size, } } fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { self.size = new_size; self.sc_desc.width = new_size.width; self.sc_desc.height = new_size.height; self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); } #[allow(unused_variables)] fn input(&mut self, event: &WindowEvent) -> bool { false } fn update(&mut self) {} fn render(&mut self) { let frame = self .swap_chain .get_current_frame() .expect("Timeout getting texture") .output; let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"), }); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &frame.view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }), store: true, }, }], depth_stencil_attachment: None, }); render_pass.set_pipeline(&self.render_pipeline); render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..)); render_pass.draw_indexed(0..self.num_indices, 0, 0..1); } self.queue.submit(iter::once(encoder.finish())); } } fn main() { env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); use futures::executor::block_on; // Since main can't be async, we're going to need to block let mut state = block_on(State::new(&window)); event_loop.run(move |event, _, control_flow| { match event { Event::WindowEvent { ref event, window_id, } if window_id == window.id() => { if !state.input(event) { match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { input, .. } => match input { KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => *control_flow = ControlFlow::Exit, _ => {} }, WindowEvent::Resized(physical_size) => { state.resize(*physical_size); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { // new_inner_size is &mut so w have to dereference it twice state.resize(**new_inner_size); } _ => {} } } } Event::RedrawRequested(_) => { state.update(); state.render(); } Event::MainEventsCleared => { // RedrawRequested will only trigger once, unless we manually // request it. window.request_redraw(); } _ => {} } }); }