diff --git a/engine/BUILD b/engine/BUILD index a989026..789fbe2 100644 --- a/engine/BUILD +++ b/engine/BUILD @@ -9,6 +9,7 @@ rust_binary( "src/render/vulkan/mod.rs", "src/render/vulkan/binding.rs", "src/render/vulkan/qfi.rs", + "src/render/vulkan/shaders.rs", "src/render/vulkan/swapchains.rs", ], deps = [ @@ -17,5 +18,10 @@ rust_binary( "//third_party/cargo:env_logger", "//third_party/cargo:vulkano", "//third_party/cargo:vulkano_win", + "@io_bazel_rules_rust//tools/runfiles", + ], + data = [ + "//engine/shaders:triangle_vert", + "//engine/shaders:triangle_frag", ], ) diff --git a/engine/shaders/BUILD b/engine/shaders/BUILD index 2138142..f786a65 100644 --- a/engine/shaders/BUILD +++ b/engine/shaders/BUILD @@ -5,6 +5,7 @@ glsl_binary( srcs = [ "triangle.vert" ], + visibility = ["//engine:__pkg__"], ) glsl_binary( @@ -12,4 +13,5 @@ glsl_binary( srcs = [ "triangle.frag" ], + visibility = ["//engine:__pkg__"], ) diff --git a/engine/shaders/triangle.frag b/engine/shaders/triangle.frag index 2a2bac7..e1d1db2 100644 --- a/engine/shaders/triangle.frag +++ b/engine/shaders/triangle.frag @@ -2,8 +2,9 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +layout(location = 0) in vec3 fragColor; layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(1.0, 0.0, 0.0, 1.0); + outColor = vec4(fragColor, 1.0); } diff --git a/engine/src/render/mod.rs b/engine/src/render/mod.rs index b8cfe13..c66ee26 100644 --- a/engine/src/render/mod.rs +++ b/engine/src/render/mod.rs @@ -27,6 +27,7 @@ impl Renderer { let mut instance = vulkan::Instance::new("abrasion".to_string()); let (events_loop, surface) = Self::init_window(instance.get_vulkan()); instance.use_surface(&surface); + Self { instance, events_loop, @@ -43,8 +44,15 @@ impl Renderer { (events_loop, surface) } + fn draw_frame(&mut self) { + let future = self.instance.flip(); + future.wait(None).unwrap() + } + pub fn main_loop(&mut self) { loop { + self.draw_frame(); + let mut done = false; self.events_loop.poll_events(|ev| { if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { diff --git a/engine/src/render/vulkan/mod.rs b/engine/src/render/vulkan/mod.rs index 09fee64..88e9cbc 100644 --- a/engine/src/render/vulkan/mod.rs +++ b/engine/src/render/vulkan/mod.rs @@ -1,11 +1,15 @@ use std::sync::Arc; use log; +use vulkano::command_buffer as vc; use vulkano::instance as vi; use vulkano::swapchain as vs; -use std::ops::Deref; +use vulkano::framebuffer as vf; +use vulkano::pipeline::vertex as vpv; +use vulkano::sync::{FenceSignalFuture, GpuFuture}; mod binding; +mod shaders; mod swapchains; mod qfi; @@ -23,9 +27,12 @@ pub struct Instance { binding: Option>, swapchains: Option>, + render_pass: Option>, + framebuffers: Vec>, + command_buffers: Vec>, } -impl Instance { +impl Instance { pub fn new(name: String) -> Self { let ai = vi::ApplicationInfo { application_name: Some(name.clone().into()), @@ -46,6 +53,9 @@ impl Instance { binding: None, swapchains: None, + render_pass: None, + framebuffers: vec![], + command_buffers: vec![], } } @@ -53,11 +63,95 @@ impl Instance { self.vulkan.clone() } + pub fn get_swapchain(&self) -> Arc> { + self.swapchains.as_ref().unwrap().chain.clone() + } + pub fn use_surface(&mut self, surface: &Arc>) { self.binding = Some(binding::Binding::new(&self.vulkan, &surface)); self.swapchains = Some(swapchains::Swapchains::new(self.binding.as_ref().unwrap())); log::info!("Bound to Vulkan Device: {}", self.binding.as_ref().unwrap().physical_device().name()); + + let device = self.binding.as_ref().unwrap().device.clone(); + let chain = self.get_swapchain(); + + self.create_render_pass(chain.format()); + let render_pass = self.render_pass.as_ref().unwrap().clone(); + + let pipeline = shaders::pipeline_triangle(device, chain.dimensions(), render_pass); + self.create_framebuffers(); + + self.create_command_buffers(pipeline); + } + + pub fn flip(&self) -> FenceSignalFuture, Arc>, WT>> { + let chain = self.get_swapchain(); + let (image_index, acquire_future) = vs::acquire_next_image(chain.clone(), None).unwrap(); + let command_buffer = self.command_buffers[image_index].clone(); + + let gq = self.binding.as_ref().unwrap().graphics_queue.clone(); + let pq = self.binding.as_ref().unwrap().present_queue.clone(); + + acquire_future + .then_execute(gq, command_buffer) + .unwrap() + .then_swapchain_present(pq, chain, image_index) + .then_signal_fence_and_flush() + .unwrap() + } + + fn create_render_pass(&mut self, color_format: vulkano::format::Format) { + let device = self.binding.as_ref().unwrap().device.clone(); + + self.render_pass = Some(Arc::new(vulkano::single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap())) + } + + fn create_framebuffers(&mut self) { + let render_pass = self.render_pass.as_ref().unwrap().clone(); + + self.framebuffers = self.swapchains.as_ref().unwrap().images.iter() + .map(|image| { + let fba: Arc = Arc::new(vf::Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + }) + .collect::>(); + } + + fn create_command_buffers(&mut self, pipeline: Arc) { + let device = self.binding.as_ref().unwrap().device.clone(); + let qf = self.binding.as_ref().unwrap().graphics_queue.family(); + self.command_buffers = self.framebuffers.iter() + .map(|framebuffer| { + let vertices = vpv::BufferlessVertices { vertices: 3, instances: 1 }; + 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(pipeline.clone(), &vc::DynamicState::none(), + vertices, (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); } @@ -74,5 +168,3 @@ impl Instance { }).expect("could not create debug callback") } } - - diff --git a/engine/src/render/vulkan/shaders.rs b/engine/src/render/vulkan/shaders.rs new file mode 100644 index 0000000..38a52ce --- /dev/null +++ b/engine/src/render/vulkan/shaders.rs @@ -0,0 +1,180 @@ +use log; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fs::File; +use std::io::prelude::*; +use std::sync::Arc; + +use runfiles::Runfiles; +use vulkano::descriptor as vD; +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; +use vulkano::pipeline::vertex as vpv; + +pub type ConcreteGraphicsPipeline = vp::GraphicsPipeline, Arc>; + +pub fn pipeline_triangle( + device: Arc, + swap_chain_extent: [u32; 2], + render_pass: Arc, +) -> Arc { + let vertex = ShaderDefinition { + name: "triangle_vert.spv".to_string(), + ty: vps::GraphicsShaderType::Vertex, + inputs: vec![], + 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: "triangle_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, + }; + + Arc::new(vp::GraphicsPipeline::start() + .vertex_input(vpv::BufferlessDefinition {}) + .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_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, +} + +impl ShaderDefinition { + 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)?; + let path = r.rlocation(format!("abrasion/engine/shaders/{}", self.name)); + + log::info!("Loading shader {}", path.to_str().unwrap_or("UNKNOWN")); + + let mut f = File::open(path).map_err(stringify)?; + let mut v = vec![]; + f.read_to_end(&mut v).map_err(stringify)?; + + let module = unsafe { + vps::ShaderModule::new(device.clone(), &v).unwrap() + }; + + Ok(LoadedShader { + def: self, + module: module, + }) + } +} + +struct LoadedShader { + def: ShaderDefinition, + module: Arc, +} + +impl LoadedShader { + fn ios(&self) -> (ShaderInterface, ShaderInterface) { + ( + ShaderInterface { entries: self.def.inputs.clone() }, + ShaderInterface { entries: self.def.outputs.clone() }, + ) + } + + fn layout(&self) -> ShaderLayout { + match self.def.ty { + vps::GraphicsShaderType::Vertex => ShaderLayout(vdd::ShaderStages{ vertex: true, ..vdd::ShaderStages::none() }), + vps::GraphicsShaderType::Fragment => ShaderLayout(vdd::ShaderStages{ fragment: true, ..vdd::ShaderStages::none() }), + _ => panic!("unknown shader type") + } + } + + pub fn entry_point<'a, S>(&'a self) -> vps::GraphicsEntryPoint<'a, S, ShaderInterface, ShaderInterface, ShaderLayout> { + let (input, output) = self.ios(); + let layout = self.layout(); + + unsafe { + self.module.graphics_entry_point( + CStr::from_bytes_with_nul_unchecked(b"main\0"), + input, + output, + layout, + self.def.ty) + } + } +} + +#[derive (Debug, Clone)] +struct ShaderLayout(vdd::ShaderStages); + +unsafe impl vdp::PipelineLayoutDesc for ShaderLayout { + fn num_sets(&self) -> usize { 0 } + fn num_bindings_in_set(&self, _set: usize) -> Option { None } + fn descriptor(&self, _set: usize, _binding: usize) -> Option { None } + fn num_push_constants_ranges(&self) -> usize { 0 } + fn push_constants_range(&self, _num: usize) -> Option { None } +} + +struct ShaderInterface { + entries: Vec, +} + +unsafe impl vps::ShaderInterfaceDef for ShaderInterface { + type Iter = InterfaceIterator; + + fn elements(&self) -> Self::Iter { + InterfaceIterator { + entries: self.entries.clone(), + } + } +} + +pub struct InterfaceIterator { + entries: Vec, +} + +impl std::iter::Iterator for InterfaceIterator { + type Item = vps::ShaderInterfaceDefEntry; + fn next(&mut self) -> Option { + let cur = self.entries.first()?.clone(); + self.entries = self.entries[1..].to_vec(); + Some(cur) + } + fn size_hint(&self) -> (usize, Option) { + let len = self.entries.len(); + (len, Some(len)) + } +} +impl std::iter::ExactSizeIterator for InterfaceIterator {} diff --git a/engine/src/render/vulkan/swapchains.rs b/engine/src/render/vulkan/swapchains.rs index 604fe4d..a02f8d4 100644 --- a/engine/src/render/vulkan/swapchains.rs +++ b/engine/src/render/vulkan/swapchains.rs @@ -6,8 +6,8 @@ use vulkano::format as vf; use vulkano::sync as vy; pub struct Swapchains { - chain: Arc>, - images: Vec>>, + pub chain: Arc>, + pub images: Vec>>, } impl Swapchains {