From 7d50d00ab66b2895252daf281afc361e277ccdef Mon Sep 17 00:00:00 2001 From: Till Date: Mon, 22 Jul 2019 18:12:06 +0200 Subject: [PATCH] stuff --- Cargo.toml | 4 +- shaders/line.frag | 7 ++ shaders/line.vert | 7 ++ shaders/triangle.frag | 2 +- shaders/triangle.vert | 8 +- src/main.rs | 44 +++++++- src/vulkan.rs | 244 ++++++++++++++++++++++++++---------------- 7 files changed, 218 insertions(+), 98 deletions(-) create mode 100644 shaders/line.frag create mode 100644 shaders/line.vert diff --git a/Cargo.toml b/Cargo.toml index 7a894cf..527b881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,9 @@ edition = "2018" vulkano-shaders = "0.13" vulkano = "0.13" vulkano-win = "0.13" +shade_runner = "0.1.2" +shaderc = "0.5.0" cgmath = "0.17" image = "0.21" winit = "0.19" -time = "0.1.37" \ No newline at end of file +time = "0.1.37" diff --git a/shaders/line.frag b/shaders/line.frag new file mode 100644 index 0000000..f40b64b --- /dev/null +++ b/shaders/line.frag @@ -0,0 +1,7 @@ +#version 450 + +layout(location = 0) out vec4 f_color; + +void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/shaders/line.vert b/shaders/line.vert new file mode 100644 index 0000000..0d82f83 --- /dev/null +++ b/shaders/line.vert @@ -0,0 +1,7 @@ +#version 450 + +layout(location = 0) in vec3 position; + +void main() { + gl_Position = vec4(position, 1.0); +} \ No newline at end of file diff --git a/shaders/triangle.frag b/shaders/triangle.frag index f40b64b..68ba49e 100644 --- a/shaders/triangle.frag +++ b/shaders/triangle.frag @@ -3,5 +3,5 @@ layout(location = 0) out vec4 f_color; void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); + f_color = vec4(0.0, 1.0, 0.0, 1.0); } \ No newline at end of file diff --git a/shaders/triangle.vert b/shaders/triangle.vert index eca2400..3671517 100644 --- a/shaders/triangle.vert +++ b/shaders/triangle.vert @@ -1,7 +1,11 @@ #version 450 -layout(location = 0) in vec2 position; +layout(location = 0) in vec3 position; + +layout(push_constant) uniform push_constants { + float time; +} push; void main() { - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ef0c8f6..6ec0bf8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,47 @@ +use crate::vulkan::{Vertex, GameData}; +use winit::{DeviceId, KeyboardInput, ElementState, VirtualKeyCode}; +use std::time::SystemTime; + mod vulkan; +impl GameData<'_> { + fn on_init(self: &Self) { + + } + + fn on_keyboard_event(self: &Self, _: DeviceId, input: KeyboardInput) { + if input.state == ElementState::Pressed && input.virtual_keycode == Some(VirtualKeyCode::F) { + println!("doot"); + } + } + fn update_push_constants(self: &mut Self) { + self.push_constants.time = self.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0; + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PushConstants { + pub time: f32 +} + fn main() { - vulkan::init(); + let mut pc = PushConstants { + time: 0.0 + }; + + let data = GameData { + mesh_vertices: vec![ + Vertex { position: [0.1, 0.2, 0.2] }, + Vertex { position: [0.2, 0.4, 0.2] }, + Vertex { position: [0.2, 0.2, 0.3] } + ], + line_vertices: vec![ + Vertex { position: [-1., 0., 0.] }, + Vertex { position: [1., 0., 0.] }, + ], + push_constants: &mut pc, + start_time: SystemTime::now() + }; + + vulkan::init(data); } \ No newline at end of file diff --git a/src/vulkan.rs b/src/vulkan.rs index a59ac5b..0f406c2 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -4,7 +4,8 @@ use vulkano::device::{Device, DeviceExtensions}; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract}; use vulkano::image::SwapchainImage; use vulkano::instance::{Instance, PhysicalDevice}; -use vulkano::pipeline::GraphicsPipeline; +use vulkano::pipeline::{GraphicsPipeline}; +use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule}; use vulkano::pipeline::viewport::Viewport; use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError}; use vulkano::swapchain; @@ -16,39 +17,49 @@ use vulkano_win::VkSurfaceBuild; use winit::{EventsLoop, Window, WindowBuilder, Event, WindowEvent}; use std::sync::Arc; +use std::time::SystemTime; +use std::path::PathBuf; +use std::ffi::CStr; -mod vs { - vulkano_shaders::shader!{ - ty: "vertex", - path: "shaders/triangle.vert", - } +use shade_runner; +use shade_runner::{CompiledShaders, Entry}; + +use shaderc; +use vulkano::pipeline::vertex::SingleBufferDefinition; +use vulkano::descriptor::PipelineLayoutAbstract; +use crate::PushConstants; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +#[derive(Default, Debug, Clone)] +pub struct Vertex { + pub position: [f32; 3], +} +vulkano::impl_vertex!(Vertex, position); + +pub struct GameData<'a> { + pub start_time: SystemTime, + pub mesh_vertices: Vec, + pub line_vertices: Vec, + pub push_constants: &'a mut PushConstants, } -mod fs { - vulkano_shaders::shader!{ - ty: "fragment", - path: "shaders/triangle.frag", - } -} - -mod line_vs { - vulkano_shaders::shader!{ - ty: "vertex", - path: "shaders/triangle.vert", - } -} - -mod line_fs { - vulkano_shaders::shader!{ - ty: "fragment", - path: "shaders/triangle.frag", - } -} - -pub fn init() { +pub fn init(mut game: GameData) { let instance = { let extensions = vulkano_win::required_extensions(); - Instance::new(None, &extensions, None).unwrap() + + if ENABLE_VALIDATION_LAYERS { + Instance::new(None, &extensions, VALIDATION_LAYERS.iter().cloned()).expect("failed to create Vulkan instance") + } else { + Instance::new(None, &extensions, None).expect("failed to create Vulkan instance") + } }; let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); @@ -133,7 +144,7 @@ pub fn init() { [dimensions.0, dimensions.1] } else { // The window no longer exists so exit the application. - return; + panic!("idk"); }; // Please take a look at the docs for the meaning of the parameters we didn't mention. @@ -143,24 +154,8 @@ pub fn init() { }; - #[derive(Default, Debug, Clone)] - struct Vertex { position: [f32; 2] } - vulkano::impl_vertex!(Vertex, position); - // We now create a buffer that will store the shape of our triangle. - let vertex_buffer = { - CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [ - Vertex { position: [-0.5, -0.25] }, - Vertex { position: [0.0, 0.5] }, - Vertex { position: [0.25, -0.1] } - ].iter().cloned()).unwrap() - }; - - let line_vertex_buffer = { - CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [ - Vertex { position: [-0.4, -0.3] }, - Vertex { position: [0.01, 0.55] }, - ].iter().cloned()).unwrap() - }; + let mesh_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.mesh_vertices.iter().cloned()).unwrap(); + let line_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.mesh_vertices.iter().cloned()).unwrap(); // The next step is to create the shaders. // @@ -206,44 +201,38 @@ pub fn init() { } ).unwrap()); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); - - let line_vs = line_vs::Shader::load(device.clone()).unwrap(); - let line_fs = line_fs::Shader::load(device.clone()).unwrap(); - let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); - // 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. - // The type `SingleBufferDefinition` actually contains a template parameter corresponding - // to the type of each vertex. But in this code it is automatically inferred. - .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(vs.main_entry_point(), ()) - // 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(fs.main_entry_point(), ()) - // 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()); + let pipeline = create_pipeline(device.clone(), sub_pass.clone()); + + 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"); + 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( + CStr::from_bytes_with_nul_unchecked(b"main\0"), + line_shader_data.vert_input, + line_shader_data.vert_output, + line_shader_data.vert_layout, + GraphicsShaderType::Vertex); + line_shader_module_fragment = ShaderModule::from_words(device.clone(), &line_shader.fragment).expect("Failed to load"); + line_shader_fragment_entry = line_shader_module_fragment.graphics_entry_point( + CStr::from_bytes_with_nul_unchecked(b"main\0"), + line_shader_data.frag_input, + line_shader_data.frag_output, + line_shader_data.frag_layout, + GraphicsShaderType::Fragment); + }; let line_pipeline = Arc::new(GraphicsPipeline::start() - .vertex_input_single_buffer() - .vertex_shader(line_vs.main_entry_point(), ()) + .vertex_input_single_buffer::() + .vertex_shader(line_shader_vertex_entry.clone(), ()) .line_list() .viewports_dynamic_scissors_irrelevant(1) - .fragment_shader(line_fs.main_entry_point(), ()) + .fragment_shader(line_shader_fragment_entry.clone(), ()) .render_pass(sub_pass.clone()) .build(device.clone()) .unwrap()); @@ -258,10 +247,8 @@ pub fn init() { // 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); - - // Initialization is finally finished! - - // In some situations, the swapchain will become invalid by itself. This includes for example + + // 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 // foreground. @@ -333,6 +320,8 @@ pub fn init() { // Specify the color to clear the framebuffer with i.e. blue let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into()); + game.update_push_constants(); + // In order to draw, we have to build a *command buffer*. The command buffer object holds // the list of commands that are going to be executed. // @@ -350,23 +339,19 @@ pub fn init() { // The third parameter builds the list of values to clear the attachments with. The API // is similar to the list of attachments when building the framebuffers, except that // only the attachments that use `load: Clear` appear in the list. - .begin_render_pass(framebuffers[image_num].clone(), false, clear_values) - .unwrap() + .begin_render_pass(framebuffers[image_num].clone(), false, clear_values).unwrap() // We are now inside the first subpass of the render pass. We add a draw command. // // The last two parameters contain the list of resources to pass to the shaders. // Since we used an `EmptyPipeline` object, the objects have to be `()`. - .draw(pipeline.clone(), &dynamic_state, vertex_buffer.clone(), (), ()) - .unwrap() - .draw(line_pipeline.clone(), &dynamic_state, line_vertex_buffer.clone(), (), ()) - .unwrap() + .draw(pipeline.clone(), &dynamic_state, mesh_vertex_buffer.clone(), (), game.push_constants.clone()).unwrap() + .draw(line_pipeline.clone(), &dynamic_state, line_vertex_buffer.clone(), (), ()).unwrap() // We leave the render pass by calling `draw_end`. Note that if we had multiple // subpasses we could have called `next_inline` (or `next_secondary`) to jump to the // next subpass. - .end_render_pass() - .unwrap() + .end_render_pass().unwrap() // Finish building the command buffer by calling `build`. .build().unwrap(); @@ -412,7 +397,10 @@ pub fn init() { match ev { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true, Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true, - _ => () + Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => { + game.on_keyboard_event(device_id, input); + } + _ => {} } }); if done { return; } @@ -442,3 +430,73 @@ fn window_size_dependent_setup( ) as Arc }).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); + }; + + // 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 pipeline; +} + +fn read_shader(vert_path_relative: &str, frag_path_relative: &str) -> (CompiledShaders, Entry) { + let project_root = std::env::current_dir().expect("failed to get root directory"); + + let mut vert_path = project_root.clone(); + vert_path.push(PathBuf::from(vert_path_relative)); + + let mut frag_path = project_root.clone(); + frag_path.push(PathBuf::from(frag_path_relative)); + + let shader_result = shade_runner::load(vert_path, frag_path); + match shader_result { + Ok(shader) => { + let shader_data = shade_runner::parse(&shader).expect("Failed to parse"); + return (shader, shader_data); + } + Err(shade_runner::error::Error::Compile(shade_runner::error::CompileError::Compile(shaderc::Error::CompilationError(line, error)))) => { + panic!("Shader line {}: {:?}", line, error); + } + Err(error) => panic!("Shader compilation error: {:?}", error) + } +} \ No newline at end of file