Refactor resource caches and use it with textures now

This commit is contained in:
Keavon Chambers 2020-05-02 22:34:23 -07:00
parent 2ab74f7ba1
commit f4900dd919
8 changed files with 192 additions and 77 deletions

View file

@ -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<wgpu::ShaderModule>,
pub pipeline_cache: ResourceCache<Pipeline>,
pub texture_cache: ResourceCache<Texture>,
pub gui_rect_queue: VecDeque<GUIRect>,
pub draw_command_queue: VecDeque<DrawCommand>,
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::<wgpu::ShaderModule>::new();
let pipeline_cache = ResourceCache::<Pipeline>::new();
let texture_cache = ResourceCache::<Texture>::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

View file

@ -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,

View file

@ -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;

View file

@ -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<Pipeline>,
pub name_to_id: HashMap<String, PipelineID>,
name_to_id: HashMap<String, CacheID>,
}
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);
}
}

52
src/resource_cache.rs Normal file
View file

@ -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<T> {
pub resources: Vec<T>,
name_to_id: HashMap<String, CacheID>,
}
impl<T> ResourceCache<T> {
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);
}
}
}
}

View file

@ -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<wgpu::ShaderModule>,
pub path_to_id: HashMap<String, ShaderID>,
name_to_id: HashMap<String, CacheID>,
}
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);
}

14
src/shader_stage.rs Normal file
View file

@ -0,0 +1,14 @@
pub fn compile_from_glsl(device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> std::io::Result<wgpu::ShaderModule> {
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)
}

View file

@ -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<wgpu::Texture>,
pub name_to_id: HashMap<String, TextureID>,
pub textures: Vec<Texture>,
name_to_id: HashMap<String, CacheID>,
}
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(())