diff --git a/src/application.rs b/src/application.rs index 276dda96..a9f12ce9 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,6 +1,5 @@ -// use super::render_state::RenderState; use super::color_palette::ColorPalette; -use super::gui_rect::GuiRect; +use super::window_events; use super::pipeline::Pipeline; use super::texture::Texture; use super::shader_stage::compile_from_glsl; @@ -9,8 +8,7 @@ use super::draw_command::DrawCommand; use super::gui_tree::GuiTree; use std::collections::VecDeque; use winit::event::*; -use winit::event_loop::ControlFlow; -use winit::event_loop::EventLoop; +use winit::event_loop::*; use winit::window::Window; use futures::executor::block_on; @@ -111,13 +109,17 @@ impl Application { // Load the vertex shader let vertex_shader_path = "shaders/shader.vert"; - let vertex_shader_module = compile_from_glsl(&self.device, vertex_shader_path, glsl_to_spirv::ShaderType::Vertex).unwrap(); - self.shader_cache.set(vertex_shader_path, vertex_shader_module); + if self.shader_cache.get(vertex_shader_path).is_none() { + let vertex_shader_module = compile_from_glsl(&self.device, vertex_shader_path, glsl_to_spirv::ShaderType::Vertex).unwrap(); + self.shader_cache.set(vertex_shader_path, vertex_shader_module); + } // Load the fragment shader let fragment_shader_path = "shaders/shader.frag"; - let fragment_shader_module = compile_from_glsl(&self.device, fragment_shader_path, glsl_to_spirv::ShaderType::Fragment).unwrap(); - self.shader_cache.set(fragment_shader_path, fragment_shader_module); + if self.shader_cache.get(fragment_shader_path).is_none() { + let fragment_shader_module = compile_from_glsl(&self.device, fragment_shader_path, glsl_to_spirv::ShaderType::Fragment).unwrap(); + self.shader_cache.set(fragment_shader_path, fragment_shader_module); + } // Get the shader pair let vertex_shader = self.shader_cache.get(vertex_shader_path).unwrap(); @@ -125,14 +127,18 @@ impl Application { // Construct a pipeline from the shader pair let pipeline_name = "example"; - let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader); - self.pipeline_cache.set(pipeline_name, pipeline); + if self.pipeline_cache.get(pipeline_name).is_none() { + let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader); + self.pipeline_cache.set(pipeline_name, pipeline); + } let example_pipeline = self.pipeline_cache.get(pipeline_name).unwrap(); // Load a texture from the image file let texture_path = "textures/grid.png"; - let texture = Texture::from_filepath(&self.device, &mut self.queue, texture_path).unwrap(); - self.texture_cache.set(texture_path, texture); + if self.texture_cache.get(texture_path).is_none() { + let texture = Texture::from_filepath(&self.device, &mut self.queue, texture_path).unwrap(); + self.texture_cache.set(texture_path, texture); + } let grid_texture = self.texture_cache.get(texture_path).unwrap(); // Create a BindGroup that holds a new TextureView @@ -156,17 +162,19 @@ impl Application { self.draw_command_queue.push_back(draw_command); } + // Initializes the event loop for rendering and event handling pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) { event_loop.run(move |event, _, control_flow| self.main_event_loop(event, control_flow, &window)); } + // Called every time by the event loop pub fn main_event_loop(&mut self, event: Event<'_, T>, control_flow: &mut ControlFlow, window: &Window) { // Wait for the next event to cause a subsequent event loop run, instead of looping instantly as a game would need *control_flow = ControlFlow::Wait; match event { // Handle all window events (like input and resize) in sequence - Event::WindowEvent { window_id, ref event } if window_id == window.id() => self.window_event(event, control_flow), + Event::WindowEvent { window_id, ref event } if window_id == window.id() => window_events::window_event(self, control_flow, event), // Handle raw hardware-related events not related to a window Event::DeviceEvent { .. } => (), // Handle custom-dispatched events @@ -185,54 +193,10 @@ impl Application { } } - pub fn window_event(&mut self, event: &WindowEvent, control_flow: &mut ControlFlow) { - match event { - WindowEvent::Resized(physical_size) => self.resize(*physical_size), - WindowEvent::Moved(_) => (), - WindowEvent::CloseRequested => self.quit(control_flow), - WindowEvent::Destroyed => (), - WindowEvent::DroppedFile(_) => (), - WindowEvent::HoveredFile(_) => (), - WindowEvent::HoveredFileCancelled => (), - WindowEvent::ReceivedCharacter(_) => (), - WindowEvent::Focused(_) => (), - WindowEvent::KeyboardInput { input, .. } => self.keyboard_event(input, control_flow), - WindowEvent::CursorMoved { .. } => (), - WindowEvent::CursorEntered { .. } => (), - WindowEvent::CursorLeft { .. } => (), - WindowEvent::MouseWheel { .. } => (), - WindowEvent::MouseInput { .. } => (), - WindowEvent::TouchpadPressure { .. } => (), - WindowEvent::AxisMotion { .. } => (), - WindowEvent::Touch(_) => (), - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => self.resize(**new_inner_size), - WindowEvent::ThemeChanged(_) => (), - } - } - - pub fn keyboard_event(&mut self, input: &KeyboardInput, control_flow: &mut ControlFlow) { - match input { - KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => self.quit(control_flow), - KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => self.example(), - _ => *control_flow = ControlFlow::Wait, - } - } - - pub fn quit(&self, control_flow: &mut ControlFlow) { - *control_flow = ControlFlow::Exit; - } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - self.swap_chain_descriptor.width = new_size.width; - self.swap_chain_descriptor.height = new_size.height; - - self.swap_chain = self.device.create_swap_chain(&self.surface, &self.swap_chain_descriptor); - - // TODO: Mark root of GUI as dirty to force redraw of everything - } - // Traverse dirty GUI elements and turn GUI changes into draw commands added to the render pipeline queue pub fn redraw_gui(&mut self, window: &Window) { + self.example(); + // If any draw commands were actually added, ask the window to dispatch a redraw event if !self.draw_command_queue.is_empty() { window.request_redraw(); diff --git a/src/main.rs b/src/main.rs index 4c90ffbb..417df370 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod resource_cache; mod shader_stage; mod draw_command; mod gui_tree; +mod window_events; use application::Application; use winit::event_loop::EventLoop; @@ -20,11 +21,7 @@ fn main() { let window = WindowBuilder::new().with_title("Graphite").build(&event_loop).unwrap(); // Initialize the render pipeline - let mut app = Application::new(&window); - app.example(); - - // State managers for render pipeline and program logic - // let app_render_state = RenderState::new(&mut app); + let app = Application::new(&window); // Begin the application lifecycle app.begin_lifecycle(event_loop, window); diff --git a/src/pipeline.rs b/src/pipeline.rs index 9b5256b1..16f47520 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -1,3 +1,5 @@ +use std::mem; + pub struct Pipeline { pub bind_group_layout: wgpu::BindGroupLayout, pub render_pipeline: wgpu::RenderPipeline, @@ -58,7 +60,7 @@ impl Pipeline { vertex_state: wgpu::VertexStateDescriptor { index_format: wgpu::IndexFormat::Uint16, vertex_buffers: &[wgpu::VertexBufferDescriptor { - stride: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, + stride: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[wgpu::VertexAttributeDescriptor { offset: 0, diff --git a/src/render_state.rs b/src/render_state.rs deleted file mode 100644 index c5899a30..00000000 --- a/src/render_state.rs +++ /dev/null @@ -1,26 +0,0 @@ -// use super::texture::Texture; -// use super::application::Application; - -// use std::collections::HashMap; - -// pub struct RenderState { -// pub render_pipeline: wgpu::RenderPipeline, -// pub vertex_buffer: wgpu::Buffer, -// pub index_buffer: wgpu::Buffer, -// pub num_indices: u32, -// pub texture: Texture, -// pub texture_bind_group: wgpu::BindGroup, -// } - -// impl RenderState { -// pub fn new(application: &mut Application) -> Self { - - -// Self { -// vertex_buffer, -// index_buffer, -// num_indices, -// } -// } -// } - diff --git a/src/shader_stage.rs b/src/shader_stage.rs index 2a35d18a..cdc052bc 100644 --- a/src/shader_stage.rs +++ b/src/shader_stage.rs @@ -1,5 +1,8 @@ -pub fn compile_from_glsl(device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> std::io::Result { - let source = std::fs::read_to_string(path)?; +use std::fs; +use std::io; + +pub fn compile_from_glsl(device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> io::Result { + let source = fs::read_to_string(path)?; let spirv = match glsl_to_spirv::compile(&source[..], shader_type) { Ok(spirv_output) => spirv_output, Err(message) => { diff --git a/src/texture.rs b/src/texture.rs index e807f5ae..f574b7bb 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -1,3 +1,4 @@ +use std::fs; use image::GenericImageView; pub struct Texture { @@ -9,7 +10,7 @@ pub struct Texture { impl Texture { pub fn from_filepath(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str) -> Result { // Read the raw bytes from the specified file - let bytes = std::fs::read(path)?; + let bytes = fs::read(path)?; // Construct and return a Texture from the bytes Texture::from_bytes(device, queue, &bytes[..]) diff --git a/src/window_events.rs b/src/window_events.rs new file mode 100644 index 00000000..800ebb79 --- /dev/null +++ b/src/window_events.rs @@ -0,0 +1,49 @@ +use super::application::Application; +use winit::event::*; +use winit::event_loop::ControlFlow; + +pub fn window_event(application: &mut Application, control_flow: &mut ControlFlow, event: &WindowEvent) { + match event { + WindowEvent::Resized(physical_size) => resize(application, *physical_size), + WindowEvent::Moved(_) => (), + WindowEvent::CloseRequested => quit(control_flow), + WindowEvent::Destroyed => (), + WindowEvent::DroppedFile(_) => (), + WindowEvent::HoveredFile(_) => (), + WindowEvent::HoveredFileCancelled => (), + WindowEvent::ReceivedCharacter(_) => (), + WindowEvent::Focused(_) => (), + WindowEvent::KeyboardInput { input, .. } => keyboard_event(application, control_flow, input), + WindowEvent::CursorMoved { .. } => (), + WindowEvent::CursorEntered { .. } => (), + WindowEvent::CursorLeft { .. } => (), + WindowEvent::MouseWheel { .. } => (), + WindowEvent::MouseInput { .. } => (), + WindowEvent::TouchpadPressure { .. } => (), + WindowEvent::AxisMotion { .. } => (), + WindowEvent::Touch(_) => (), + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => resize(application, **new_inner_size), + WindowEvent::ThemeChanged(_) => (), + } +} + +fn keyboard_event(application: &mut Application, control_flow: &mut ControlFlow, input: &KeyboardInput) { + match input { + KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => quit(control_flow), + KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => application.example(), + _ => *control_flow = ControlFlow::Wait, + } +} + +fn quit(control_flow: &mut ControlFlow) { + *control_flow = ControlFlow::Exit; +} + +fn resize(application: &mut Application, new_size: winit::dpi::PhysicalSize) { + application.swap_chain_descriptor.width = new_size.width; + application.swap_chain_descriptor.height = new_size.height; + + application.swap_chain = application.device.create_swap_chain(&application.surface, &application.swap_chain_descriptor); + + // TODO: Mark root of GUI as dirty to force redraw of everything +}