diff --git a/rust-engine.iml b/rust-engine.iml index 7fe828a..d5ac164 100644 --- a/rust-engine.iml +++ b/rust-engine.iml @@ -7,6 +7,9 @@ + + + diff --git a/src/main.rs b/src/main.rs index 16c1436..6e59c79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use crate::vulkan::{Vertex, GameData}; -use winit::{Event, WindowEvent}; +use winit::{Event, WindowEvent, ElementState}; use std::time::SystemTime; use std::iter::FromIterator; @@ -7,7 +7,7 @@ mod vulkan; impl GameData<'_> { /// Returns true if event should be ignored by the vulkan handler - fn on_window_event(self: &Self, event: &Event) -> bool { + fn on_window_event(self: &mut Self, event: &Event) -> bool { match event { Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => { let mods = String::from_iter( @@ -20,6 +20,10 @@ impl GameData<'_> { } else { println!("Keyboard {:?} input {:?} {:?}", device_id, input.state, input.scancode) } + + if input.state == ElementState::Released && input.modifiers.ctrl && input.scancode == 19 { + self.recreate_pipeline = true; + } } _ => {} } @@ -52,7 +56,8 @@ fn main() { Vertex { position: [0.9, 0., 0.] }, ], push_constants: &mut pc, - start_time: SystemTime::now() + start_time: SystemTime::now(), + recreate_pipeline: false }; vulkan::init(data); diff --git a/src/vulkan.rs b/src/vulkan.rs index 0bea9ef..815cca4 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -50,6 +50,7 @@ pub struct GameData<'a> { pub mesh_vertices: Vec, pub line_vertices: Vec, pub push_constants: &'a mut PushConstants, + pub recreate_pipeline: bool, } pub fn init(mut game: GameData) { @@ -92,7 +93,7 @@ pub fn init(mut game: GameData) { error: true, warning: true, performance_warning: true, - information: true, + information: false, debug: true, }; @@ -237,13 +238,13 @@ pub fn init(mut game: GameData) { let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); - let pipeline = create_pipeline(device.clone(), sub_pass.clone()); + let mut pipeline = create_pipeline(device.clone(), sub_pass.clone()).unwrap(); let line_shader_vertex_entry; let line_shader_fragment_entry; let line_shader_module_vertex; let line_shader_module_fragment; - let (line_shader, line_shader_data) = read_shader("shaders/line.vert", "shaders/line.frag"); + let (line_shader, line_shader_data) = read_shader("shaders/line.vert", "shaders/line.frag").unwrap(); unsafe { line_shader_module_vertex = ShaderModule::from_words(device.clone(), &line_shader.vertex).expect("Failed to load"); line_shader_vertex_entry = line_shader_module_vertex.graphics_entry_point( @@ -281,7 +282,7 @@ pub fn init(mut game: GameData) { // Since we need to draw to multiple images, we are going to create a different framebuffer for // each image. let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state); - + // In some situations, the swapchain will become invalid by it This includes for example // when the window is resized (as the images of the swapchain will no longer match the // window's) or, on Android, when the application went to the background and goes back to the @@ -335,6 +336,16 @@ pub fn init(mut game: GameData) { recreate_swapchain = false; } + if game.recreate_pipeline { + if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone()) { + pipeline = pipeline_ok; + println!("Updated pipeline."); + } else { + println!("Failed to update pipeline."); + } + game.recreate_pipeline = false; + } + // Before we can draw on the output, we have to *acquire* an image from the swapchain. If // no image is available (which happens if you submit draw commands too quickly), then the // function will block. @@ -464,55 +475,59 @@ fn window_size_dependent_setup( }).collect::>() } -fn create_pipeline(device: Arc, sub_pass: Subpass>) -> Arc, Box, Arc>> { - let mesh_shader_vertex_entry; - let mesh_shader_fragment_entry; - let mesh_shader_module_vertex; - let mesh_shader_module_fragment; - let (mesh_shader, mesh_shader_data) = read_shader("shaders/triangle.vert", "shaders/triangle.frag"); - unsafe { - mesh_shader_module_vertex = ShaderModule::from_words(device.clone(), &mesh_shader.vertex).expect("Failed to load"); - mesh_shader_vertex_entry = mesh_shader_module_vertex.graphics_entry_point( - CStr::from_bytes_with_nul_unchecked(b"main\0"), - mesh_shader_data.vert_input, - mesh_shader_data.vert_output, - mesh_shader_data.vert_layout, - GraphicsShaderType::Vertex); - mesh_shader_module_fragment = ShaderModule::from_words(device.clone(), &mesh_shader.fragment).expect("Failed to load"); - mesh_shader_fragment_entry = mesh_shader_module_fragment.graphics_entry_point( - CStr::from_bytes_with_nul_unchecked(b"main\0"), - mesh_shader_data.frag_input, - mesh_shader_data.frag_output, - mesh_shader_data.frag_layout, - GraphicsShaderType::Fragment); - }; +fn create_pipeline(device: Arc, sub_pass: Subpass>) -> Option, Box, Arc>>> { + if let Some((mesh_shader, mesh_shader_data)) = read_shader("shaders/triangle.vert", "shaders/triangle.frag") { + let mesh_shader_vertex_entry; + let mesh_shader_fragment_entry; + let mesh_shader_module_vertex; + let mesh_shader_module_fragment; - // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL - // program, but much more specific. - let pipeline = Arc::new(GraphicsPipeline::start() - // We need to indicate the layout of the vertices. - .vertex_input_single_buffer::() - // A Vulkan shader can in theory contain multiple entry points, so we have to specify - // which one. The `main` word of `main_entry_point` actually corresponds to the name of - // the entry point. - .vertex_shader(mesh_shader_vertex_entry.clone(), ()) - // The content of the vertex buffer describes a list of triangles. - .triangle_list() - // Use a resizable viewport set to draw over the entire window - .viewports_dynamic_scissors_irrelevant(1) - // See `vertex_shader`. - .fragment_shader(mesh_shader_fragment_entry.clone(), ()) - // We have to indicate which subpass of which render pass this pipeline is going to be used - // in. The pipeline will only be usable from this particular subpass. - .render_pass(sub_pass.clone()) - // Now that our builder is filled, we call `build()` to obtain an actual pipeline. - .build(device.clone()) - .unwrap()); + unsafe { + mesh_shader_module_vertex = ShaderModule::from_words(device.clone(), &mesh_shader.vertex).expect("Failed to load"); + mesh_shader_vertex_entry = mesh_shader_module_vertex.graphics_entry_point( + CStr::from_bytes_with_nul_unchecked(b"main\0"), + mesh_shader_data.vert_input, + mesh_shader_data.vert_output, + mesh_shader_data.vert_layout, + GraphicsShaderType::Vertex); + mesh_shader_module_fragment = ShaderModule::from_words(device.clone(), &mesh_shader.fragment).expect("Failed to load"); + mesh_shader_fragment_entry = mesh_shader_module_fragment.graphics_entry_point( + CStr::from_bytes_with_nul_unchecked(b"main\0"), + mesh_shader_data.frag_input, + mesh_shader_data.frag_output, + mesh_shader_data.frag_layout, + GraphicsShaderType::Fragment); + }; - return pipeline; + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL + // program, but much more specific. + let pipeline = Arc::new(GraphicsPipeline::start() + // We need to indicate the layout of the vertices. + .vertex_input_single_buffer::() + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. The `main` word of `main_entry_point` actually corresponds to the name of + // the entry point. + .vertex_shader(mesh_shader_vertex_entry.clone(), ()) + // The content of the vertex buffer describes a list of triangles. + .triangle_list() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + // See `vertex_shader`. + .fragment_shader(mesh_shader_fragment_entry.clone(), ()) + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(sub_pass.clone()) + // Now that our builder is filled, we call `build()` to obtain an actual pipeline. + .build(device.clone()) + .unwrap()); + + return Some(pipeline); + } else { + return None; + } } -fn read_shader(vert_path_relative: &str, frag_path_relative: &str) -> (CompiledShaders, Entry) { +fn read_shader(vert_path_relative: &str, frag_path_relative: &str) -> Option<(CompiledShaders, Entry)> { let project_root = std::env::current_dir().expect("failed to get root directory"); let mut vert_path = project_root.clone(); @@ -525,11 +540,15 @@ fn read_shader(vert_path_relative: &str, frag_path_relative: &str) -> (CompiledS match shader_result { Ok(shader) => { let shader_data = shade_runner::parse(&shader).expect("Failed to parse"); - return (shader, shader_data); + return Some((shader, shader_data)); } Err(shade_runner::error::Error::Compile(shade_runner::error::CompileError::Compile(shaderc::Error::CompilationError(line, error)))) => { - panic!("Shader line {}: {:?}", line, error); + println!("Shader line {}: {:?}", line, error); + return None; + } + Err(error) => { + println!("Shader compilation error: {:?}", error); + return None; } - Err(error) => panic!("Shader compilation error: {:?}", error) } }