From 9a60ba54fe77f026abcd462d105f456689984997 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 11 May 2020 00:38:01 -0700 Subject: [PATCH] Add bind group caching --- src/application.rs | 82 ++++++++++++++++++++++++++------------------- src/draw_command.rs | 6 ++-- src/pipeline.rs | 65 ++++++++++++++++++----------------- 3 files changed, 84 insertions(+), 69 deletions(-) diff --git a/src/application.rs b/src/application.rs index a9f12ce95..1668d1fa0 100644 --- a/src/application.rs +++ b/src/application.rs @@ -20,6 +20,7 @@ pub struct Application { pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub swap_chain: wgpu::SwapChain, pub shader_cache: ResourceCache, + pub bind_group_cache: ResourceCache, pub pipeline_cache: ResourceCache, pub texture_cache: ResourceCache, pub draw_command_queue: VecDeque, @@ -67,6 +68,7 @@ impl Application { // Resource caches that own the application's shaders, pipelines, and textures let shader_cache = ResourceCache::::new(); + let bind_group_cache = ResourceCache::::new(); let pipeline_cache = ResourceCache::::new(); let texture_cache = ResourceCache::::new(); @@ -84,6 +86,7 @@ impl Application { swap_chain_descriptor, swap_chain, shader_cache, + bind_group_cache, pipeline_cache, texture_cache, draw_command_queue, @@ -95,26 +98,24 @@ impl Application { pub fn example(&mut self) { // Example vertex data const VERTICES: &[[f32; 2]] = &[ - [-0.0868241, 0.49240386], - [-0.49513406, 0.06958647], - [-0.21918549, -0.44939706], - [0.35966998, -0.3473291], - [0.44147372, 0.2347359], + [-0.5, 0.5], + [0.5, 0.5], + [0.5, 1.0], + [-0.5, 1.0], ]; const INDICES: &[u16] = &[ - 0, 1, 4, - 1, 2, 4, - 2, 3, 4, + 0, 1, 2, + 0, 2, 3, ]; - // Load the vertex shader + // If uncached, construct a vertex shader loaded from its source code file let vertex_shader_path = "shaders/shader.vert"; if self.shader_cache.get(vertex_shader_path).is_none() { 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); } - // Load the fragment shader + // If uncached, construct a fragment shader loaded from its source code file let fragment_shader_path = "shaders/shader.frag"; if self.shader_cache.get(fragment_shader_path).is_none() { let fragment_shader_module = compile_from_glsl(&self.device, fragment_shader_path, glsl_to_spirv::ShaderType::Fragment).unwrap(); @@ -125,15 +126,23 @@ impl Application { 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"; + // If uncached, construct a pipeline from the shader pair + let pipeline_name = "example-pipeline"; if self.pipeline_cache.get(pipeline_name).is_none() { - let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader); + let bind_group_layout_binding_types = vec![ + wgpu::BindingType::SampledTexture { + dimension: wgpu::TextureViewDimension::D2, + component_type: wgpu::TextureComponentType::Float, + multisampled: false, + }, + // ty: wgpu::BindingType::Sampler, + ]; + let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader, bind_group_layout_binding_types); self.pipeline_cache.set(pipeline_name, pipeline); } let example_pipeline = self.pipeline_cache.get(pipeline_name).unwrap(); - // Load a texture from the image file + // If uncached, construct a texture loaded from the image file let texture_path = "textures/grid.png"; if self.texture_cache.get(texture_path).is_none() { let texture = Texture::from_filepath(&self.device, &mut self.queue, texture_path).unwrap(); @@ -141,24 +150,18 @@ impl Application { } 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(&grid_texture.texture_view), - }, - // wgpu::Binding { - // binding: 1, - // resource: wgpu::BindingResource::Sampler(&texture.sampler), - // } - ], - label: None, - }); + // If uncached, construct a bind group with resources matching the pipeline's bind group layout + let bind_group_name = "example-bindgroup"; + if self.bind_group_cache.get(bind_group_name).is_none() { + let binding_resources = vec![ + wgpu::BindingResource::TextureView(&grid_texture.texture_view), + ]; + let bind_group = example_pipeline.build_bind_group(&self.device, binding_resources); + self.bind_group_cache.set(bind_group_name, bind_group); + } // 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); + let draw_command = DrawCommand::new(&self.device, pipeline_name, bind_group_name, VERTICES, INDICES); self.draw_command_queue.push_back(draw_command); } @@ -232,15 +235,24 @@ impl Application { depth_stencil_attachment: None, }); + let mut current_pipeline = String::new(); + // Turn the queue of pipelines each into a command buffer and submit it to the render queue self.draw_command_queue.iter().for_each(|command| { - 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 + // Tell the GPU which pipeline to draw in this render pass + if current_pipeline != command.pipeline_name { + let pipeline = self.pipeline_cache.get(&command.pipeline_name).unwrap(); + render_pass.set_pipeline(&pipeline.render_pipeline); + current_pipeline = command.pipeline_name.clone(); + } + + // Send the GPU the vertices and triangle indices render_pass.set_vertex_buffer(0, &command.vertex_buffer, 0, 0); render_pass.set_index_buffer(&command.index_buffer, 0, 0); - render_pass.set_bind_group(0, &command.bind_group, &[]); + + // Send the GPU the bind group resources + let bind_group = self.bind_group_cache.get(&command.bind_group_name).unwrap(); + render_pass.set_bind_group(0, bind_group, &[]); // Draw call render_pass.draw_indexed(0..command.index_count, 0, 0..1); diff --git a/src/draw_command.rs b/src/draw_command.rs index 8ac0d0392..c55cfda62 100644 --- a/src/draw_command.rs +++ b/src/draw_command.rs @@ -1,20 +1,20 @@ pub struct DrawCommand { pub pipeline_name: String, - pub bind_group: wgpu::BindGroup, + pub bind_group_name: String, pub vertex_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer, pub index_count: u32, } impl DrawCommand { - pub fn new(device: &wgpu::Device, pipeline_name: &str, vertices: &[[f32; 2]], indices: &[u16], bind_group: wgpu::BindGroup) -> Self { + pub fn new(device: &wgpu::Device, pipeline_name: &str, bind_group_name: &str, vertices: &[[f32; 2]], indices: &[u16]) -> 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_name: String::from(pipeline_name), - bind_group, + bind_group_name: String::from(bind_group_name), vertex_buffer, index_buffer, index_count, diff --git a/src/pipeline.rs b/src/pipeline.rs index 16f475207..5c7c844e5 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -6,24 +6,16 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &wgpu::Device, vertex_shader: &wgpu::ShaderModule, fragment_shader: &wgpu::ShaderModule) -> Self { + pub fn new(device: &wgpu::Device, vertex_shader: &wgpu::ShaderModule, fragment_shader: &wgpu::ShaderModule, bind_group_layout_binding_types: Vec) -> Self { + let bind_group_layout_entries = bind_group_layout_binding_types.into_iter().enumerate().map(|(index, binding_type)| + wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: wgpu::ShaderStage::all(), + ty: binding_type, + } + ).collect::>(); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - bindings: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::SampledTexture { - dimension: wgpu::TextureViewDimension::D2, - component_type: wgpu::TextureComponentType::Float, - multisampled: false, - }, - }, - // wgpu::BindGroupLayoutEntry { - // binding: 1, - // visibility: wgpu::ShaderStage::FRAGMENT, - // ty: wgpu::BindingType::Sampler, - // }, - ], + bindings: bind_group_layout_entries.as_slice(), label: None, }); @@ -42,20 +34,18 @@ impl Pipeline { }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, - cull_mode: wgpu::CullMode::Back, + cull_mode: wgpu::CullMode::None, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[ - wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, // TODO: Make this match Application.swap_chain_descriptor - color_blend: wgpu::BlendDescriptor::REPLACE, - alpha_blend: wgpu::BlendDescriptor::REPLACE, - write_mask: wgpu::ColorWrite::ALL, - }, - ], + color_states: &[wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, // TODO: Make this match Application.swap_chain_descriptor + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], depth_stencil_state: None, vertex_state: wgpu::VertexStateDescriptor { index_format: wgpu::IndexFormat::Uint16, @@ -63,11 +53,10 @@ impl Pipeline { stride: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[wgpu::VertexAttributeDescriptor { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float2, - }, - ], + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float2, + }], }], }, sample_count: 1, @@ -80,4 +69,18 @@ impl Pipeline { render_pipeline, } } + + pub fn build_bind_group(&self, device: &wgpu::Device, binding_resources: Vec) -> wgpu::BindGroup { + let bindings = binding_resources.into_iter().enumerate().map(|(index, binding_resource)| + wgpu::Binding { + binding: index as u32, + resource: binding_resource, + } + ).collect::>(); + device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.bind_group_layout, + bindings: bindings.as_slice(), + label: None, + }) + } }