diff --git a/engine/BUILD b/engine/BUILD index 35b1f09..9a5797c 100644 --- a/engine/BUILD +++ b/engine/BUILD @@ -6,9 +6,11 @@ rust_binary( srcs = [ "src/main.rs", "src/render/mod.rs", + "src/render/renderable.rs", "src/render/vulkan/mod.rs", "src/render/vulkan/binding.rs", "src/render/vulkan/data.rs", + "src/render/vulkan/pipeline.rs", "src/render/vulkan/qfi.rs", "src/render/vulkan/shaders.rs", "src/render/vulkan/swapchains.rs", diff --git a/engine/src/main.rs b/engine/src/main.rs index e3c030c..4326502 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,12 +1,35 @@ use log; use env_logger; +use std::sync::Arc; + +use cgmath as cgm; mod render; mod util; +use render::vulkan::data; +use render::renderable::Renderable; + fn main() { env_logger::init(); log::info!("Starting..."); + + let elapsed = 0.0; + let transform = cgm::Matrix4::from_angle_z(cgm::Rad::from(cgm::Deg(elapsed as f32 * 0.180))); + let vertices = Arc::new(vec![ + data::Vertex::new([-0.5, -0.5, 0.0], [1.0, 0.0, 0.0]), + data::Vertex::new([0.5, -0.5, 0.0], [0.0, 1.0, 0.0]), + data::Vertex::new([0.5, 0.5, 0.0], [0.0, 0.0, 1.0]), + data::Vertex::new([-0.5, 0.5, 0.0], [1.0, 1.0, 1.0]) + ]); + let indices = Arc::new(vec![ + 0, 1, 2, 2, 3, 0, + ]); + let demo = render::renderable::Mesh { + transform, vertices, indices + }; + let mut renderer = render::Renderer::initialize(); + renderer.set_render_data(vec![demo.data().unwrap()]); renderer.main_loop(); } diff --git a/engine/src/render/mod.rs b/engine/src/render/mod.rs index 5c3948a..7e7bf31 100644 --- a/engine/src/render/mod.rs +++ b/engine/src/render/mod.rs @@ -12,7 +12,8 @@ use vulkano_win::VkSurfaceBuild; use vulkano::instance as vi; use vulkano::swapchain as vs; -mod vulkan; +pub mod vulkan; +pub mod renderable; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -20,6 +21,8 @@ const HEIGHT: u32 = 600; pub struct Renderer { instance: vulkan::Instance, events_loop: EventsLoop, + + render_data: Vec, } impl Renderer { @@ -31,9 +34,14 @@ impl Renderer { Self { instance, events_loop, + render_data: vec![], } } + pub fn set_render_data(&mut self, render_data: Vec) { + self.render_data = render_data; + } + fn init_window(instance: Arc) -> (EventsLoop, Arc>) { let events_loop = EventsLoop::new(); let surface = WindowBuilder::new() @@ -45,7 +53,7 @@ impl Renderer { } fn draw_frame(&mut self) { - self.instance.flip(); + self.instance.flip(self.render_data.clone()); } pub fn main_loop(&mut self) { diff --git a/engine/src/render/renderable.rs b/engine/src/render/renderable.rs new file mode 100644 index 0000000..80957b5 --- /dev/null +++ b/engine/src/render/renderable.rs @@ -0,0 +1,34 @@ +use std::sync::Arc; + +use cgmath as cgm; + +use crate::render::vulkan::data; + +pub trait Renderable { + fn data(&self) -> Option { + None + } +} + +#[derive(Clone)] +pub struct Data { + pub vertices: Arc>, + pub indices: Arc>, + pub transform: cgm::Matrix4, +} + +pub struct Mesh { + pub vertices: Arc>, + pub indices: Arc>, + pub transform: cgm::Matrix4, +} + +impl Renderable for Mesh { + fn data(&self) -> Option { + Some(Data { + vertices: self.vertices.clone(), + indices: self.indices.clone(), + transform: self.transform.clone(), + }) + } +} diff --git a/engine/src/render/vulkan/data.rs b/engine/src/render/vulkan/data.rs index 3c0e552..2d34244 100644 --- a/engine/src/render/vulkan/data.rs +++ b/engine/src/render/vulkan/data.rs @@ -13,19 +13,6 @@ impl Vertex { } vulkano::impl_vertex!(Vertex, pos, color); -pub fn vertices() -> [Vertex; 4] { - [ - Vertex::new([-0.5, -0.5, 0.0], [1.0, 0.0, 0.0]), - Vertex::new([0.5, -0.5, 0.0], [0.0, 1.0, 0.0]), - Vertex::new([0.5, 0.5, 0.0], [0.0, 0.0, 1.0]), - Vertex::new([-0.5, 0.5, 0.0], [1.0, 1.0, 1.0]) - ] -} - -pub fn indices() -> [u16; 6] { - [0, 1, 2, 2, 3, 0] -} - #[derive(Copy, Clone)] pub struct UniformBufferObject { pub model: cgm::Matrix4, diff --git a/engine/src/render/vulkan/mod.rs b/engine/src/render/vulkan/mod.rs index 06f8e18..79ec8af 100644 --- a/engine/src/render/vulkan/mod.rs +++ b/engine/src/render/vulkan/mod.rs @@ -1,23 +1,23 @@ use std::sync::Arc; -use std::sync::Mutex; use std::time; use log; use cgmath as cgm; -use cgmath::prelude::SquareMatrix; use vulkano::buffer as vb; use vulkano::command_buffer as vc; -use vulkano::descriptor::descriptor_set as vdd; +use vulkano::framebuffer as vf; use vulkano::instance as vi; -use vulkano::pipeline as vp; use vulkano::swapchain as vs; use vulkano::sync::{FenceSignalFuture, GpuFuture}; mod binding; -mod data; +pub mod data; +mod pipeline; +mod qfi; mod shaders; mod swapchains; -mod qfi; + +use crate::render::renderable; const VERSION: vi::Version = vi::Version { major: 1, minor: 0, patch: 0}; @@ -33,10 +33,8 @@ pub struct Instance { surface_binding: Option>, swapchain_binding: Option>, - command_buffers: Vec>, - uniform_buffers: Vec>>, - descriptor_sets: Vec, ((), vdd::PersistentDescriptorSetBuf>>)>>>, + pipeline: Option>, armed: bool, previous_frame_end: Option>>, fps_counter: crate::util::counter::Counter, @@ -66,10 +64,8 @@ impl Instance { surface_binding: None, swapchain_binding: None, - command_buffers: vec![], - uniform_buffers: vec![], - descriptor_sets: vec![], + pipeline: None, previous_frame_end: None, armed: false, fps_counter: crate::util::counter::Counter::new(time::Duration::from_millis(1000)), @@ -102,33 +98,18 @@ impl Instance { let chain = self.swapchain_binding().chain.clone(); let render_pass = self.swapchain_binding().render_pass.clone(); - let pipeline = shaders::pipeline_forward(device.clone(), chain.dimensions(), render_pass); - - let (vbuffer, future) = vb::immutable::ImmutableBuffer::from_iter( - data::vertices().iter().cloned(), - vb::BufferUsage::vertex_buffer(), - self.surface_binding().graphics_queue.clone(), - ).unwrap(); - future.flush().unwrap(); - - let (ibuffer, future) = vb::immutable::ImmutableBuffer::from_iter( - data::indices().iter().cloned(), - vb::BufferUsage::index_buffer(), - self.surface_binding().graphics_queue.clone(), - ).unwrap(); - future.flush().unwrap(); - - self.create_uniform_buffers(); - self.create_descriptor_sets(pipeline.clone()); - self.create_command_buffers(pipeline.clone(), vbuffer, ibuffer); + self.pipeline = Some(Box::new(pipeline::Forward::new(device.clone(), chain.dimensions(), render_pass))); self.previous_frame_end = None; self.armed = true; } // (╯°□°)╯︵ ┻━┻ - pub fn flip(&mut self) { + pub fn flip( + &mut self, + render_data: Vec, + ) { match &self.previous_frame_end { None => (), Some(future) => future.wait(None).unwrap(), @@ -148,7 +129,9 @@ impl Instance { }, Err(err) => panic!("{:?}", err), }; - let command_buffer = self.command_buffers[image_index].clone(); + + let fb = self.swapchain_binding().framebuffers[image_index].clone(); + let command_buffer = self.make_command_buffer(fb, render_data); let gq = self.surface_binding().graphics_queue.clone(); let pq = self.surface_binding().present_queue.clone(); @@ -176,103 +159,69 @@ impl Instance { } } - fn create_descriptor_sets( - &mut self, - pipeline: Arc - ) { - let pool = Arc::new( - Mutex::new( - vdd::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0) - ) - ); - - self.descriptor_sets = self.uniform_buffers - .iter() - .map(|uniform_buffer| - Arc::new( - pool - .lock() - .unwrap() - .next() - .add_buffer(uniform_buffer.clone()) - .unwrap() - .build() - .unwrap() - ) - ) - .collect(); - } - fn dimensions(&self) -> [f32; 2] { let dimensions_u32 = self.swapchain_binding().chain.dimensions(); [dimensions_u32[0] as f32, dimensions_u32[1] as f32] } - fn build_uniform_buffer(&self) -> data::UniformBufferObject { - let elapsed = 40.0 as f32; + fn make_command_buffer( + &mut self, + framebuffer: Arc, + render_data: Vec, + ) -> Arc { + let device = self.surface_binding().device.clone(); + let qf = self.surface_binding().graphics_queue.family(); let dimensions = self.dimensions(); + let mut c = vc::AutoCommandBufferBuilder::primary_simultaneous_use(device.clone(), qf) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap(); - let model = cgm::Matrix4::from_angle_z(cgm::Rad::from(cgm::Deg(elapsed as f32 * 0.180))); let view = cgm::Matrix4::look_at( cgm::Point3::new(2.0, 2.0, 2.0), cgm::Point3::new(0.0, 0.0, 0.0), cgm::Vector3::new(0.0, 0.0, 1.0) ); - let mut proj = cgm::perspective( + let proj = cgm::perspective( cgm::Rad::from(cgm::Deg(45.0)), - dimensions[0] as f32 / dimensions[1] as f32, + dimensions[0] / dimensions[1], 0.1, 10.0 ); - log::info!("model: {:?}", model); - log::info!(" view: {:?}", view); - log::info!(" proj: {:?}", proj); - data::UniformBufferObject { model, view, proj } - } - fn create_uniform_buffers(&mut self) { - let mut buffers = Vec::new(); - - let uniform_buffer = self.build_uniform_buffer(); - for _ in 0..self.swapchain_binding().images.len() { - let buffer = vb::CpuAccessibleBuffer::from_data( - self.surface_binding().device.clone(), - vb::BufferUsage::uniform_buffer_transfer_destination(), - uniform_buffer, + for d in render_data { + let (vbuffer, future) = vb::immutable::ImmutableBuffer::from_iter( + d.vertices.iter().cloned(), + vb::BufferUsage::vertex_buffer(), + self.surface_binding().graphics_queue.clone(), ).unwrap(); - buffers.push(buffer); + future.flush().unwrap(); + + let (ibuffer, future) = vb::immutable::ImmutableBuffer::from_iter( + d.indices.iter().cloned(), + vb::BufferUsage::index_buffer(), + self.surface_binding().graphics_queue.clone(), + ).unwrap(); + future.flush().unwrap(); + + + let ubo = data::UniformBufferObject { + model: d.transform.clone(), + view: view.clone(), + proj: proj.clone(), + }; + let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(ubo); + let pipeline = self.pipeline.as_ref().unwrap().get_pipeline(); + c = c.draw_indexed(pipeline, &vc::DynamicState::none(), + vec![vbuffer.clone()], + ibuffer.clone(), + ds, + ()).unwrap(); } - self.uniform_buffers = buffers; - } - fn create_command_buffers( - &mut self, - pipeline: Arc, - vertex_buffer: Arc, - index_buffer: Arc + Send + Sync>, - ) { - let device = self.surface_binding().device.clone(); - let qf = self.surface_binding().graphics_queue.family(); - self.command_buffers = self.swapchain_binding().framebuffers.iter() - .enumerate() - .map(|(i, framebuffer)| { - Arc::new(vc::AutoCommandBufferBuilder::primary_simultaneous_use(device.clone(), qf) - .unwrap() - .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) - .unwrap() - .draw_indexed(pipeline.clone(), &vc::DynamicState::none(), - vec![vertex_buffer.clone()], - index_buffer.clone(), - self.descriptor_sets[i].clone(), - ()) - .unwrap() - .end_render_pass() - .unwrap() - .build() - .unwrap()) - }) - .collect(); + Arc::new(c.end_render_pass().unwrap() + .build().unwrap()) } diff --git a/engine/src/render/vulkan/pipeline.rs b/engine/src/render/vulkan/pipeline.rs new file mode 100644 index 0000000..856ed3d --- /dev/null +++ b/engine/src/render/vulkan/pipeline.rs @@ -0,0 +1,118 @@ +use std::borrow::Cow; +use std::sync::Arc; + +use vulkano::buffer as vb; +use vulkano::descriptor::descriptor_set as vdd; +use vulkano::device as vd; +use vulkano::format::Format; +use vulkano::framebuffer as vf; +use vulkano::pipeline as vp; +use vulkano::pipeline::shader as vps; + +use crate::render::vulkan::data; +use crate::render::vulkan::shaders; + +type VulkanoPipeline = dyn vp::GraphicsPipelineAbstract + Send + Sync; +type VulkanoDescriptorSet = dyn vdd::DescriptorSet + Send + Sync; + +pub trait Pipeline { + fn get_pipeline(&self) -> Arc; + fn make_descriptor_set(&mut self, ubo: data::UniformBufferObject) -> Arc; +} + +pub struct Forward { + device: Arc, + + pipeline: Arc, + descriptor_set_pool: vdd::FixedSizeDescriptorSetsPool>, +} + +impl Forward { + pub fn new( + device: Arc, + viewport_dimensions: [u32; 2], + render_pass: Arc, + ) -> Forward { + let vertex_shader = shaders::ShaderDefinition { + name: "forward_vert.spv".to_string(), + ty: vps::GraphicsShaderType::Vertex, + inputs: vec![ + vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("pos")) }, + vps::ShaderInterfaceDefEntry { location: 1..2, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("color")) }, + ], + outputs: vec![ + vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) } + ], + }.load_into(device.clone()).expect("could not load vertex shader"); + + let fragment_shader = shaders::ShaderDefinition { + name: "forward_frag.spv".to_string(), + ty: vps::GraphicsShaderType::Fragment, + inputs: vec![ + vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) } + ], + outputs: vec![ + vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("outColor")) } + ], + }.load_into(device.clone()).expect("could not load fragment shader"); + + let dimensions = [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32]; + let viewport = vp::viewport::Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + // Counter-clockwise facing triangles - this is because geometry data is left-handed, and + // the vertex shader performs a handedness flip by doing .y *= -1 on emitted vertices. To + // keep geomtry-space triangles clockwise after this transformation, the pipeline must be + // set to treat counter-clockwise triangles as front-facing. An alternative would be to + // fully embrace the vulkan coordinate system, including geometry - however this goes + // against most existing software and practices. This might bite us in the ass at some + // point in the future. + let pipeline = Arc::new(vp::GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vertex_shader.entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) + .fragment_shader(fragment_shader.entry_point(), ()) + .depth_clamp(false) + .polygon_mode_fill() + .line_width(1.0) + .cull_mode_back() + .front_face_counter_clockwise() + .blend_pass_through() + .render_pass(vf::Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap()) + as Arc; + + let descriptor_set_pool = vdd::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0); + + Forward { + device, + + pipeline, + descriptor_set_pool + } + } +} + +impl Pipeline for Forward { + fn get_pipeline(&self) -> Arc { + self.pipeline.clone() + } + + fn make_descriptor_set(&mut self, ubo: data::UniformBufferObject) -> Arc { + let buffer = vb::CpuAccessibleBuffer::from_data( + self.device.clone(), + vb::BufferUsage::uniform_buffer_transfer_destination(), + ubo, + ).unwrap(); + + Arc::new(self.descriptor_set_pool.next() + .add_buffer(buffer).unwrap() + .build().unwrap()) + } +} diff --git a/engine/src/render/vulkan/shaders.rs b/engine/src/render/vulkan/shaders.rs index 8a52787..dfc6954 100644 --- a/engine/src/render/vulkan/shaders.rs +++ b/engine/src/render/vulkan/shaders.rs @@ -1,5 +1,4 @@ use log; -use std::borrow::Cow; use std::ffi::CStr; use std::fs::File; use std::io::prelude::*; @@ -9,81 +8,17 @@ use runfiles::Runfiles; use vulkano::descriptor::descriptor as vdd; use vulkano::descriptor::pipeline_layout as vdp; use vulkano::device as vd; -use vulkano::format::Format; -use vulkano::framebuffer as vf; -use vulkano::pipeline as vp; use vulkano::pipeline::shader as vps; -pub fn pipeline_forward( - device: Arc, - swap_chain_extent: [u32; 2], - render_pass: Arc, -) -> Arc { - let vertex = ShaderDefinition { - name: "forward_vert.spv".to_string(), - ty: vps::GraphicsShaderType::Vertex, - inputs: vec![ - vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("pos")) }, - vps::ShaderInterfaceDefEntry { location: 1..2, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("color")) }, - ], - outputs: vec![ - vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) } - ], - }.load_into(device.clone()).expect("could not load vertex shader"); - - let fragment = ShaderDefinition { - name: "forward_frag.spv".to_string(), - ty: vps::GraphicsShaderType::Fragment, - inputs: vec![ - vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) } - ], - outputs: vec![ - vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("outColor")) } - ], - }.load_into(device.clone()).expect("could not load fragment shader"); - - let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; - let viewport = vp::viewport::Viewport { - origin: [0.0, 0.0], - dimensions, - depth_range: 0.0 .. 1.0, - }; - - // Counter-clockwise facing triangles - this is because geometry data is left-handed, - // and the vertex shader performs a handedness flip by doing .y *= -1 on emitted - // vertices. To keep geomtry-space triangles clockwise after this transformation, - // the pipeline must be set to treat counter-clockwise triangles as front-facing. - // An alternative would be to fully embrace the vulkan coordinate system, including geometry - - // however this goes against most existing software and practices. - // This might bite us in the ass at some point in the future. - Arc::new(vp::GraphicsPipeline::start() - .vertex_input_single_buffer::() - .vertex_shader(vertex.entry_point(), ()) - .triangle_list() - .primitive_restart(false) - .viewports(vec![viewport]) - .fragment_shader(fragment.entry_point(), ()) - .depth_clamp(false) - .polygon_mode_fill() - .line_width(1.0) - .cull_mode_back() - .front_face_counter_clockwise() - .blend_pass_through() - .render_pass(vf::Subpass::from(render_pass.clone(), 0).unwrap()) - .build(device.clone()) - .unwrap() - ) -} - -struct ShaderDefinition { - name: String, - ty: vps::GraphicsShaderType, - inputs: Vec, - outputs: Vec, +pub struct ShaderDefinition { + pub name: String, + pub ty: vps::GraphicsShaderType, + pub inputs: Vec, + pub outputs: Vec, } impl ShaderDefinition { - fn load_into(self, device: Arc) -> Result { + pub fn load_into(self, device: Arc) -> Result { fn stringify(x: std::io::Error) -> String { format!("IO error: {}", x) } let r = Runfiles::create().map_err(stringify)?; @@ -106,7 +41,7 @@ impl ShaderDefinition { } } -struct LoadedShader { +pub struct LoadedShader { def: ShaderDefinition, module: Arc, } @@ -143,7 +78,7 @@ impl LoadedShader { } #[derive (Debug, Clone)] -struct ShaderLayout(vdd::ShaderStages); +pub struct ShaderLayout(vdd::ShaderStages); unsafe impl vdp::PipelineLayoutDesc for ShaderLayout { fn num_sets(&self) -> usize { 1 } @@ -177,7 +112,7 @@ unsafe impl vdp::PipelineLayoutDesc for ShaderLayout { fn push_constants_range(&self, _num: usize) -> Option { None } } -struct ShaderInterface { +pub struct ShaderInterface { entries: Vec, } diff --git a/engine/src/render/vulkan/swapchains.rs b/engine/src/render/vulkan/swapchains.rs index 91742a3..8b08e1a 100644 --- a/engine/src/render/vulkan/swapchains.rs +++ b/engine/src/render/vulkan/swapchains.rs @@ -123,14 +123,14 @@ impl SwapchainBinding { } fn choose_swap_present_mode(available_present_modes: vs::SupportedPresentModes) -> vs::PresentMode { - //if available_present_modes.mailbox { - // vs::PresentMode::Mailbox - //} else if available_present_modes.immediate { - // vs::PresentMode::Immediate - //} else { - // vs::PresentMode::Fifo - //} - vs::PresentMode::Fifo + if available_present_modes.mailbox { + vs::PresentMode::Mailbox + } else if available_present_modes.immediate { + vs::PresentMode::Immediate + } else { + vs::PresentMode::Fifo + } + //vs::PresentMode::Fifo } fn choose_swap_extent(capabilities: &vs::Capabilities) -> [u32; 2] {