use std::{convert::TryInto, io::{self, ErrorKind, Read, Write}, path::PathBuf, sync::Arc}; use vulkano::{buffer::TypedBufferAccess, command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}, descriptor_set::PersistentDescriptorSet, image::view::ImageView, pipeline::{PipelineBindPoint, shader::ShaderModule}, render_pass::{RenderPass, Subpass}}; use vulkano::device::Device; use vulkano::pipeline::GraphicsPipeline; use crate::{GameObject, vulkan::TextVertex}; use crate::vulkan::Vertex; use crate::vulkan::GameData; use crate::VulkanRenderer; use super::{TextureHandle, gameobject::PushConstantType}; type RP = Arc; type GP = Arc; type DS = Arc; pub trait Drawcall { fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData); fn create_descriptor_sets(self: &Self, textures: &Vec, renderer: &VulkanRenderer) -> Vec>; fn recreate_pipeline(self: &mut Self, device: Arc, render_pass: RP); fn get_pipeline(self: &Self) -> &GP; } pub mod vs { vulkano_shaders::shader!{ ty: "vertex", path: "shaders/triangle.vert" } } pub mod fs { vulkano_shaders::shader! { ty: "fragment", path: "shaders/triangle.frag" } } #[derive(Clone)] pub struct DefaultShader { pipeline: GP, } fn _shader_module_from_file(device: Arc, path: &str) -> Arc { let mut file = std::fs::File::open(path).unwrap(); let mut buffer = vec![]; file.read_to_end(&mut buffer).unwrap(); let words: Vec = buffer.chunks_exact(4).map(|c| u32::from_ne_bytes(c.try_into().unwrap())).collect(); unsafe { ShaderModule::from_words(device.clone(), &words).unwrap() } } #[allow(dead_code)] fn matches_extension(path: &PathBuf, extensions: &Vec<&str>) -> bool { if let Some(Some(path_extension)) = path.extension().map(|e| e.to_str()) { for extension in extensions { if *extension == path_extension { return true; } } } return false; } #[allow(dead_code)] fn compile_shaders() -> io::Result<()> { for file_maybe in std::fs::read_dir("./shaders")? { let path = file_maybe?.path(); if !path.is_dir() && matches_extension(&path, &vec!["frag", "vert"]) { let mut target_path = path.to_str().ok_or(ErrorKind::Other)?.to_string(); target_path.push_str(".spv"); let output = std::process::Command::new("glslangValidator") .arg("-V") .arg(path.to_str().ok_or(ErrorKind::Other)?) .arg("-o") .arg(target_path) .output().unwrap(); std::io::stdout().write_all(&output.stdout)?; if !output.status.success() { eprintln!("Shader compiler {:?}", output.status); } } } Ok(()) } impl DefaultShader { pub fn new(device: Arc, render_pass: RP) -> Self { DefaultShader { pipeline: Self::create_pipeline(device, render_pass) } } fn create_pipeline(device: Arc, render_pass: RP) -> GP { let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); /* #[cfg(debug_assertions)] { println!("Compiling shaders..."); compile_shaders().unwrap(); } unsafe { static ENTRY_NAME: [u8; 5usize] = [109u8, 97u8, 105u8, 110u8, 0]; let entry_name_c = std::ffi::CStr::from_ptr(ENTRY_NAME.as_ptr() as *const _); let fs_module = shader_module_from_file(device.clone(), "shaders/triangle.frag.spv"); let fs_layout = fs::MainLayout(ShaderStages { fragment: true, ..ShaderStages::none() }); let fs_entry = fs_module.graphics_entry_point(entry_name_c, fs::MainInput, fs::MainOutput, fs_layout, GraphicsShaderType::Fragment); let vs_module = shader_module_from_file(device.clone(), "shaders/triangle.vert.spv"); let vs_layout = vs::Layout(ShaderStages { vertex: true, ..ShaderStages::none() }); let vs_entry = vs_module.graphics_entry_point(entry_name_c, vs::MainInput, vs::MainOutput, vs_layout, GraphicsShaderType::Vertex); */ let vs = vs::Shader::load(device.clone()).unwrap(); let fs = fs::Shader::load(device.clone()).unwrap(); Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer::() .vertex_shader(vs.main_entry_point(), ()) .triangle_list() .viewports_dynamic_scissors_irrelevant(1) .depth_stencil_simple_depth() .fragment_shader(fs.main_entry_point(), ()) .blend_alpha_blending() .cull_mode_back() .render_pass(sub_pass.clone()) .build(device.clone()) .unwrap()) //} } } impl Drawcall for DefaultShader { fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData) { for i in 0..game_objects.len() { let game_object = &game_objects[i]; let mesh = &game_data.meshes[game_object.mesh_index]; #[allow(irrefutable_let_patterns)] if let PushConstantType::MeshPC(mesh_push) = game_object.get_push_constants() { builder .bind_pipeline_graphics(self.pipeline.clone()) .bind_descriptor_sets(PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, game_object.descriptor_sets[fb_index].clone()) .bind_vertex_buffers(0, mesh.vertex_buffer.clone()) .bind_index_buffer(mesh.index_buffer.clone()) .push_constants(self.pipeline.layout().clone(), 0, mesh_push) .draw_indexed(mesh.index_buffer.len() as u32, 1, 0, 0, 0).unwrap(); } } } fn create_descriptor_sets(self: &Self, textures: &Vec, renderer: &VulkanRenderer) -> Vec> { let descriptor_set_layout_0 = self.get_pipeline().layout().descriptor_set_layouts().get(0).unwrap().clone(); renderer.uniform_buffers.iter().map(|uniform_buffer| { debug_assert!(textures.len() == 2, "Expected diffuse and normal map for object shader!"); let diffuse = &renderer.game_data.textures[textures[0]]; let diffuse_view = ImageView::new(diffuse.image.clone()).unwrap(); let normal_map = &renderer.game_data.textures[textures[1]]; let normal_view = ImageView::new(normal_map.image.clone()).unwrap(); let mut builder = PersistentDescriptorSet::start(descriptor_set_layout_0.clone()); builder .add_buffer(uniform_buffer.clone()).unwrap() .add_sampled_image(diffuse_view, diffuse.sampler.clone()).unwrap() .add_sampled_image(normal_view, normal_map.sampler.clone()).unwrap(); vec![Arc::new(builder.build().unwrap())] }).collect() } fn recreate_pipeline(self: &mut Self, device: Arc, render_pass: RP) { self.pipeline = Self::create_pipeline(device, render_pass); } fn get_pipeline(self: &Self) -> &GP { &self.pipeline } } /* pub mod line_vs { vulkano_shaders::shader!{ ty: "vertex", path: "shaders/line.vert" } } pub mod line_fs { vulkano_shaders::shader! { ty: "fragment", path: "shaders/line.frag" } } #[derive(Clone)] pub struct LineShader { pipeline: GP, vertex_buffer: Arc> } impl LineShader { pub fn _new(device: Arc, render_pass: RP, vertex_buffer: Arc>) -> Self { LineShader { pipeline: Self::create_pipeline(device, render_pass), vertex_buffer } } fn create_pipeline(device: Arc, render_pass: RP) -> GP { let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); let vertex_shader = line_vs::Shader::load(device.clone()).unwrap(); let fragment_shader = line_fs::Shader::load(device.clone()).unwrap(); Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer::() .vertex_shader(vertex_shader.main_entry_point(), ()) .line_list() .viewports_dynamic_scissors_irrelevant(1) .depth_stencil_simple_depth() .fragment_shader(fragment_shader.main_entry_point(), ()) .render_pass(sub_pass.clone()) .build(device.clone()) .unwrap()) } } impl Drawcall for LineShader { fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, _fb_index: usize, _game_objects: Vec<&GameObject>, game_data: &GameData) { builder.draw(self.pipeline.clone(), vec![self.vertex_buffer.clone()], (), game_data.line_push_constants.clone()).unwrap(); } fn create_descriptor_sets(self: &Self, _textures: &Vec, _renderer: &VulkanRenderer) -> Vec> { vec![] } fn recreate_pipeline(self: &mut Self, device: Arc, render_pass: RP) { self.pipeline = Self::create_pipeline(device, render_pass); } fn get_pipeline(self: &Self) -> &GP { &self.pipeline } } */ pub mod vs_text { vulkano_shaders::shader!{ ty: "vertex", path: "shaders/text.vert" } } pub mod fs_text { vulkano_shaders::shader!{ ty: "fragment", path: "shaders/text.frag" } } pub struct TextShader { pipeline: GP, } impl TextShader { pub fn new(device: Arc, render_pass: RP) -> Self { TextShader { pipeline: Self::create_pipeline(device, render_pass) } } fn create_pipeline(device: Arc, render_pass: RP) -> GP { let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap(); /* #[cfg(debug_assertions)] { println!("Compiling shaders..."); compile_shaders().unwrap(); } unsafe { static ENTRY_NAME: [u8; 5usize] = [109u8, 97u8, 105u8, 110u8, 0]; let entry_name_c = std::ffi::CStr::from_ptr(ENTRY_NAME.as_ptr() as *const _); let fs_module = shader_module_from_file(device.clone(), "shaders/text.frag.spv"); let fs_layout = fs::Layout(ShaderStages { fragment: true, ..ShaderStages::none() }); let fs_entry = fs_module.graphics_entry_point(entry_name_c, fs_text::MainInput, fs_text::MainOutput, fs_layout, GraphicsShaderType::Fragment); let vs_module = shader_module_from_file(device.clone(), "shaders/text.vert.spv"); let vs_layout = vs::Layout(ShaderStages { vertex: true, ..ShaderStages::none() }); let vs_entry = vs_module.graphics_entry_point(entry_name_c, vs_text::MainInput, vs_text::MainOutput, vs_layout, GraphicsShaderType::Vertex); */ let vs = vs_text::Shader::load(device.clone()).unwrap(); let fs = fs_text::Shader::load(device.clone()).unwrap(); let gp = Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer::() .vertex_shader(vs.main_entry_point(), ()) .triangle_list() .viewports_dynamic_scissors_irrelevant(1) .depth_stencil_simple_depth() .fragment_shader(fs.main_entry_point(), ()) .blend_alpha_blending() .cull_mode_disabled() .render_pass(sub_pass.clone()) .build(device.clone()) .unwrap()); gp //} } } impl Drawcall for TextShader { fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData) { for i in 0..game_objects.len() { let game_object = &game_objects[i]; let mesh = &game_data.meshes_text[game_object.mesh_index]; builder.bind_pipeline_graphics(self.pipeline.clone()) .bind_descriptor_sets(PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, game_object.descriptor_sets[fb_index].clone()) .bind_vertex_buffers(0, mesh.vertex_buffer.clone()) .bind_index_buffer(mesh.index_buffer.clone()) .draw_indexed(mesh.index_buffer.len() as u32, 1, 0, 0, 0).unwrap(); } } fn create_descriptor_sets(self: &Self, textures: &Vec, renderer: &VulkanRenderer) -> Vec> { let descriptor_set_layout = self.get_pipeline().layout().descriptor_set_layouts().get(0).unwrap().clone(); renderer.uniform_buffers.iter().map(|uniform_buffer| { let diffuse_index = match textures.len() { 0 => 0, 1 => textures[0], _ => panic!("Expected only diffuse map for text shader!"), }; let diffuse = &renderer.game_data.textures[diffuse_index]; let diffuse_view = ImageView::new(diffuse.image.clone()).unwrap(); let mut builder = PersistentDescriptorSet::start(descriptor_set_layout.clone()); builder.add_buffer(uniform_buffer.clone()).unwrap() .add_sampled_image(diffuse_view.clone(), diffuse.sampler.clone()).unwrap(); vec![Arc::new(builder.build().unwrap())] }).collect() } fn recreate_pipeline(self: &mut Self, device: Arc, render_pass: RP) { self.pipeline = Self::create_pipeline(device, render_pass); } fn get_pipeline(self: &Self) -> &GP { &self.pipeline } }