diff --git a/src/canvas/canvas.rs b/src/canvas/canvas.rs new file mode 100644 index 00000000..0860b6aa --- /dev/null +++ b/src/canvas/canvas.rs @@ -0,0 +1,493 @@ +use crate::util::vertex_2d::{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 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::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(&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, compare_mask: None, write_mask: None, reference: 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(), Arc::new(CanvasShader::new_colored(solid_color_kernel.clone(), + capabilities.clone(), + queue.clone(), + physical.clone(), + device.clone())) + ), + (texture_kernel.clone(), Arc::new(CanvasShader::new_textured(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 handle = Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32}); + + let image = CanvasImage { + handle: handle.clone(), + buffer: AttachmentImage::with_usage( + self.device.clone(), + [dimensions.0, dimensions.1], + Format::R8G8B8A8Uint, + usage).unwrap(), + size: dimensions, + }; + + 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 + }); + + let texture = Arc::new(CanvasTexture { + handle: handle.clone(), + buffer: self.get_texture_from_file(filename.clone()), + name: filename.clone(), + size: (0, 0), + }); + + self.texture_buffers.push(texture); + + Some(handle) + } + + pub fn get_texture_handle(&self, texture_name: String) + -> Option> { + + for i in self.texture_buffers.clone() { + if i.name == texture_name { + return Some(i.handle.clone()); + } + } + None + } + + pub 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( + // TODO: bad bad bad adbadbadbab + v.first().unwrap().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(); + + + // Images + let mut shader = self.shader_buffers.get("simple_texture").unwrap().clone(); + + + let handle = self.get_texture_handle(String::from("funky-bird.jpg")).unwrap().clone(); + + // TODO: bad bad bad + // Only uses the first texture + let descriptor_set = self.texture_buffers.first().clone().unwrap().clone() + .get_descriptor_set(shader.clone(), self.sampler.clone()); + + let vertex_buffer = self.textured_vertex_buffer.get(&handle).unwrap().clone(); + + command_buffer = command_buffer.draw( + shader.get_pipeline().clone(), + &self.dynamic_state.clone(), vec![vertex_buffer], + vec![descriptor_set], () + ).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() + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/canvas/mod.rs b/src/canvas/mod.rs index 2566f124..81bf0cfd 100644 --- a/src/canvas/mod.rs +++ b/src/canvas/mod.rs @@ -4,6 +4,7 @@ pub mod canvas_frame; pub mod shader; pub mod canvas_text; pub mod canvas_buffer; +pub mod canvas; use crate::canvas::canvas_frame::CanvasFrame; diff --git a/src/compute/compu_state.rs b/src/compute/compu_state.rs index fcf9c573..eed27649 100644 --- a/src/compute/compu_state.rs +++ b/src/compute/compu_state.rs @@ -23,13 +23,14 @@ use crate::compute::compu_buffer::{CompuBuffers, CompuBufferHandle}; use crate::compute::compu_frame::CompuFrame; -// Canvas analog +/// State holding the compute buffers for computation and the kernels which will compute them pub struct CompuState { compute_buffers: Vec, kernels: Vec, } impl CompuState { + pub fn new() -> CompuState { CompuState { compute_buffers: vec![], @@ -37,11 +38,13 @@ impl CompuState { } } + /// Creates a 2d compute buffer from incoming data pub fn new_compute_buffer(&mut self, data: Vec, dimensions: (u32, u32), stride: u32, device: Arc) -> Arc { + let handle = Arc::new(CompuBufferHandle { handle: self.compute_buffers.len() as u32 }); @@ -52,13 +55,18 @@ impl CompuState { handle } + /// Read the compute buffer back into a Vec (TODO BROKEN) pub fn read_compute_buffer(&mut self, handle: Arc) -> Vec { // This is way more difficult than it should be //let compute_buffer : CompuBuffers = self.compute_buffers.get(handle.into()).unwrap(); //compute_buffer.read_output_buffer().to_vec() - Vec::new() + unimplemented!("read_compute_buffer is not implemented") + } + + /// Write to the compute buffer, ostensibly overwriting what's already there + pub fn write_compute_buffer(&self, handle: Arc, data: Vec) { + unimplemented!("read_compute_buffer is not implemented") } - pub fn write_compute_buffer(&self, handle: Arc, data: Vec) {} pub fn new_kernel(&mut self, filename: String, diff --git a/src/main.rs b/src/main.rs index 64a93f27..73e3b1cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,8 +36,25 @@ pub mod sprite; pub mod canvas; pub mod compute; +/* + + Trac3r : A program to convert images to 2D toolpaths + + TODO: + + + Text rendering is half implemented. + + Need generalized interface for render (image, texture, text) + + Currently using local copies of a few libraries: + shade_runner + vulkano/vulkano-win + vulkano/vulkano-shaders + vulkano/vulkano + + Need to generate runtime vertex definitions if I want to have on the fly shaders + + + +*/ -/// Main Entry pub fn main() { hprof::start_frame(); @@ -59,6 +76,7 @@ pub fn main() { let mut window = surface.window(); let mut processor = vkprocessor::VkProcessor::new(&instance, &surface); + { let g = hprof::enter("vulkan preload"); processor.create_swapchain(&surface); @@ -112,20 +130,24 @@ pub fn main() { let mut count = 0; - while let Some(p) = window.get_position() { - elapsed_time = timer.elap_time(); - delta_time = elapsed_time - current_time; - current_time = elapsed_time; - if delta_time > 0.02 { - delta_time = 0.02; + while let true = processor.is_open() { + + // Take care of our timing + { + elapsed_time = timer.elap_time(); + delta_time = elapsed_time - current_time; + current_time = elapsed_time; + if delta_time > 0.02 { + delta_time = 0.02; + } + accumulator_time += delta_time; } - accumulator_time += delta_time; while (accumulator_time - step_size) >= step_size { accumulator_time -= step_size; } - // println!("{}", delta_time); + // Events loop is borrowed from the surface events_loop.poll_events(|event| { match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => @@ -156,21 +178,13 @@ pub fn main() { break; } - let mut compu_frame = CompuFrame::new(); - compu_frame.add(compute_buffer.clone(), compute_kernel.clone()); - compu_frame.add_with_image_swap(compute_buffer.clone(), compute_kernel.clone(), &compu_sprite1); - - - let mut canvas = CanvasFrame::new(); - canvas.draw(&funky_sprite); - canvas.draw(&test_polygon); - - - - - - - +// let mut compu_frame = CompuFrame::new(); +// compu_frame.add(compute_buffer.clone(), compute_kernel.clone()); +// compu_frame.add_with_image_swap(compute_buffer.clone(), compute_kernel.clone(), &compu_sprite1); +// +// let mut canvas = CanvasFrame::new(); +// canvas.draw(&funky_sprite); +// canvas.draw(&test_polygon); { let g = hprof::enter("Run"); diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs index 220db96b..1a16fd82 100644 --- a/src/vkprocessor.rs +++ b/src/vkprocessor.rs @@ -23,8 +23,8 @@ use crate::canvas::shader::text_shader::{TextShader, GlyphInstance}; use vulkano::pipeline::vertex::{OneVertexOneInstanceDefinition, SingleBufferDefinition}; use crate::util::vertex_3d::Vertex3D; -/// VKProcessor holds the vulkan instance information, the swapchain, and the compute and canvas states -/// +/// VKProcessor holds the vulkan instance information, the swapchain, +/// and the compute and canvas states pub struct VkProcessor<'a> { // Vulkan state fields pub instance: Arc, @@ -38,10 +38,13 @@ pub struct VkProcessor<'a> { swapchain_recreate_needed: bool, + /// State holding textures, images, and their related vertex buffers + canvas_state: CanvasState, + /// State holding compute_state: CompuState, capabilities: Capabilities, - canvas_state: CanvasState, + } @@ -85,6 +88,10 @@ impl<'a> VkProcessor<'a> { } } + pub fn is_open(&mut self) -> bool { + self.surfcae + } + /// Using the surface, we calculate the surface capabilities and create the swapchain and swapchain images pub fn create_swapchain(&mut self, surface: &'a Arc>) { let (mut swapchain, images) = { @@ -213,6 +220,7 @@ impl<'a> VkProcessor<'a> { self.compute_state.write_compute_buffer(handle, data) } + /// pub fn run(&mut self, surface: &'a Arc>, canvas_frame: CanvasFrame,