use crate::vertex_2d::{ColoredVertex2D, Vertex2D}; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; use std::collections::HashMap; use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer}; use std::sync::Arc; use vulkano::format::{ClearValue, Format}; use vulkano::framebuffer::{FramebufferAbstract, Framebuffer}; use vulkano::device::{Device, Queue}; use vulkano::instance::PhysicalDevice; use vulkano::image::immutable::ImmutableImage; use crate::util::shader_kernels::ShaderKernels; use vulkano::image::{Dimensions, ImageAccess, ImageDimensions, SwapchainImage, ImageUsage, AttachmentImage}; use vulkano::sampler::{Sampler, SamplerAddressMode, MipmapMode, Filter}; use vulkano::descriptor::DescriptorSet; use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; use std::path::PathBuf; use image::GenericImageView; use crate::util::compute_image::ComputeImage; use std::iter::FromIterator; use vulkano::swapchain::Capabilities; use winit::Window; use vulkano::pipeline::viewport::Viewport; // Canvas is the accumulator of Sprites for drawing // Needs to know: // textured? // colored? // vertices /* If it is textured. It needs to be rendered with the texture shader which requires a separate graphics pipeline. Might as well have a new render pass as well. So framebuffer is tied to the swapchains images as well as the renderpass it appears that renderpass is tied to the individual shader */ // I want to be able to draw 2d sprites. // These sprites might be textured or a single color // All of the single colors will be grouped into one batch using colored vertices. // The rest will be grouped by their texture and run individually pub trait Vertex { fn position(&self) -> (f32, f32) { (0.0, 0.0) } fn color(&self) -> Option<(f32, f32, f32, f32)> { Some((0., 0., 0., 0.)) } } impl Vertex for ColoredVertex2D { fn position(&self) -> (f32, f32) { (0.0, 0.0) } fn color(&self) -> Option<(f32, f32, f32, f32)> { Some((0., 0., 0., 0.)) } } pub trait Drawable { fn get_vertices(&self) -> Vec<(f32, f32)>; fn get_color(&self) -> (f32, f32, f32, f32); fn get_texture_handle(&self) -> Option>; fn get_image_handle(&self) -> Option>; } // Need three types of shaders. Solid, Textured, Image #[derive(PartialEq, Eq, Hash, Clone)] pub enum ShaderType { SOLID = 0, TEXTURED = 1, IMAGE = 2, } pub struct CanvasFrame { colored_drawables: Vec, textured_drawables: HashMap, Vec>>, image_drawables: HashMap, Vec>>, } impl CanvasFrame { pub fn new() -> CanvasFrame { CanvasFrame { colored_drawables: vec![], textured_drawables: Default::default(), image_drawables: Default::default() } } // Accumulates the drawables vertices and colors pub fn draw(&mut self, drawable: &dyn Drawable) { match drawable.get_texture_handle() { Some(handle) => { self.textured_drawables .entry(handle.clone()) .or_insert(Vec::new()) .push(drawable.get_vertices().iter().map(|n| Vertex2D { position: [n.0, n.1], } ).collect::>()); } None => { match drawable.get_image_handle() { Some(handle) => { self.image_drawables .entry(handle.clone()) .or_insert(Vec::new()) .push(drawable.get_vertices().iter().map(|n| Vertex2D { position: [n.0, n.1], } ).collect()); } None => { let colors = drawable.get_color(); self.colored_drawables.extend( drawable.get_vertices().iter().map(|n| ColoredVertex2D { position: [n.0, n.1], color: [colors.0, colors.1, colors.2, colors.3], } ) ); } } } } } } #[derive(Clone)] pub struct Canvas { shader_kernels: HashMap, texture_store: HashMap>>, dynamic_state: DynamicState, sampler: Arc, // hold the image, texture, and shader buffers the same was as we do CompuState image_buffers: Vec>, image_buffer_handles: Vec>, texture_buffers: Vec>>, texture_buffer_handles: Vec>, shader_buffers: HashMap, shader_buffer_handles: Vec>, // Hold onto the vertices we get from the Compu and Canvas Frames // When the run comes around, push the vertices to the GPU colored_drawables: Vec, colored_vertex_buffer: Vec>, textured_drawables: HashMap, Vec>>, textured_vertex_buffer: HashMap, Arc<(dyn BufferAccess + std::marker::Send + std::marker::Sync)>>, image_drawables: HashMap, Vec>>, image_vertex_buffer: HashMap, Arc<(dyn BufferAccess + std::marker::Send + std::marker::Sync)>>, // Looks like we gotta hold onto the queue for managing textures queue: Arc, device: Arc, } impl Canvas { // This method is called once during initialization, then again whenever the window is resized pub fn window_size_dependent_setup(&mut self, images: &[Arc>]) -> Vec> { let dimensions = images[0].dimensions(); self.dynamic_state.viewports = Some(vec![Viewport { origin: [0.0, 0.0], dimensions: [dimensions.width() as f32, dimensions.height() as f32], depth_range: 0.0..1.0, }]); images.iter().map(|image| { Arc::new( Framebuffer::start(self.shader_kernels.get(&ShaderType::SOLID).unwrap().render_pass.clone()) .add(image.clone()).unwrap() .build().unwrap() ) as Arc }).collect::>() } // needs to take in the texture list pub fn new(queue: Arc, device: Arc, physical: PhysicalDevice, capabilities: Capabilities) -> Canvas { let solid_color_kernel = String::from("color-passthrough"); let texture_kernel = String::from("simple_texture"); let shader_kernels : HashMap = HashMap::from_iter(vec![ (ShaderType::SOLID, ShaderKernels::new(solid_color_kernel, capabilities.clone(), queue.clone(), physical.clone(), device.clone())), (ShaderType::TEXTURED, ShaderKernels::new(texture_kernel, capabilities.clone(), queue.clone(), physical.clone(), device.clone())) ]); Canvas { shader_kernels: Default::default(), texture_store: Default::default(), dynamic_state: DynamicState { line_width: None, viewports: None, scissors: None }, sampler: Sampler::new(device.clone(), Filter::Linear, Filter::Linear, MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(), image_buffers: vec![], image_buffer_handles: vec![], texture_buffers: vec![], texture_buffer_handles: vec![], shader_buffers: Default::default(), shader_buffer_handles: vec![], colored_drawables: vec![], colored_vertex_buffer: vec![], textured_drawables: Default::default(), textured_vertex_buffer: Default::default(), image_drawables: Default::default(), image_vertex_buffer: Default::default(), queue: queue.clone(), device: device.clone(), } } pub fn create_image(&mut self, dimensions: (u32, u32), usage: ImageUsage) -> Arc { self.image_buffers.push( AttachmentImage::with_usage( self.device.clone(), [dimensions.0, dimensions.1], Format::R8G8B8A8Uint, usage).unwrap()); let id = Arc::new(self.image_buffers.len() as u32); self.image_buffer_handles.push(id.clone()); id } pub fn get_image(&self, image_handle: Arc) -> std::sync::Arc { self.image_buffers.get((*image_handle).clone() as usize).unwrap().clone() } // TODO Handle file not found gracefully fn get_texture_from_file(&self, image_filename: String) -> Arc> { let project_root = std::env::current_dir() .expect("failed to get root directory"); let mut compute_path = project_root.clone(); compute_path.push(PathBuf::from("resources/images/")); compute_path.push(PathBuf::from(image_filename)); let img = image::open(compute_path).expect("Couldn't find image"); let xy = img.dimensions(); let data_length = xy.0 * xy.1 * 4; let pixel_count = img.raw_pixels().len(); let mut image_buffer = Vec::new(); if pixel_count != data_length as usize { println!("Creating apha channel..."); for i in img.raw_pixels().iter() { if (image_buffer.len() + 1) % 4 == 0 { image_buffer.push(255); } image_buffer.push(*i); } image_buffer.push(255); } else { image_buffer = img.raw_pixels(); } let (texture, tex_future) = ImmutableImage::from_iter( image_buffer.iter().cloned(), Dimensions::Dim2d { width: xy.0, height: xy.1 }, Format::R8G8B8A8Srgb, self.queue.clone(), ).unwrap(); texture } pub fn load_texture_from_filename(&mut self, filename: String) -> Arc> { if self.texture_store.contains_key(&filename.clone()) { println!("{} Already exists, not going to replace it.", filename.clone()); self.texture_store.get(&filename.clone()).unwrap().clone() } else { let texture = self.get_texture_from_file(filename.clone()); self.texture_store.insert(filename, texture.clone()); texture } } pub fn load_texture(&mut self, filename: String) -> Option> { let texture_buffer = self.get_texture_from_file(filename.clone()); self.texture_buffers.push(texture_buffer.clone()); let id = Arc::new(self.texture_buffers.len() as u32); self.texture_buffer_handles.push(id.clone()); Some(id) } // After done using this, need to call allocated vertex buffers pub fn draw(&mut self, canvas_frame: CanvasFrame) { self.textured_drawables = canvas_frame.textured_drawables; self.colored_drawables = canvas_frame.colored_drawables; self.image_drawables = canvas_frame.image_drawables; self.allocate_vertex_buffers(self.device.clone()); } fn get_texture(&self, texture_id: String) -> Arc> { if let Some(i) = self.texture_store.get(&texture_id) { return i.clone(); } else { panic!("{} : Texture not loaded", texture_id); } } pub fn allocate_vertex_buffers(&mut self, device: Arc) { self.colored_vertex_buffer.clear(); self.textured_vertex_buffer.clear(); self.image_vertex_buffer.clear(); //TODO should probably use cpu accessible buffer instead of recreating immutes each frame // CpuAccessibleBuffer::from_iter( // device.clone(), // BufferUsage::vertex_buffer(), // self.colored_drawables.iter().cloned(), // ).unwrap().0; self.colored_vertex_buffer.push( ImmutableBuffer::from_iter( self.colored_drawables.iter().cloned(), BufferUsage::vertex_buffer(), self.queue.clone(), ).unwrap().0 ); for (k, v) in self.textured_drawables.drain() { self.textured_vertex_buffer.insert( k.clone(), ImmutableBuffer::from_iter( v.iter().cloned(), BufferUsage::vertex_buffer(), self.queue.clone(), ).unwrap().0, ); } } fn get_solid_color_descriptor_set(&self) -> Box { println!("{}", self.shader_kernels.get(&ShaderType::SOLID).unwrap().clone().get_pipeline().clone().num_sets()); let o: Box = Box::new( PersistentDescriptorSet::start( self.shader_kernels.get(&ShaderType::SOLID).unwrap().clone().get_pipeline().clone(), 0, ).build().unwrap()); o } fn get_textured_descriptor_set(&self, texture_id: String) -> Box { let o: Box = Box::new( PersistentDescriptorSet::start( self.shader_kernels.get(&ShaderType::TEXTURED).unwrap().clone().get_pipeline().clone(), 0, ) .add_sampled_image(self.get_texture(texture_id), self.sampler.clone()).unwrap() .build().unwrap()); o } // This is the image which is written to by the write compute buffer // I suppose I could just have a general image set maker instead of compue... they are // somewhat similar fn get_compute_swap_descriptor_set(&mut self, device: Arc, compute_image: &ComputeImage) -> Box { let sampler = Sampler::new(device.clone(), Filter::Linear, Filter::Linear, MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(); let o: Box = Box::new( PersistentDescriptorSet::start( self.shader_kernels.get(&ShaderType::IMAGE).clone().unwrap().clone().get_pipeline(), 0, ) .add_image(compute_image.clone().get_swap_buffer().clone()).unwrap() .build().unwrap()); o } pub fn draw_commands(&self, mut command_buffer: AutoCommandBufferBuilder, framebuffers: Vec>, image_num: usize) -> AutoCommandBufferBuilder { // Specify the color to clear the framebuffer with i.e. blue let clear_values = vec!(ClearValue::Float([0.0, 0.0, 1.0, 1.0])); let mut command_buffer = command_buffer.begin_render_pass( framebuffers[image_num].clone(), false, clear_values.clone(), ).unwrap(); for (shader_type, kernel) in self.shader_kernels.clone().iter() { match shader_type { ShaderType::SOLID => { command_buffer = command_buffer.draw( kernel.clone().get_pipeline().clone(), &self.dynamic_state.clone(), self.colored_vertex_buffer.clone(), (), (), ).unwrap(); } ShaderType::TEXTURED => { // command_buffer = command_buffer.draw( // kernel.clone().get_pipeline().clone(), // &dynamic_state.clone(), self.textured_vertex_buffer.clone(), // vec![self.get_textured_descriptor_set(String::from("funky-bird.jpg"))], () // ).unwrap(); } ShaderType::IMAGE => { } } } command_buffer .end_render_pass() .unwrap() } }