diff --git a/src/application.rs b/src/application.rs index bffa2fa88..d5f53e55c 100644 --- a/src/application.rs +++ b/src/application.rs @@ -3,8 +3,8 @@ use super::color_palette::ColorPalette; use super::gui_rect::GUIRect; use super::pipeline::Pipeline; use super::texture::Texture; -use super::shader_cache::ShaderCache; -use super::pipeline_cache::PipelineCache; +use super::shader_stage::compile_from_glsl; +use super::resource_cache::ResourceCache; use super::draw_command::DrawCommand; use std::collections::VecDeque; use winit::event::*; @@ -20,9 +20,9 @@ pub struct Application { pub queue: wgpu::Queue, pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub swap_chain: wgpu::SwapChain, - pub shader_cache: ShaderCache, - pub pipeline_cache: PipelineCache, - // pub texture_cache: TextureCache, + pub shader_cache: ResourceCache, + pub pipeline_cache: ResourceCache, + pub texture_cache: ResourceCache, pub gui_rect_queue: VecDeque, pub draw_command_queue: VecDeque, pub temp_color_toggle: bool, @@ -67,8 +67,9 @@ impl Application { let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor); // Cache of all loaded shaders and the Pipeline programs they form - let shader_cache = ShaderCache::new(); - let pipeline_cache = PipelineCache::new(); + let shader_cache = ResourceCache::::new(); + let pipeline_cache = ResourceCache::::new(); + let texture_cache = ResourceCache::::new(); let gui_rect_queue = VecDeque::new(); @@ -83,6 +84,7 @@ impl Application { swap_chain, shader_cache, pipeline_cache, + texture_cache, gui_rect_queue, draw_command_queue, temp_color_toggle: true, @@ -104,21 +106,39 @@ impl Application { 2, 3, 4, ]; - // Load the vertex and fragment shaders - self.shader_cache.load(&self.device, "shaders/shader.vert", glsl_to_spirv::ShaderType::Vertex).unwrap(); - self.shader_cache.load(&self.device, "shaders/shader.frag", glsl_to_spirv::ShaderType::Fragment).unwrap(); - let vertex_shader = self.shader_cache.get_by_path("shaders/shader.vert").unwrap(); - let fragment_shader = self.shader_cache.get_by_path("shaders/shader.frag").unwrap(); + // Load the vertex shader + let vertex_shader_path = "shaders/shader.vert"; + let vertex_shader_module = compile_from_glsl(&self.device, vertex_shader_path, glsl_to_spirv::ShaderType::Vertex).unwrap(); + self.shader_cache.set(vertex_shader_path, vertex_shader_module); - // Construct a pipeline from the shader pair and a new BindGroup that holds a new TextureView, then store the pipeline in the cache - let example_pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader); - let example_texture_view = Texture::from_filepath(&self.device, &mut self.queue, "textures/grid.png").unwrap().texture_view; + // Load the fragment shader + let fragment_shader_path = "shaders/shader.frag"; + let fragment_shader_module = compile_from_glsl(&self.device, fragment_shader_path, glsl_to_spirv::ShaderType::Fragment).unwrap(); + self.shader_cache.set(fragment_shader_path, fragment_shader_module); + + // Get the shader pair + let vertex_shader = self.shader_cache.get(vertex_shader_path).unwrap(); + let fragment_shader = self.shader_cache.get(fragment_shader_path).unwrap(); + + // Construct a pipeline from the shader pair + let pipeline_name = "example"; + let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader); + self.pipeline_cache.set(pipeline_name, pipeline); + let example_pipeline = self.pipeline_cache.get(pipeline_name).unwrap(); + + // Load a texture from the image file + let texture_path = "textures/grid.png"; + let texture = Texture::from_filepath(&self.device, &mut self.queue, texture_path).unwrap(); + self.texture_cache.set(texture_path, texture); + let grid_texture = self.texture_cache.get(texture_path).unwrap(); + + // Create a BindGroup that holds a new TextureView let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &example_pipeline.bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, - resource: wgpu::BindingResource::TextureView(&example_texture_view), + resource: wgpu::BindingResource::TextureView(&grid_texture.texture_view), }, // wgpu::Binding { // binding: 1, @@ -127,14 +147,10 @@ impl Application { ], label: None, }); - let pipeline_id = self.pipeline_cache.set("example", example_pipeline); - assert_eq!(pipeline_id, super::pipeline_cache::PipelineID::new(0)); - - // Create a draw command with the vertex data and bind group - let example_draw_command = DrawCommand::new(&self.device, pipeline_id, VERTICES, INDICES, bind_group); - - self.draw_command_queue.push_back(example_draw_command); + // Create a draw command with the vertex data and bind group and push it to the GPU command queue + let draw_command = DrawCommand::new(&self.device, pipeline_name, VERTICES, INDICES, bind_group); + self.draw_command_queue.push_back(draw_command); } pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) { @@ -256,7 +272,7 @@ impl Application { // println!("Set pipeline"); // } - let pipeline = self.pipeline_cache.get_by_id(command.pipeline_id).unwrap(); + let pipeline = self.pipeline_cache.get(&command.pipeline_name).unwrap(); render_pass.set_pipeline(&pipeline.render_pipeline); // Commands sent to the GPU for drawing during this render pass diff --git a/src/draw_command.rs b/src/draw_command.rs index 6c2aef42a..8ac0d0392 100644 --- a/src/draw_command.rs +++ b/src/draw_command.rs @@ -1,7 +1,5 @@ -use super::pipeline_cache::PipelineID; - pub struct DrawCommand { - pub pipeline_id: PipelineID, + pub pipeline_name: String, pub bind_group: wgpu::BindGroup, pub vertex_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer, @@ -9,13 +7,13 @@ pub struct DrawCommand { } impl DrawCommand { - pub fn new(device: &wgpu::Device, pipeline_id: PipelineID, vertices: &[[f32; 2]], indices: &[u16], bind_group: wgpu::BindGroup) -> Self { + pub fn new(device: &wgpu::Device, pipeline_name: &str, vertices: &[[f32; 2]], indices: &[u16], bind_group: wgpu::BindGroup) -> Self { let vertex_buffer = device.create_buffer_with_data(bytemuck::cast_slice(vertices), wgpu::BufferUsage::VERTEX); let index_buffer = device.create_buffer_with_data(bytemuck::cast_slice(indices), wgpu::BufferUsage::INDEX); let index_count = indices.len() as u32; Self { - pipeline_id, + pipeline_name: String::from(pipeline_name), bind_group, vertex_buffer, index_buffer, diff --git a/src/main.rs b/src/main.rs index 8e163d908..c11d99621 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,9 @@ mod texture; mod color_palette; mod shader_cache; mod pipeline_cache; +mod texture_cache; +mod resource_cache; +mod shader_stage; mod draw_command; use application::Application; diff --git a/src/pipeline_cache.rs b/src/pipeline_cache.rs index 350d0db26..0062afdca 100644 --- a/src/pipeline_cache.rs +++ b/src/pipeline_cache.rs @@ -2,19 +2,19 @@ use std::collections::HashMap; use super::pipeline::Pipeline; #[derive(Copy, Clone, PartialEq, Debug)] -pub struct PipelineID { +struct CacheID { index: usize, } -impl PipelineID { - pub fn new(index: usize) -> Self { +impl CacheID { + fn new(index: usize) -> Self { Self { index } } } pub struct PipelineCache { pub pipelines: Vec, - pub name_to_id: HashMap, + name_to_id: HashMap, } impl PipelineCache { @@ -28,30 +28,32 @@ impl PipelineCache { } } - pub fn get_by_name(&self, name: &str) -> Option<&Pipeline> { + #[allow(dead_code)] + pub fn get(&self, name: &str) -> Option<&Pipeline> { match self.name_to_id.get(name) { Some(id) => self.pipelines.get(id.index), None => None, } } - pub fn get_by_id(&self, id: PipelineID) -> Option<&Pipeline> { - self.pipelines.get(id.index) - } - - pub fn set(&mut self, name: &str, pipeline: Pipeline) -> PipelineID { + #[allow(dead_code)] + pub fn set(&mut self, name: &str, pipeline: Pipeline) { match self.name_to_id.get(name) { Some(id) => { self.pipelines[id.index] = pipeline; - id.clone() }, None => { let last_index = self.name_to_id.len(); - let id = PipelineID::new(last_index); + let id = CacheID::new(last_index); self.name_to_id.insert(String::from(name), id); self.pipelines.push(pipeline); - id } } } + + #[allow(dead_code)] + pub fn load(&mut self, device: &wgpu::Device, name: &str, vertex_shader: &wgpu::ShaderModule, fragment_shader: &wgpu::ShaderModule) { + let pipeline = Pipeline::new(device, vertex_shader, fragment_shader); + self.set(name, pipeline); + } } \ No newline at end of file diff --git a/src/resource_cache.rs b/src/resource_cache.rs new file mode 100644 index 000000000..341f095f3 --- /dev/null +++ b/src/resource_cache.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +#[derive(Copy, Clone, PartialEq, Debug)] +struct CacheID { + index: usize, +} + +impl CacheID { + fn new(index: usize) -> Self { + Self { index } + } +} + +pub struct ResourceCache { + pub resources: Vec, + name_to_id: HashMap, +} + +impl ResourceCache { + pub fn new() -> Self { + let resources = Vec::new(); + let name_to_id = HashMap::new(); + + Self { + resources, + name_to_id, + } + } + + #[allow(dead_code)] + pub fn get(&self, name: &str) -> Option<&T> { + match self.name_to_id.get(name) { + Some(id) => self.resources.get(id.index), + None => None, + } + } + + #[allow(dead_code)] + pub fn set(&mut self, name: &str, resource: T) { + match self.name_to_id.get(name) { + Some(id) => { + self.resources[id.index] = resource; + }, + None => { + let last_index = self.name_to_id.len(); + let id = CacheID::new(last_index); + self.name_to_id.insert(String::from(name), id); + self.resources.push(resource); + } + } + } +} \ No newline at end of file diff --git a/src/shader_cache.rs b/src/shader_cache.rs index ec5be77d1..8dbbb168a 100644 --- a/src/shader_cache.rs +++ b/src/shader_cache.rs @@ -1,45 +1,59 @@ use std::collections::HashMap; + #[derive(Copy, Clone, PartialEq, Debug)] -pub struct ShaderID { +struct CacheID { index: usize, } -impl ShaderID { - pub fn new(index: usize) -> Self { +impl CacheID { + fn new(index: usize) -> Self { Self { index } } } pub struct ShaderCache { pub shaders: Vec, - pub path_to_id: HashMap, + name_to_id: HashMap, } impl ShaderCache { pub fn new() -> Self { let shaders = Vec::new(); - let path_to_id = HashMap::new(); + let name_to_id = HashMap::new(); Self { shaders, - path_to_id, + name_to_id, } } - pub fn get_by_path(&self, path: &str) -> Option<&wgpu::ShaderModule> { - match self.path_to_id.get(path) { + #[allow(dead_code)] + pub fn get(&self, name: &str) -> Option<&wgpu::ShaderModule> { + match self.name_to_id.get(name) { Some(id) => self.shaders.get(id.index), None => None, } } - // pub fn get_by_id(&self, id: ShaderID) -> Option<&wgpu::ShaderModule> { - // self.shaders.get(id.index) - // } + #[allow(dead_code)] + pub fn set(&mut self, name: &str, shader: wgpu::ShaderModule) { + match self.name_to_id.get(name) { + Some(id) => { + self.shaders[id.index] = shader; + }, + None => { + let last_index = self.name_to_id.len(); + let id = CacheID::new(last_index); + self.name_to_id.insert(String::from(name), id); + self.shaders.push(shader); + } + } + } + #[allow(dead_code)] pub fn load(&mut self, device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> std::io::Result<()> { - if self.path_to_id.get(path).is_none() { + if self.name_to_id.get(path).is_none() { let source = std::fs::read_to_string(path)?; let spirv = match glsl_to_spirv::compile(&source[..], shader_type) { Ok(spirv_output) => spirv_output, @@ -51,8 +65,8 @@ impl ShaderCache { let compiled = wgpu::read_spirv(spirv)?; let shader = device.create_shader_module(&compiled); - let last_index = self.path_to_id.len(); - self.path_to_id.insert(String::from(path), ShaderID { index: last_index }); + let last_index = self.name_to_id.len(); + self.name_to_id.insert(String::from(path), CacheID::new(last_index)); self.shaders.push(shader); } diff --git a/src/shader_stage.rs b/src/shader_stage.rs new file mode 100644 index 000000000..2a35d18ac --- /dev/null +++ b/src/shader_stage.rs @@ -0,0 +1,14 @@ +pub fn compile_from_glsl(device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> std::io::Result { + let source = std::fs::read_to_string(path)?; + let spirv = match glsl_to_spirv::compile(&source[..], shader_type) { + Ok(spirv_output) => spirv_output, + Err(message) => { + println!("Error compiling GLSL to SPIRV shader: {}", message); + panic!("{}", message); + } + }; + let compiled = wgpu::read_spirv(spirv)?; + let shader = device.create_shader_module(&compiled); + + Ok(shader) +} \ No newline at end of file diff --git a/src/texture_cache.rs b/src/texture_cache.rs index 976bc3c4c..98bb5620f 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -1,48 +1,64 @@ use std::collections::HashMap; -use std::collections::HashMap; +use super::texture::Texture; -#[derive(Copy, Clone)] -pub struct TextureID { - pub index: usize, +#[derive(Copy, Clone, PartialEq, Debug)] +struct CacheID { + index: usize, +} + +impl CacheID { + fn new(index: usize) -> Self { + Self { index } + } } pub struct TextureCache { - pub textures: Vec, - pub name_to_id: HashMap, + pub textures: Vec, + name_to_id: HashMap, } -impl ShaderCache { +impl TextureCache { pub fn new() -> Self { - let shaders = Vec::new(); + let textures = Vec::new(); let name_to_id = HashMap::new(); Self { - shaders, + textures, name_to_id, } } - pub fn get_by_path(&self, path: &str) -> Option<&wgpu::ShaderModule> { - match self.name_to_id.get(path) { - Some(id) => self.shaders.get(id.index), + #[allow(dead_code)] + pub fn get(&self, name: &str) -> Option<&Texture> { + match self.name_to_id.get(name) { + Some(id) => self.textures.get(id.index), None => None, } } - pub fn get_by_id(&self, id: ShaderID) -> Option<&wgpu::ShaderModule> { - self.shaders.get(id.index) + #[allow(dead_code)] + pub fn set(&mut self, name: &str, texture: Texture) { + match self.name_to_id.get(name) { + Some(id) => { + self.textures[id.index] = texture; + }, + None => { + let last_index = self.name_to_id.len(); + let id = CacheID::new(last_index); + self.name_to_id.insert(String::from(name), id); + self.textures.push(texture); + } + } } - pub fn load(&mut self, device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> std::io::Result<()> { + #[allow(dead_code)] + pub fn load(&mut self, device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str) -> std::io::Result<()> { if self.name_to_id.get(path).is_none() { - let source = std::fs::read_to_string(path)?; - let spirv = glsl_to_spirv::compile(&source[..], shader_type).unwrap(); - let compiled = wgpu::read_spirv(spirv).unwrap(); - let shader = device.create_shader_module(&compiled); + let texture = Texture::from_filepath(device, queue, "textures/grid.png").unwrap(); let length = self.name_to_id.len(); - self.name_to_id.insert(String::from(path), ShaderID { index: length }); - self.shaders.push(shader); + self.name_to_id.insert(String::from(path), CacheID::new(length)); + self.textures.push(texture); } Ok(())