From 214cc1de407dfd21d1a307afdd1bfffb1a1646a9 Mon Sep 17 00:00:00 2001 From: STP Date: Sat, 11 Nov 2023 18:13:37 -0500 Subject: [PATCH] Added program class for generic programs --- Cargo.lock | 17 +++ Cargo.toml | 1 + src/main.rs | 140 ++++++++++++++++++- src/window.rs | 372 -------------------------------------------------- 4 files changed, 153 insertions(+), 377 deletions(-) delete mode 100644 src/window.rs diff --git a/Cargo.lock b/Cargo.lock index 9606f5f..f649aad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,6 +1084,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "pkg-config" version = "0.3.27" @@ -1223,6 +1229,7 @@ dependencies = [ "log", "nalgebra", "pollster", + "tokio", "wgpu", "winit", ] @@ -1437,6 +1444,16 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tokio" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +dependencies = [ + "backtrace", + "pin-project-lite", +] + [[package]] name = "toml_datetime" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index d1e6d1f..cca3b8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ env_logger = "0.10" log = "0.4" pollster = "0.3" wgpu = "0.17" +tokio = "1.34.0" diff --git a/src/main.rs b/src/main.rs index 59fc4c6..d297e53 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,146 @@ #![allow(dead_code)] #![allow(unused_imports)] +#![allow(unused_variables)] //Use our ray module mod ray; use ray::Ray; -//Use our window -mod window; -use window::run; +//Use our display +pub mod display; +use display::Display; //Use linear algebra module use nalgebra::Vector4; //Use modules for wgpu +use std::iter; +use winit::{ + event::*, + event_loop::{ControlFlow, EventLoop}, + window::Window, + window::WindowBuilder, +}; + +pub struct Program { + display: Display, +} + +impl Program { + pub async fn new() -> Self { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + let display = Display::new(window, "shader.wgsl".to_string()).await; + Program { display } + } +impl Program {} + async fn start_event_loop(mut self) { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + event_loop.run(move |event, _, control_flow| match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => { + if !self.input(event) { + match event { + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(physical_size) => { + self.display.resize(*physical_size); + } + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + self.display.resize(**new_inner_size); + } + _ => {} + } + } + } + Event::RedrawRequested(window_id) if window_id == window.id() => { + self.update(); + match self.display.render(Self::render) { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { + self.display.update_surface(); + } + Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, + Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), + } + } + Event::RedrawEventsCleared => { + window.request_redraw(); + } + _ => {} + }); + } + + fn input(&self, event: &WindowEvent) -> bool { + match event { + WindowEvent::MouseInput { .. } => true, + WindowEvent::KeyboardInput { input, .. } => { + match input.virtual_keycode.expect("Not a keycode") { + VirtualKeyCode::Space => true, + _ => false, + } + } + _ => true, + } + } + + fn update(&mut self) {} + + fn render( + device: &wgpu::Device, + queue: &wgpu::Queue, + output: &wgpu::SurfaceTexture, + render_pipeline: &wgpu::RenderPipeline, + ) -> Result<(), wgpu::SurfaceError> { + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 1.0, + b: 1.0, + g: 1.0, + a: 1.0, + }), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(render_pipeline); + render_pass.draw(0..3, 0..1); // 3. + } + + // submit will accept anything that implements IntoIter + queue.submit(std::iter::once(encoder.finish())); + output.present(); + Ok(()) + } +} //MAIN --------------------------------------------------------------------------------- -fn main() { - pollster::block_on(run()); +#[tokio::main] +async fn main() { + let program = Program::new(); + program.start_event_loop().await; } diff --git a/src/window.rs b/src/window.rs deleted file mode 100644 index 9b8e525..0000000 --- a/src/window.rs +++ /dev/null @@ -1,372 +0,0 @@ -use std::iter; - -use winit::{ - event::*, - event_loop::{ControlFlow, EventLoop}, - window::Window, - window::WindowBuilder, -}; - -pub struct State { - //Surface we will draw two - surface: wgpu::Surface, - // Description of a surface - config: wgpu::SurfaceConfiguration, - //Device we will he using to render - device: wgpu::Device, - // Command query for a divice - queue: wgpu::Queue, - size: winit::dpi::PhysicalSize, - // The window must be declared after the surface so - // it gets dropped after it as the surface contains - // unsafe references to the window's resources. - window: winit::window::Window, - // Handle to a rendering pipeline - render_pipeline: wgpu::RenderPipeline, - //Challenge 2: Alternate pipeline - render_pipeline2: wgpu::RenderPipeline, - challange2: bool, - // Challenge 1: Clear Color - clear_color: wgpu::Color, -} - -impl State { - // Creating some of the wgpu types requires async code - async fn new(window: Window) -> Self { - //Self explaining, the window size - let size = window.inner_size(); - // Backends to handle our gpu - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::all(), - dx12_shader_compiler: Default::default(), - }); - // Surface needs to be alive as long as window is created - let surface = unsafe { instance.create_surface(&window) }.unwrap(); - // Handles our graphics card, gets backend the adapter uses - // Compatible surface fields tells wgpu whats a adapter that can supply a surface - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - // Use the adapter to create a device and queue - // Includes limitations on the graphics card - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), - // WebGL doesn't support all of wgpu's features, so if - // we're building for the web we'll have to disable some. - limits: if cfg!(target_arch = "wasm32") { - wgpu::Limits::downlevel_webgl2_defaults() - } else { - wgpu::Limits::default() - }, - label: None, - }, - None, // Trace path - ) - .await - .unwrap(); - // Some surfaces have different capabilities, get the capabilities - let surface_caps = surface.get_capabilities(&adapter); - // Find how surface textures will be stored on the GPU - let surface_format = surface_caps - .formats - .iter() - .copied() - .find(|f| f.is_srgb()) - .unwrap_or(surface_caps.formats[0]); - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: size.width, - height: size.height, - present_mode: surface_caps.present_modes[0], - alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], - }; - surface.configure(&device, &config); - //Load a shader into our device and get a module handler - let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); - //Create pipeline layout using device - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - //Create final render pipeline - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", // 1. - buffers: &[], // 2. - }, - fragment: Some(wgpu::FragmentState { - // 3. - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - // 4. - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, // 1. - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, // 2. - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: None, // 1. - multisample: wgpu::MultisampleState { - count: 1, // 2. - mask: !0, // 3. - alpha_to_coverage_enabled: false, // 4. - }, - multiview: None, // 5. - }); - - let challange2 = false; - let render_pipeline2 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", // 1. - buffers: &[], // 2. - }, - fragment: Some(wgpu::FragmentState { - // 3. - module: &shader, - entry_point: "fs_alt", - targets: &[Some(wgpu::ColorTargetState { - // 4. - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, // 1. - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, // 2. - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: None, // 1. - multisample: wgpu::MultisampleState { - count: 1, // 2. - mask: !0, // 3. - alpha_to_coverage_enabled: false, // 4. - }, - multiview: None, // 5. - }); - - let clear_color = wgpu::Color { - r: 0.0, - g: 0.2, - b: 0.4, - a: 1.0, - }; - - Self { - window, - surface, - device, - queue, - config, - size, - render_pipeline, - render_pipeline2, - challange2, - clear_color, - } - } - - pub fn window(&self) -> &Window { - &self.window - } - - 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); - } - } - - fn input(&mut self, event: &WindowEvent) -> bool { - match event { - WindowEvent::MouseInput { .. } => { - self.clear_color = wgpu::Color { - r: 0.5, // Example values for when mouse is clicked - g: 0.5, - b: 0.5, - a: 1.0, - }; - true - } - WindowEvent::KeyboardInput { input, .. } => { - match input.virtual_keycode.expect("Not a keycode") { - VirtualKeyCode::Space => { - self.challange2 ^= true; - true - } - _ => false, - } - } - _ => true, - } - } - - fn update(&mut self) {} - - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - let output = self.surface.get_current_texture()?; - - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(self.clear_color), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - match self.challange2 { - true => render_pass.set_pipeline(&self.render_pipeline), - false => render_pass.set_pipeline(&self.render_pipeline2), - }; - render_pass.draw(0..3, 0..1); // 3. - } - - // submit will accept anything that implements IntoIter - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) - } -} -pub async fn run() { - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init_with_level(log::Level::Warn).expect("Couldn't initialize logger"); - } else { - env_logger::init(); - } - } - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - - #[cfg(target_arch = "wasm32")] - { - // Winit prevents sizing with CSS, so we have to set - // the size manually when on web. - use winit::dpi::PhysicalSize; - window.set_inner_size(PhysicalSize::new(450, 400)); - - use winit::platform::web::WindowExtWebSys; - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| { - let dst = doc.get_element_by_id("wasm-example")?; - let canvas = web_sys::Element::from(window.canvas()); - dst.append_child(&canvas).ok()?; - Some(()) - }) - .expect("Couldn't append canvas to document body."); - } - - // State::new uses async code, so we're going to wait for it to finish - let mut state = State::new(window).await; - - event_loop.run(move |event, _, control_flow| { - match event { - Event::WindowEvent { - ref event, - window_id, - } if window_id == state.window().id() => { - if !state.input(event) { - // UPDATED! - match event { - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - 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(window_id) if window_id == state.window().id() => { - state.update(); - match state.render() { - Ok(_) => {} - // Reconfigure the surface if it's lost or outdated - Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { - state.resize(state.size) - } - // The system is out of memory, we should probably quit - Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, - - Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), - } - } - Event::RedrawEventsCleared => { - // RedrawRequested will only trigger once, unless we manually - // request it. - state.window().request_redraw(); - } - _ => {} - } - }); -}