From 767efab3d41a8698c3db43fe3eb6f63dbf2db1b0 Mon Sep 17 00:00:00 2001 From: Till Date: Thu, 25 Jul 2019 16:38:42 +0200 Subject: [PATCH] asdf --- Cargo.toml | 2 - shaders/triangle.vert | 5 +- src/main.rs | 46 +++++++--- src/vulkan.rs | 203 +++++++++++++++--------------------------- 4 files changed, 111 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 527b881..20f7c40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,4 @@ 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" diff --git a/shaders/triangle.vert b/shaders/triangle.vert index eebfee0..c6849e2 100644 --- a/shaders/triangle.vert +++ b/shaders/triangle.vert @@ -6,9 +6,12 @@ layout(location = 0) out vec3 pos; layout(push_constant) uniform push_constants { float time; + mat4 model; + mat4 view; + mat4 projection; } push; void main() { - gl_Position = vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0); + gl_Position = push.projection * push.view * push.model * vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0); pos = gl_Position.xyz; } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6e59c79..b344805 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,28 +2,36 @@ use crate::vulkan::{Vertex, GameData}; use winit::{Event, WindowEvent, ElementState}; use std::time::SystemTime; use std::iter::FromIterator; +use cgmath::{Matrix4, SquareMatrix, Rad, Point3, Vector3}; mod vulkan; +const PRINT_KEYBOARD_INPUT: bool = false; + impl GameData<'_> { /// Returns true if event should be ignored by the vulkan handler 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( - vec!["shift", "ctrl", "alt", "logo"].iter() - .zip(vec![input.modifiers.shift, input.modifiers.ctrl, input.modifiers.alt, input.modifiers.logo]) - .filter(|(&_name, state)| *state) - .map(|(&name, _state)| name)); - if mods.len() > 0 { - println!("Keyboard {:?} input {:?} {:?} + {:?}", device_id, input.state, &mods, input.scancode) - } else { - println!("Keyboard {:?} input {:?} {:?}", device_id, input.state, input.scancode) + if PRINT_KEYBOARD_INPUT { + let mods = String::from_iter( + vec!["shift", "ctrl", "alt", "logo"].iter() + .zip(vec![input.modifiers.shift, input.modifiers.ctrl, input.modifiers.alt, input.modifiers.logo]) + .filter(|(&_name, state)| *state) + .map(|(&name, _state)| name)); + if mods.len() > 0 { + println!("Keyboard {:?} input {:?} {:?} + {:?}", device_id, input.state, &mods, input.scancode) + } 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; } + if input.state == ElementState::Released && input.scancode == 1 { + self.shutdown = true; + } } _ => {} } @@ -32,17 +40,29 @@ impl GameData<'_> { fn update_push_constants(self: &mut Self) { self.push_constants.time = self.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0; + self.push_constants.model = Matrix4::identity(); + self.push_constants.view = Matrix4::look_at(Point3::new(f32::sin(self.push_constants.time), 0.0, f32::cos(self.push_constants.time)), Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0)); + self.push_constants.projection = cgmath::perspective(Rad(std::f32::consts::FRAC_PI_2), self.aspect_ratio, 0.01, 100.0); } } #[derive(Debug, Clone, Copy)] pub struct PushConstants { - pub time: f32 + pub time: f32, + pub model: Matrix4, + pub view: Matrix4, + pub projection: Matrix4, } fn main() { + let mut whatever = String::new(); + std::io::stdin().read_line(&mut whatever).unwrap(); + let mut pc = PushConstants { - time: 0.0 + time: 0.0, + model: Matrix4::identity(), + view: Matrix4::identity(), + projection: Matrix4::identity() }; let data = GameData { @@ -57,7 +77,9 @@ fn main() { ], push_constants: &mut pc, start_time: SystemTime::now(), - recreate_pipeline: false + recreate_pipeline: false, + aspect_ratio: 1.0, + shutdown: false, }; vulkan::init(data); diff --git a/src/vulkan.rs b/src/vulkan.rs index 815cca4..0d99ae5 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -2,7 +2,7 @@ use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; use vulkano::device::{Device, DeviceExtensions}; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract}; -use vulkano::image::SwapchainImage; +use vulkano::image::{SwapchainImage, AttachmentImage}; use vulkano::instance::{Instance, PhysicalDevice, ApplicationInfo, Version, InstanceExtensions}; use vulkano::pipeline::{GraphicsPipeline}; use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule}; @@ -13,6 +13,7 @@ use vulkano::sync::{GpuFuture, FlushError}; use vulkano::sync; use vulkano::pipeline::vertex::SingleBufferDefinition; use vulkano::descriptor::PipelineLayoutAbstract; +use vulkano::format::Format; use vulkano_win::VkSurfaceBuild; @@ -51,6 +52,8 @@ pub struct GameData<'a> { pub line_vertices: Vec, pub push_constants: &'a mut PushConstants, pub recreate_pipeline: bool, + pub aspect_ratio: f32, + pub shutdown: bool, } pub fn init(mut game: GameData) { @@ -130,44 +133,15 @@ pub fn init(mut game: GameData) { // // We have to choose which queues to use early on, because we will need this info very soon. let queue_family = physical.queue_families().find(|&q| { - // We take the first queue that supports drawing to our window. q.supports_graphics() && surface.is_supported(q).unwrap_or(false) }).unwrap(); - // Now initializing the device. This is probably the most important object of Vulkan. - // - // We have to pass five parameters when creating a device: - // - // - Which physical device to connect to. - // - // - A list of optional features and extensions that our program needs to work correctly. - // Some parts of the Vulkan specs are optional and must be enabled manually at device - // creation. In this example the only thing we are going to need is the `khr_swapchain` - // extension that allows us to draw to a window. - // - // - A list of layers to enable. This is very niche, and you will usually pass `None`. - // - // - The list of queues that we are going to use. The exact parameter is an iterator whose - // items are `(Queue, f32)` where the floating-point represents the priority of the queue - // between 0.0 and 1.0. The priority of the queue is a hint to the implementation about how - // much it should prioritize queues between one another. - // - // The list of created queues is returned by the function alongside with the device. let device_ext = DeviceExtensions { khr_swapchain: true, .. DeviceExtensions::none() }; let (device, mut queues) = Device::new(physical, physical.supported_features(), &device_ext, [(queue_family, 0.5)].iter().cloned()).unwrap(); - - // Since we can request multiple queues, the `queues` variable is in fact an iterator. In this - // example we use only one queue, so we just retrieve the first and only element of the - // iterator and throw it away. let queue = queues.next().unwrap(); - // Before we can draw on the surface, we have to create what is called a swapchain. Creating - // a swapchain allocates the color buffers that will contain the image that will ultimately - // be visible on the screen. These images are returned alongside with the swapchain. let (mut swapchain, images) = { - // Querying the capabilities of the surface. When we create the swapchain we can only - // pass values that are allowed by the capabilities. let caps = surface.capabilities(physical).unwrap(); let usage = caps.supported_usage_flags; @@ -202,7 +176,6 @@ pub fn init(mut game: GameData) { Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, initial_dimensions, 1, usage, &queue, SurfaceTransform::Identity, alpha, PresentMode::Fifo, true, None).unwrap() - }; let mesh_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.mesh_vertices.iter().cloned()).unwrap(); @@ -211,66 +184,29 @@ pub fn init(mut game: GameData) { let render_pass = Arc::new(vulkano::single_pass_renderpass!( device.clone(), attachments: { - // `color` is a custom name we give to the first and only attachment. color: { - // `load: Clear` means that we ask the GPU to clear the content of this - // attachment at the start of the drawing. load: Clear, - // `store: Store` means that we ask the GPU to store the output of the draw - // in the actual image. We could also ask it to discard the result. store: Store, - // `format: ` indicates the type of the format of the image. This has to - // be one of the types of the `vulkano::format` module (or alternatively one - // of your structs that implements the `FormatDesc` trait). Here we use the - // same format as the swapchain. format: swapchain.format(), - // TODO: + samples: 1, + }, + depth: { + load: Clear, + store: DontCare, + format: Format::D16Unorm, samples: 1, } }, pass: { - // We use the attachment named `color` as the one and only color attachment. color: [color], - // No depth-stencil attachment is indicated with empty brackets. - depth_stencil: {} + depth_stencil: {depth} } ).unwrap()); let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); - 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").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( - 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_shader_vertex_entry.clone(), ()) - .line_list() - .viewports_dynamic_scissors_irrelevant(1) - .fragment_shader(line_shader_fragment_entry.clone(), ()) - .render_pass(sub_pass.clone()) - .build(device.clone()) - .unwrap()); + let mut pipeline = create_pipeline(device.clone(), sub_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false).unwrap(); + let line_pipeline = create_pipeline(device.clone(), sub_pass.clone(), "shaders/line.vert", "shaders/line.frag", true).unwrap(); // Dynamic viewports allow us to recreate just the viewport when the window is resized // Otherwise we would have to recreate the whole pipeline. @@ -281,7 +217,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); + let mut framebuffers = window_size_dependent_setup(device.clone(), &images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio); // 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 @@ -331,13 +267,13 @@ pub fn init(mut game: GameData) { swapchain = new_swapchain; // Because framebuffers contains an Arc on the old swapchain, we need to // recreate framebuffers as well. - framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state); + framebuffers = window_size_dependent_setup(device.clone(), &new_images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio); recreate_swapchain = false; } if game.recreate_pipeline { - if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone()) { + if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false) { pipeline = pipeline_ok; println!("Updated pipeline."); } else { @@ -363,7 +299,7 @@ pub fn init(mut game: GameData) { }; // Specify the color to clear the framebuffer with i.e. blue - let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into()); + let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into(), 1f32.into()); game.update_push_constants(); @@ -437,27 +373,23 @@ pub fn init(mut game: GameData) { // Handling the window events in order to close the program when the user wants to close // it. - let mut done = false; events_loop.poll_events(|ev| { if !game.on_window_event(&ev) { match ev { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true, + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => game.shutdown = true, Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true, _ => {} } } }); - if done { return; } + if game.shutdown { return; } } } /// This method is called once during initialization, then again whenever the window is resized -fn window_size_dependent_setup( - images: &[Arc>], - render_pass: Arc, - dynamic_state: &mut DynamicState -) -> Vec> { +fn window_size_dependent_setup(device: Arc, images: &[Arc>], render_pass: Arc, dynamic_state: &mut DynamicState, aspect_ratio: &mut f32) -> Vec> { let dimensions = images[0].dimensions(); + *aspect_ratio = dimensions[0] as f32 / dimensions[1] as f32; let viewport = Viewport { origin: [0.0, 0.0], @@ -466,60 +398,71 @@ fn window_size_dependent_setup( }; dynamic_state.viewports = Some(vec!(viewport)); + let depth_buffer = AttachmentImage::transient(device.clone(), dimensions, Format::D16Unorm).unwrap(); + images.iter().map(|image| { - Arc::new( - Framebuffer::start(render_pass.clone()) - .add(image.clone()).unwrap() - .build().unwrap() + Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .add(depth_buffer.clone()).unwrap() + .build().unwrap() ) as Arc }).collect::>() } -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; +fn create_pipeline(device: Arc, sub_pass: Subpass>, vertex_shader_path: &str, fragment_shader_path: &str, is_line: bool) -> Option, Box, Arc>>> { + if let Some((shader, shader_data)) = read_shader(vertex_shader_path, fragment_shader_path) { + let vertex_shader_entry; + let fragment_shader_entry; + let vertex_shader_module; + let fragment_shader_module; 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( + vertex_shader_module = ShaderModule::from_words(device.clone(), &shader.vertex).expect("Failed to load vertex shader."); + vertex_shader_entry = vertex_shader_module.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, + shader_data.vert_input, + shader_data.vert_output, + 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( + fragment_shader_module = ShaderModule::from_words(device.clone(), &shader.fragment).expect("Failed to load fragment shader."); + fragment_shader_entry = fragment_shader_module.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, + shader_data.frag_input, + shader_data.frag_output, + 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()); + let pipeline; + if is_line { + pipeline = Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vertex_shader_entry.clone(), ()) + .line_list() + .viewports_dynamic_scissors_irrelevant(1) + .fragment_shader(fragment_shader_entry.clone(), ()) + .render_pass(sub_pass.clone()) + .build(device.clone()) + .unwrap()); + } else { + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL + // program, but much more specific. + pipeline = Arc::new(GraphicsPipeline::start() + .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(vertex_shader_entry.clone(), ()) + .triangle_list() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + .fragment_shader(fragment_shader_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()) + .build(device.clone()) + .unwrap()); + } return Some(pipeline); } else {