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 std::iter::FromIterator; use vulkano::swapchain::Capabilities; use winit::Window; use vulkano::pipeline::viewport::Viewport; use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer; use crate::canvas_frame::CanvasFrame; use std::hash::Hash; // 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, } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct CanvasTextureHandle { pub handle: u32 } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct CanvasImageHandle { pub handle: u32 } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct CanvasShaderHandle { pub handle: u32 } #[derive(Clone)] pub struct CanvasTexture { handle: Arc, buffer: Arc>, name: String, size: (u32, u32), } impl CanvasTexture { fn get_descriptor_set(&mut self, shader: Arc, sampler: Arc) -> Box { let o: Box = Box::new( PersistentDescriptorSet::start( shader.clone().get_pipeline().clone(), 0, ) .add_sampled_image(self.buffer.clone(), sampler.clone()).unwrap() .build().unwrap()); o } } #[derive(Clone)] pub struct CanvasImage { handle: Arc, buffer: Arc, size: (u32, u32), } impl CanvasImage { fn get_descriptor_set(&mut self, shader: Arc) -> Box { let o: Box = Box::new( PersistentDescriptorSet::start( shader.clone().get_pipeline().clone(), 0, ) .add_image(self.buffer.clone()).unwrap() .build().unwrap()); o } } #[derive(Clone)] pub struct CanvasState { dynamic_state: DynamicState, sampler: Arc, // hold the image, texture, and shader buffers the same was as we do CompuState image_buffers: Vec>, texture_buffers: Vec>, shader_buffers: HashMap, // 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 CanvasState { // 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_buffers.get("color-passthrough").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) -> CanvasState { let solid_color_kernel = String::from("color-passthrough"); let texture_kernel = String::from("simple_texture"); CanvasState { 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![], texture_buffers: vec![], shader_buffers: HashMap::from_iter(vec![ (solid_color_kernel.clone(), ShaderKernels::new(solid_color_kernel.clone(), capabilities.clone(), queue.clone(), physical.clone(), device.clone()) ), (texture_kernel.clone(), ShaderKernels::new(texture_kernel.clone(), capabilities.clone(), queue.clone(), physical.clone(), device.clone()) ), ]), colored_drawables: vec![], colored_vertex_buffer: vec![], textured_drawables: HashMap::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 { let image = CanvasImage { handle: Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32 + 1 }), buffer: AttachmentImage::with_usage( self.device.clone(), [dimensions.0, dimensions.1], Format::R8G8B8A8Uint, usage).unwrap(), size: dimensions, }; let handle = image.handle.clone(); self.image_buffers.push(Arc::new(image)); handle } pub fn get_image(&self, image_handle: Arc) -> Arc { self.image_buffers.get((*image_handle).clone().handle as usize).unwrap() .clone().buffer.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(&mut self, filename: String) -> Option> { let texture_buffer = self.get_texture_from_file(filename.clone()); let handle = Arc::new(CanvasTextureHandle { handle: self.texture_buffers.len() as u32 + 1 }); let texture = Arc::new(CanvasTexture { handle: handle.clone(), buffer: self.get_texture_from_file(filename.clone()), name: "".to_string(), size: (0, 0), }); self.texture_buffers.push(texture); Some(handle) } fn get_texture(&self, texture_handle: Arc) -> Arc> { let handle = texture_handle.handle as usize; if let Some(i) = self.texture_buffers.get(handle) { return i.clone().buffer.clone(); } else { panic!("{} : Texture not loaded", handle); } } // 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 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, kernel: Arc) -> Box { let o: Box = Box::new( PersistentDescriptorSet::start( kernel.clone().get_pipeline().clone(), 0, ).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(); // Solid colors let mut shader = self.shader_buffers.get("color-passthrough").unwrap().clone(); command_buffer = command_buffer.draw( shader.get_pipeline().clone(), &self.dynamic_state.clone(), self.colored_vertex_buffer.clone(), (), (), ).unwrap(); /*for (shader_type, kernel) in self.shader_kernels.clone().iter() { match shader_type { ShaderType::SOLID => { } 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() } }