diff --git a/wgpu/examples/README.md b/wgpu/examples/README.md index 523a8dc7fc..6de13d2eb7 100644 --- a/wgpu/examples/README.md +++ b/wgpu/examples/README.md @@ -15,6 +15,7 @@ All framework-based examples render to the window. | vertex attributes | :star: | :star: | :star: | :star: | :star: | | :star: | :star: | | instancing | :star: | | | | | | | | | lines and points | | | | :star: | | | | | +| dynamic buffer offsets | | | | | :star: | | | | | implicit layout | | | | | | | | | | sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: | | storage textures | :star: | | | | | | | | diff --git a/wgpu/examples/boids/main.rs b/wgpu/examples/boids/main.rs index 7685da3541..54099c199f 100644 --- a/wgpu/examples/boids/main.rs +++ b/wgpu/examples/boids/main.rs @@ -205,17 +205,15 @@ impl framework::Example for Example { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(sim_param_buffer.slice(..)), + resource: sim_param_buffer.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::Buffer(particle_buffers[i].slice(..)), + resource: particle_buffers[i].as_entire_binding(), }, wgpu::BindGroupEntry { binding: 2, - resource: wgpu::BindingResource::Buffer( - particle_buffers[(i + 1) % 2].slice(..), // bind to opposite buffer - ), + resource: particle_buffers[(i + 1) % 2].as_entire_binding(), // bind to opposite buffer }, ], label: None, diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index 1465ccc941..0c603c78f9 100644 --- a/wgpu/examples/cube/main.rs +++ b/wgpu/examples/cube/main.rs @@ -230,7 +230,7 @@ impl framework::Example for Example { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + resource: uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, diff --git a/wgpu/examples/hello-compute/main.rs b/wgpu/examples/hello-compute/main.rs index b63ae16f40..4ea4f13cf5 100644 --- a/wgpu/examples/hello-compute/main.rs +++ b/wgpu/examples/hello-compute/main.rs @@ -80,7 +80,7 @@ async fn execute_gpu(numbers: Vec) -> Vec { layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(storage_buffer.slice(..)), + resource: storage_buffer.as_entire_binding(), }], }); diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index 669a674166..3fe0ea9bc2 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -327,7 +327,7 @@ impl framework::Example for Example { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + resource: uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index 030c63d539..55ec611795 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -90,8 +90,7 @@ struct Entity { vertex_buf: Rc, index_buf: Rc, index_count: usize, - bind_group: wgpu::BindGroup, - uniform_buf: wgpu::Buffer, + uniform_offset: wgpu::DynamicOffset, } struct Light { @@ -178,7 +177,9 @@ struct Example { shadow_pass: Pass, forward_pass: Pass, forward_depth: wgpu::TextureView, + entity_bind_group: wgpu::BindGroup, light_uniform_buf: wgpu::Buffer, + entity_uniform_buf: wgpu::Buffer, } impl Example { @@ -245,53 +246,6 @@ impl framework::Example for Example { usage: wgpu::BufferUsage::INDEX, }); - let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; - let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: entity_uniform_size, - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); - - let local_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, - min_binding_size: wgpu::BufferSize::new( - mem::size_of::() as _ - ), - }, - count: None, - }], - label: None, - }); - - let mut entities = vec![{ - use cgmath::SquareMatrix; - - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &local_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(plane_uniform_buf.slice(..)), - }], - label: None, - }); - Entity { - mx_world: cgmath::Matrix4::identity(), - rotation_speed: 0.0, - color: wgpu::Color::WHITE, - vertex_buf: Rc::new(plane_vertex_buf), - index_buf: Rc::new(plane_index_buf), - index_count: plane_index_data.len(), - bind_group, - uniform_buf: plane_uniform_buf, - } - }]; - struct CubeDesc { offset: cgmath::Vector3, angle: f32, @@ -325,7 +279,31 @@ impl framework::Example for Example { }, ]; - for cube in &cube_descs { + let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; + let num_entities = 1 + cube_descs.len() as wgpu::BufferAddress; + assert!(entity_uniform_size <= wgpu::BIND_BUFFER_ALIGNMENT); + //Note: dynamic offsets also have to be aligned to `BIND_BUFFER_ALIGNMENT`. + let entity_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: num_entities * wgpu::BIND_BUFFER_ALIGNMENT, + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, + }); + + let mut entities = vec![{ + use cgmath::SquareMatrix; + Entity { + mx_world: cgmath::Matrix4::identity(), + rotation_speed: 0.0, + color: wgpu::Color::WHITE, + vertex_buf: Rc::new(plane_vertex_buf), + index_buf: Rc::new(plane_index_buf), + index_count: plane_index_data.len(), + uniform_offset: 0, + } + }]; + + for (i, cube) in cube_descs.iter().enumerate() { use cgmath::{Decomposed, Deg, InnerSpace, Quaternion, Rotation3}; let transform = Decomposed { @@ -333,12 +311,6 @@ impl framework::Example for Example { rot: Quaternion::from_axis_angle(cube.offset.normalize(), Deg(cube.angle)), scale: cube.scale, }; - let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: entity_uniform_size, - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); entities.push(Entity { mx_world: cgmath::Matrix4::from(transform), rotation_speed: cube.rotation, @@ -346,18 +318,36 @@ impl framework::Example for Example { vertex_buf: Rc::clone(&cube_vertex_buf), index_buf: Rc::clone(&cube_index_buf), index_count: cube_index_data.len(), - bind_group: device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &local_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), - }], - label: None, - }), - uniform_buf, + uniform_offset: ((i + 1) * wgpu::BIND_BUFFER_ALIGNMENT as usize) as _, }); } + let local_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer { + dynamic: true, + min_binding_size: wgpu::BufferSize::new(entity_uniform_size), + }, + count: None, + }], + label: None, + }); + let entity_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &local_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &entity_uniform_buf, + offset: 0, + size: wgpu::BufferSize::new(entity_uniform_size), + }, + }], + label: None, + }); + // Create other resources let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { label: Some("shadow"), @@ -474,7 +464,7 @@ impl framework::Example for Example { layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + resource: uniform_buf.as_entire_binding(), }], label: None, }); @@ -595,11 +585,11 @@ impl framework::Example for Example { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + resource: uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::Buffer(light_uniform_buf.slice(..)), + resource: light_uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 2, @@ -679,6 +669,8 @@ impl framework::Example for Example { forward_pass, forward_depth: depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), light_uniform_buf, + entity_uniform_buf, + entity_bind_group, } } @@ -739,7 +731,11 @@ impl framework::Example for Example { entity.color.a as f32, ], }; - queue.write_buffer(&entity.uniform_buf, 0, bytemuck::bytes_of(&data)); + queue.write_buffer( + &self.entity_uniform_buf, + entity.uniform_offset as wgpu::BufferAddress, + bytemuck::bytes_of(&data), + ); } if self.lights_are_dirty { @@ -782,7 +778,7 @@ impl framework::Example for Example { pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]); for entity in &self.entities { - pass.set_bind_group(1, &entity.bind_group, &[]); + pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]); pass.set_index_buffer(entity.index_buf.slice(..)); pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); @@ -818,7 +814,7 @@ impl framework::Example for Example { pass.set_bind_group(0, &self.forward_pass.bind_group, &[]); for entity in &self.entities { - pass.set_bind_group(1, &entity.bind_group, &[]); + pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]); pass.set_index_buffer(entity.index_buf.slice(..)); pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index 858de6ed85..8995eb5635 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -257,7 +257,7 @@ impl framework::Example for Skybox { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + resource: uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index 0c09a2c418..a3fcbc7ba7 100644 --- a/wgpu/examples/water/main.rs +++ b/wgpu/examples/water/main.rs @@ -237,7 +237,7 @@ impl Example { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(water_uniforms.slice(..)), + resource: water_uniforms.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, @@ -471,7 +471,7 @@ impl framework::Example for Example { layout: &terrain_bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(terrain_normal_uniform_buf.slice(..)), + resource: terrain_normal_uniform_buf.as_entire_binding(), }], label: Some("Terrain Normal Bind Group"), }); @@ -479,7 +479,7 @@ impl framework::Example for Example { layout: &terrain_bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer(terrain_flipped_uniform_buf.slice(..)), + resource: terrain_flipped_uniform_buf.as_entire_binding(), }], label: Some("Terrain Flipped Bind Group"), }); diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 8ab21d75db..70f9fd2aaa 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -633,17 +633,17 @@ impl crate::Context for Context { .map(|entry| bm::BindGroupEntry { binding: entry.binding, resource: match entry.resource { - BindingResource::Buffer(ref buffer_slice) => { - bm::BindingResource::Buffer(bm::BufferBinding { - buffer_id: buffer_slice.buffer.id, - offset: buffer_slice.offset, - size: buffer_slice.size, - }) - } - BindingResource::Sampler(ref sampler) => { - bm::BindingResource::Sampler(sampler.id) - } - BindingResource::TextureView(ref texture_view) => { + BindingResource::Buffer { + buffer, + offset, + size, + } => bm::BindingResource::Buffer(bm::BufferBinding { + buffer_id: buffer.id, + offset, + size, + }), + BindingResource::Sampler(sampler) => bm::BindingResource::Sampler(sampler.id), + BindingResource::TextureView(texture_view) => { bm::BindingResource::TextureView(texture_view.id) } BindingResource::TextureViewArray(texture_view_array) => { diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index e4b8dfaef8..b2aed264bf 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -922,12 +922,16 @@ impl crate::Context for Context { .entries .iter() .map(|binding| { - let mapped_resource = match &binding.resource { - BindingResource::Buffer(buffer_slice) => { + let mapped_resource = match binding.resource { + BindingResource::Buffer { + ref buffer, + offset, + size, + } => { let mut mapped_buffer_binding = - web_sys::GpuBufferBinding::new(&buffer_slice.buffer.id.0); - mapped_buffer_binding.offset(buffer_slice.offset as f64); - if let Some(s) = buffer_slice.size { + web_sys::GpuBufferBinding::new(&buffer.id.0); + mapped_buffer_binding.offset(offset as f64); + if let Some(s) = size { mapped_buffer_binding.size(s.get() as f64); } JsValue::from(mapped_buffer_binding.clone()) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 00fbfa238a..10d4d90316 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -842,7 +842,15 @@ pub enum BindingResource<'a> { /// /// Corresponds to [`BindingType::UniformBuffer`] and [`BindingType::StorageBuffer`] /// with [`BindGroupLayoutEntry::count`] set to None. - Buffer(BufferSlice<'a>), + Buffer { + /// The buffer to bind. + buffer: &'a Buffer, + /// Base offset of the buffer. For bindings with `dynamic == true`, this offset + /// will be added to the dynamic offset provided in [`RenderPass::set_bind_group`]. + offset: BufferAddress, + /// Size of the binding, or `None` for using the rest of the buffer. + size: Option, + }, /// Binding is a sampler. /// /// Corresponds to [`BindingType::Sampler`] with [`BindGroupLayoutEntry::count`] set to None. @@ -1646,6 +1654,15 @@ impl Drop for BufferViewMut<'_> { } impl Buffer { + /// Return the binding view of the entire buffer. + pub fn as_entire_binding(&self) -> BindingResource { + BindingResource::Buffer { + buffer: self, + offset: 0, + size: None, + } + } + /// Use only a portion of this Buffer for a given operation. Choosing a range with no end /// will use the rest of the buffer. Using a totally unbounded range will use the entire buffer. pub fn slice>(&self, bounds: S) -> BufferSlice {