From bf3abfe29dc70bb9dc7fc0b76080ef9b4c5e95e7 Mon Sep 17 00:00:00 2001 From: mitchellhansen Date: Mon, 9 Sep 2019 21:52:52 -0700 Subject: [PATCH] more documentation. Image base impl --- resources/shaders/simple_image.fragment | 22 +++ resources/shaders/simple_image.vertex | 18 +++ src/canvas.rs | 179 ++++++++++++++++-------- src/canvas_buffer.rs | 2 +- src/canvas_shader.rs | 69 +++------ src/main.rs | 25 ++-- src/vkprocessor.rs | 14 +- 7 files changed, 204 insertions(+), 125 deletions(-) create mode 100644 resources/shaders/simple_image.fragment create mode 100644 resources/shaders/simple_image.vertex diff --git a/resources/shaders/simple_image.fragment b/resources/shaders/simple_image.fragment new file mode 100644 index 00000000..8a6cd8ac --- /dev/null +++ b/resources/shaders/simple_image.fragment @@ -0,0 +1,22 @@ +#version 450 +// SIMPLE TEXTURE : FRAGMENT SHADER + +// These come in from the previous shader (vertex) +layout(location = 0) in vec2 img_coords; + +// This goes out to the bound image in window_size_dependent setup +layout(location = 0) out vec4 f_color; + +// This is bound by the descriptor set +// Currently handled by the individual buffer and are 1:1 +layout(set = 0, binding = 0, rgba32ui) readonly uniform uimage2D img; +void main() { + + ivec2 pos = ivec2(gl_FragCoord.x, gl_FragCoord.y); + + f_color = imageLoad(img, ivec2(pos)) / (255.0); + + float gamma = 0.5; + f_color.rgb = pow(f_color.rgb, vec3(1.0/gamma)); + +} \ No newline at end of file diff --git a/resources/shaders/simple_image.vertex b/resources/shaders/simple_image.vertex new file mode 100644 index 00000000..4135665c --- /dev/null +++ b/resources/shaders/simple_image.vertex @@ -0,0 +1,18 @@ +#version 450 +// SIMPLE IMAGE : VERTEX SHADER + +// These come in from the vertex definition +layout(location = 0) in vec2 position; + +// These are made up in the shader themselves +layout(location = 0) out vec2 img_coords; + +void main() { + + gl_Position = vec4(position, 0.0, 1.0); + img_coords = position; +} + + + + diff --git a/src/canvas.rs b/src/canvas.rs index 57eb8f0b..b6ed268f 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -4,7 +4,7 @@ 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::framebuffer::{FramebufferAbstract, Framebuffer, RenderPass, RenderPassAbstract}; use vulkano::device::{Device, Queue}; use vulkano::instance::PhysicalDevice; use vulkano::image::immutable::ImmutableImage; @@ -21,16 +21,8 @@ use vulkano::pipeline::viewport::Viewport; use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer; use crate::canvas_frame::CanvasFrame; use std::hash::Hash; -use crate::canvas_shader::CanvasShader; +use crate::canvas_shader::{CanvasShader, CanvasShaderHandle}; use crate::canvas_buffer::{CanvasImage, CanvasTexture}; -/* -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 -*/ - /// Vertex trait for Drawable Vertices. pub trait Vertex { @@ -81,15 +73,20 @@ pub struct CanvasImageHandle { pub handle: u32 } +/// Canvas state is used for storage of texture and image buffers in addition to vertex buffers +/// Canvas state also contains logic for writing the stored buffers to the command_buffer #[derive(Clone)] pub struct CanvasState { + + /// Generated during new() dynamic_state: DynamicState, + /// Generated during new() 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>, + shader_buffers: Vec>, // Hold onto the vertices we get from the Compu and Canvas Frames // When the run comes around, push the vertices to the GPU @@ -105,13 +102,16 @@ pub struct CanvasState { // Looks like we gotta hold onto the queue for managing textures queue: Arc, device: Arc, + render_pass: Arc, } impl CanvasState { - // This method is called once during initialization, then again whenever the window is resized + + /// 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 = @@ -123,19 +123,54 @@ impl CanvasState { images.iter().map(|image| { Arc::new( - Framebuffer::start(self.shader_buffers.get("color-passthrough").unwrap().render_pass.clone()) + Framebuffer::start(self.render_pass.clone()) .add(image.clone()).unwrap() .build().unwrap() ) as Arc }).collect::>() } - // needs to take in the texture list + /// Creates a Canvas State. Which at this point is pretty empty pub fn new(queue: Arc, device: Arc, physical: PhysicalDevice, capabilities: Capabilities) -> CanvasState { + + let format = capabilities.supported_formats[0].0; + + let render_pass = Arc::new(vulkano::single_pass_renderpass!( + device.clone(), + + // Attachments are outgoing like f_color + attachments: { + // `color` is a custom name we give to the first and only attachment. + color: { + // `load: Clear` means that we ask the GPU to clear the content of this + // attachment at the start of the drawing. + load: Clear, + // `store: Store` means that we ask the GPU to store the output of the draw + // in the actual image. We could also ask it to discard the result. + store: Store, + // `format: ` indicates the type of the format of the image. This has to + // be one of the types of the `vulkano::format` module (or alternatively one + // of your structs that implements the `FormatDesc` trait). Here we use the + // same format as the swapchain. + format: format, + // TODO: + samples: 1, + } + }, + pass: { + // We use the attachment named `color` as the one and only color attachment. + color: [color], + //color: [], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {} + } + ).unwrap()); + + CanvasState { dynamic_state: DynamicState { line_width: None, viewports: None, scissors: None }, sampler: Sampler::new(device.clone(), Filter::Linear, Filter::Linear, @@ -143,7 +178,7 @@ impl CanvasState { SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(), image_buffers: vec![], texture_buffers: vec![], - shader_buffers: HashMap::from_iter(vec![]), + shader_buffers: vec![], colored_drawables: vec![], colored_vertex_buffer: vec![], @@ -154,9 +189,11 @@ impl CanvasState { queue: queue.clone(), device: device.clone(), + render_pass: render_pass.clone(), } } + /// Using the dimensions and suggested usage, load a CanvasImage and return it's handle pub fn create_image(&mut self, dimensions: (u32, u32), usage: ImageUsage) -> Arc { let handle = Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32 }); @@ -175,12 +212,13 @@ impl CanvasState { handle } + /// Return the image buffer from an input 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 + /// Load a texture buffer from an input filename fn get_texture_from_file(&self, image_filename: String) -> Arc> { let project_root = std::env::current_dir() @@ -222,6 +260,7 @@ impl CanvasState { texture } + /// Load a texture using it's filename from a file. Returns the handle of the loaded texture pub fn load_texture(&mut self, filename: String) -> Option> { let texture_buffer = self.get_texture_from_file(filename.clone()); @@ -246,14 +285,25 @@ impl CanvasState { pub fn load_shader(&mut self, filename: String, physical: PhysicalDevice, - capabilities: Capabilities) { - - self.shader_buffers.insert(filename.clone(), - Arc::new(CanvasShader::new_colored(filename.clone(), - capabilities.clone(), - self.queue.clone(), - physical.clone(), - self.device.clone()))); + capabilities: Capabilities) -> Option> { + + let handle = Arc::new(CanvasShaderHandle { + handle: self.shader_buffers.len() as u32 + }); + + let shader = Arc::new(CanvasShader::new_colored( + filename.clone(), + capabilities.clone(), + self.queue.clone(), + physical.clone(), + self.device.clone(), + handle.clone(), + self.render_pass.clone()) + ); + + self.shader_buffers.push(shader.clone()); + + Some(handle) } /// Using the texture name, iterates through the stored textures and matches by the name @@ -267,6 +317,17 @@ impl CanvasState { None } + /// Using the shader name, iterates through the stored textures and matches by the name + pub fn get_shader_handle(&self, shader_name: String) + -> Option> { + for shader in self.shader_buffers.clone() { + if shader.name == shader_name { + return Some(shader.handle.clone()); + } + } + None + } + /// Using the texture handle, grab the stored texture and return the buffer pub fn get_texture(&self, texture_handle: Arc) -> Arc> { @@ -291,7 +352,6 @@ impl CanvasState { /// draw(canvas_fame) stored all the intermediate information, this function /// allocates the vertex buffers using that information fn allocate_vertex_buffers(&mut self) { - self.colored_vertex_buffer.clear(); { let g = hprof::enter("Colored Vertex Buffer"); @@ -344,7 +404,8 @@ impl CanvasState { o } - /// Pushes the draw commands s + /// Pushes the draw commands to the command buffer. Requires the framebuffers and + /// image number to be passed in as they are taken care of by the vkprocessor pub fn draw_commands(&self, mut command_buffer: AutoCommandBufferBuilder, framebuffers: Vec>, @@ -358,7 +419,10 @@ impl CanvasState { ).unwrap(); // Solid colors - let mut shader = self.shader_buffers.get("color-passthrough").unwrap().clone(); + let mut shader = self.shader_buffers.get( + self.get_shader_handle(String::from("color-passthrough")) + .unwrap().clone().handle as usize + ).unwrap(); // This looks a little weird as colored_vertex_buffer is a vec of GPU allocated vecs. // But we can pass in multiple vertex buffers @@ -372,42 +436,43 @@ impl CanvasState { } // Textures - let mut shader = self.shader_buffers.get("simple_texture").unwrap().clone(); + let mut shader = self.shader_buffers.get( + self.get_shader_handle(String::from("simple_texture")) + .unwrap().clone().handle as usize + ).unwrap(); if !self.textured_vertex_buffer.is_empty() { - let handle = self.get_texture_handle(String::from("funky-bird.jpg")).unwrap().clone(); - - // TODO : BAD BAD BAD. SELECTS FIRST TEXTURE ONLY!!!!!!!!!!!! - 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 (texture_handle, vertex_buffer) in self.textured_vertex_buffer.clone() { + let handle = texture_handle.clone().handle as usize; + let descriptor_set = self.texture_buffers.get(handle).clone().unwrap().clone() + .get_descriptor_set(shader.clone(), self.sampler.clone()); + + command_buffer = command_buffer.draw( + shader.get_pipeline().clone(), + &self.dynamic_state.clone(), vec![vertex_buffer], + vec![descriptor_set], (), + ).unwrap(); + } } - - let mut shader = self.shader_buffers.get("simple-image").unwrap().clone(); + // Images + let mut shader = self.shader_buffers.get( + self.get_shader_handle(String::from("simple_image")) + .unwrap().clone().handle as usize + ).unwrap(); if !self.image_vertex_buffer.is_empty() { - - let handle = self.get_texture_handle(String::from("funky-bird.jpg")).unwrap().clone(); - - // TODO : BAD BAD BAD. SELECTS FIRST TEXTURE ONLY!!!!!!!!!!!! - 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 (image_handle, vertex_buffer) in self.image_vertex_buffer.clone() { + let handle = image_handle.clone().handle as usize; + let descriptor_set = self.image_buffers.get(handle).clone().unwrap().clone() + .get_descriptor_set(shader.clone()); + + command_buffer = command_buffer.draw( + shader.get_pipeline().clone(), + &self.dynamic_state.clone(), vec![vertex_buffer], + vec![descriptor_set], (), + ).unwrap(); + } } command_buffer diff --git a/src/canvas_buffer.rs b/src/canvas_buffer.rs index 99fc1536..86776a63 100644 --- a/src/canvas_buffer.rs +++ b/src/canvas_buffer.rs @@ -37,7 +37,7 @@ pub struct CanvasImage { } impl CanvasImage { - pub fn get_descriptor_set(&mut self, shader: Arc) + pub fn get_descriptor_set(&self, shader: Arc) -> Box { let o: Box = Box::new( PersistentDescriptorSet::start( diff --git a/src/canvas_shader.rs b/src/canvas_shader.rs index 2073866e..a266dd8d 100644 --- a/src/canvas_shader.rs +++ b/src/canvas_shader.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use shade_runner as sr; use vulkano::framebuffer::{Subpass, RenderPassAbstract, Framebuffer, FramebufferAbstract}; use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule, SpecializationConstants, SpecializationMapEntry}; -use vulkano::swapchain::{Capabilities}; +use vulkano::swapchain::Capabilities; use crate::vertex_2d::{ColoredVertex2D, Vertex2D}; /// Typed wrapper for a u32 shader handle (index id) @@ -20,18 +20,17 @@ pub struct CanvasShaderHandle { #[derive(Clone)] pub struct CanvasShader { - pub render_pass: Arc, graphics_pipeline: Option>, device: Arc, + pub(crate) handle: Arc, + pub(crate) name: String, } impl CanvasShader { - /// Takes the filename of a .vertex .fragment shader combo in resources/shaders/ /// Returns pathbuffer of that vertex and fragment shader fn get_path(filename: String) -> (PathBuf, PathBuf) { - let project_root = std::env::current_dir() .expect("failed to get root directory"); @@ -58,10 +57,12 @@ impl CanvasShader { /// Create a new `Colored` shader. Which just means that it uses ColoredVertex2D's /// This will explode when the shader does not want to compile pub fn new_colored(filename: String, - capabilities: Capabilities, - queue: Arc, - physical: PhysicalDevice, - device: Arc) -> CanvasShader { + capabilities: Capabilities, + queue: Arc, + physical: PhysicalDevice, + device: Arc, + handle: Arc, + render_pass: Arc,) -> CanvasShader { let format = capabilities.supported_formats[0].0; @@ -142,7 +143,6 @@ impl CanvasShader { CanvasShader { - graphics_pipeline: Some(Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer::() @@ -170,17 +170,20 @@ impl CanvasShader { .unwrap())), device: device, - render_pass: render_pass, + handle: handle.clone(), + name: filename.clone(), } } /// Create a new `Textured` shader. Which just means that it uses plain Vertex2D's /// This will explode when the shader does not want to compile pub fn new_textured(filename: String, - capabilities: Capabilities, - queue: Arc, - physical: PhysicalDevice, - device: Arc) -> CanvasShader { + capabilities: Capabilities, + queue: Arc, + physical: PhysicalDevice, + device: Arc, + handle: Arc, + render_pass: Arc,) -> CanvasShader { let format = capabilities.supported_formats[0].0; @@ -227,41 +230,7 @@ impl CanvasShader { GraphicsShaderType::Vertex)) }; - - let render_pass = Arc::new(vulkano::single_pass_renderpass!( - device.clone(), - - // Attachments are outgoing like f_color - attachments: { - // `color` is a custom name we give to the first and only attachment. - color: { - // `load: Clear` means that we ask the GPU to clear the content of this - // attachment at the start of the drawing. - load: Clear, - // `store: Store` means that we ask the GPU to store the output of the draw - // in the actual image. We could also ask it to discard the result. - store: Store, - // `format: ` indicates the type of the format of the image. This has to - // be one of the types of the `vulkano::format` module (or alternatively one - // of your structs that implements the `FormatDesc` trait). Here we use the - // same format as the swapchain. - format: format, - // TODO: - samples: 1, - } - }, - pass: { - // We use the attachment named `color` as the one and only color attachment. - color: [color], - //color: [], - // No depth-stencil attachment is indicated with empty brackets. - depth_stencil: {} - } - ).unwrap()); - - CanvasShader { - graphics_pipeline: Some(Arc::new(GraphicsPipeline::start() .vertex_input_single_buffer::() @@ -289,10 +258,10 @@ impl CanvasShader { .unwrap())), device: device, - render_pass: render_pass, + handle: handle.clone(), + name: filename.clone(), } } - } #[repr(C)] diff --git a/src/main.rs b/src/main.rs index b9140204..331c5746 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ - #![allow(dead_code)] #![allow(unused_variables)] #![allow(unused_mut)] @@ -14,7 +13,7 @@ extern crate hprof; use sfml::system::*; use vulkano::sync; use crate::timer::Timer; -use vulkano::instance::{Instance}; +use vulkano::instance::Instance; use vulkano::sync::GpuFuture; use winit::{EventsLoop, WindowBuilder, WindowEvent, Event, DeviceEvent, VirtualKeyCode, ElementState}; use winit::dpi::LogicalSize; @@ -59,7 +58,6 @@ Canvas works, but I want to use CPU accessible buffer instead of immutable buffe /// Main Entry pub fn main() { - hprof::start_frame(); let q1 = hprof::enter("setup"); @@ -96,12 +94,12 @@ pub fn main() { let mut accumulator_time: f32 = 0.0; let mut current_time: f32 = timer.elap_time(); - let mut mouse_xy = Vector2i::new(0,0); + let mut mouse_xy = Vector2i::new(0, 0); - let sprite = Sprite::new_with_color((0.,0.), (0.1,0.1), (1.,0.,0.,1.)); - let sprite2 = Sprite::new_with_color((-1.,-0.5), (0.1,0.1), (0.,1.,0.,1.)); + let sprite = Sprite::new_with_color((0., 0.), (0.1, 0.1), (1., 0., 0., 1.)); + let sprite2 = Sprite::new_with_color((-1., -0.5), (0.1, 0.1), (0., 1., 0., 1.)); - let compu_sprite1 = CompuSprite::new((-1.,-0.5), (0.1,0.1), + let compu_sprite1 = CompuSprite::new((-1., -0.5), (0.1, 0.1), // This swap image needs to match the size of the compute processor.new_swap_image((720, 756))); @@ -113,7 +111,7 @@ pub fn main() { let handle = processor.get_texture_handle(String::from("funky-bird.jpg")).unwrap(); - let sprite3 = Sprite::new_with_texture((0.3, 0.5), (0.1,0.1), handle.clone()); + let sprite3 = Sprite::new_with_texture((0.3, 0.5), (0.1, 0.1), handle.clone()); drop(q2); drop(q1); @@ -141,10 +139,10 @@ pub fn main() { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { exit = true; - }, + } Event::WindowEvent { event: WindowEvent::Resized(_), .. } => { processor.recreate_swapchain(&surface); - }, + } Event::DeviceEvent { event: DeviceEvent::Key(keyboard_input), .. } => { match keyboard_input.virtual_keycode.unwrap() { VirtualKeyCode::A => { @@ -154,7 +152,7 @@ pub fn main() { } _ => () } - }, + } // Event::DeviceEvent { event: DeviceEvent::Button(mouse_input), .. } => { // mouse_xy.x // }, @@ -178,9 +176,8 @@ pub fn main() { { let g = hprof::enter("Run"); processor.run(&surface, - //frame_future, - canvas, - compu_frame); + canvas, + compu_frame); } } diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs index 2aad7ed7..888f6ca2 100644 --- a/src/vkprocessor.rs +++ b/src/vkprocessor.rs @@ -157,20 +157,25 @@ impl<'a> VkProcessor<'a> { pub fn preload_shaders(&mut self) { self.canvas.load_shader(String::from("color-passthrough"), self.physical.clone(), self.capabilities.clone()); self.canvas.load_shader(String::from("simple_texture"), self.physical.clone(), self.capabilities.clone()); + self.canvas.load_shader(String::from("simple_image"), self.physical.clone(), self.capabilities.clone()); } - + /// O(n) Lookup for the matching texture string pub fn get_texture_handle(&self, texture_name: String) -> Option> { self.canvas.get_texture_handle(texture_name) } + + /// O(n) Lookup for the matching kernel string pub fn get_kernel_handle(&self, kernel_name: String) -> Option> { self.compute_state.get_kernel_handle(kernel_name) } + + /// O(n) Lookup for the matching shader string pub fn get_shader_handle(&self, shader_name: String) -> Option> { - None + self.canvas.get_shader_handle(shader_name) } - // Create a new image which has the transfer usage + /// Create a new image which has the transfer usage pub fn new_swap_image(&mut self, dimensions: (u32, u32)) -> Arc { let mut usage = ImageUsage::none(); usage.transfer_destination = true; @@ -179,14 +184,17 @@ impl<'a> VkProcessor<'a> { self.canvas.create_image(dimensions, usage) } + /// Builds a compute buffer and returns it's handle pub fn new_compute_buffer(&mut self, data: Vec, dimensions: (u32, u32), stride: u32) -> Arc { self.compute_state.new_compute_buffer(data, dimensions, stride, self.device.clone()) } + /// Takes a compute buffer handle and returns the read data pub fn read_compute_buffer(&mut self, handle: Arc) -> Vec { self.compute_state.read_compute_buffer(handle) } + /// Takes a compute buffer handle and writes the received data pub fn write_compute_buffer(&self, handle: Arc, data: Vec) { self.compute_state.write_compute_buffer(handle, data) }