diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f573c09d19..46f536bb9d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,7 +121,9 @@ By @cwfitzgerald in [#7030](https://github.com/gfx-rs/wgpu/pull/7030). By @kpreid in [#7063](https://github.com/gfx-rs/wgpu/pull/7063). -- Added `Buffer` methods corresponding to `BufferSlice` methods, so you can skip creating a `BufferSlice` when it offers no benefit, and `BufferSlice::slice()` for sub-slicing a slice. By @kpreid in [#7123](https://github.com/gfx-rs/wgpu/pull/7123). +- Add `Buffer` methods corresponding to `BufferSlice` methods, so you can skip creating a `BufferSlice` when it offers no benefit, and `BufferSlice::slice()` for sub-slicing a slice. By @kpreid in [#7123](https://github.com/gfx-rs/wgpu/pull/7123). +- Add `BufferSlice::buffer()`, `BufferSlice::offset()` and `BufferSlice::size()`. By @kpreid in [#7148](https://github.com/gfx-rs/wgpu/pull/7148). +- Add `impl From for BufferBinding` and `impl From for BindingResource`, allowing `BufferSlice`s to be easily used in creating bind groups. By @kpreid in [#7148](https://github.com/gfx-rs/wgpu/pull/7148). - Add `util::StagingBelt::allocate()` so the staging belt can be used to write textures. By @kpreid in [#6900](https://github.com/gfx-rs/wgpu/pull/6900). - Added `CommandEncoder::transition_resources()` for native API interop, and allowing users to slightly optimize barriers. By @JMS55 in [#6678](https://github.com/gfx-rs/wgpu/pull/6678). diff --git a/tests/validation_tests/api/buffer.rs b/tests/validation_tests/api/buffer.rs index f0469c608af..3a4f17e3e86 100644 --- a/tests/validation_tests/api/buffer.rs +++ b/tests/validation_tests/api/buffer.rs @@ -1,16 +1,21 @@ //! Tests of [`wgpu::Buffer`] and related. +use core::num::NonZero; + mod buffer_slice { + use super::*; + + const ARBITRARY_DESC: &wgpu::BufferDescriptor = &wgpu::BufferDescriptor { + label: None, + size: 100, + usage: wgpu::BufferUsages::VERTEX, + mapped_at_creation: false, + }; + #[test] fn reslice_success() { let (device, _queue) = crate::request_noop_device(); - - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 100, - usage: wgpu::BufferUsages::VERTEX, - mapped_at_creation: false, - }); + let buffer = device.create_buffer(ARBITRARY_DESC); assert_eq!(buffer.slice(10..90).slice(10..70), buffer.slice(20..80)); } @@ -19,14 +24,52 @@ mod buffer_slice { #[should_panic = "slice offset 10 size 80 is out of range for buffer of size 80"] fn reslice_out_of_bounds() { let (device, _queue) = crate::request_noop_device(); - - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 100, - usage: wgpu::BufferUsages::VERTEX, - mapped_at_creation: false, - }); + let buffer = device.create_buffer(ARBITRARY_DESC); buffer.slice(10..90).slice(10..90); } + + #[test] + fn getters() { + let (device, _queue) = crate::request_noop_device(); + let buffer = device.create_buffer(ARBITRARY_DESC); + + let slice_with_size = buffer.slice(10..90); + assert_eq!( + ( + slice_with_size.buffer(), + slice_with_size.offset(), + slice_with_size.size() + ), + (&buffer, 10, NonZero::new(80).unwrap()) + ); + + let slice_without_size = buffer.slice(10..); + assert_eq!( + ( + slice_without_size.buffer(), + slice_without_size.offset(), + slice_without_size.size() + ), + (&buffer, 10, NonZero::new(90).unwrap()) + ); + } + + #[test] + fn into_buffer_binding() { + let (device, _queue) = crate::request_noop_device(); + let buffer = device.create_buffer(ARBITRARY_DESC); + + // BindingResource doesn’t implement PartialEq, so use matching + let wgpu::BindingResource::Buffer(wgpu::BufferBinding { + buffer: b, + offset: 50, + size: Some(size), + }) = wgpu::BindingResource::from(buffer.slice(50..80)) + else { + panic!("didn't match") + }; + assert_eq!(b, &buffer); + assert_eq!(size, NonZero::new(30).unwrap()); + } } diff --git a/wgpu/src/api/buffer.rs b/wgpu/src/api/buffer.rs index 26a4c2a9110..cfcb8a02a76 100644 --- a/wgpu/src/api/buffer.rs +++ b/wgpu/src/api/buffer.rs @@ -360,7 +360,8 @@ impl Buffer { /// /// You can pass buffer slices to methods like [`RenderPass::set_vertex_buffer`] /// and [`RenderPass::set_index_buffer`] to indicate which portion of the buffer -/// a draw call should consult. +/// a draw call should consult. You can also convert it to a [`BufferBinding`] +/// with `.into()`. /// /// To access the slice's contents on the CPU, you must first [map] the buffer, /// and then call [`BufferSlice::get_mapped_range`] or @@ -506,6 +507,48 @@ impl<'a> BufferSlice<'a> { readable: self.buffer.usage.contains(BufferUsages::MAP_READ), } } + + /// Returns the buffer this is a slice of. + /// + /// You should usually not need to call this, and if you received the buffer from code you + /// do not control, you should refrain from accessing the buffer outside the bounds of the + /// slice. Nevertheless, it’s possible to get this access, so this method makes it simple. + pub fn buffer(&self) -> &'a Buffer { + self.buffer + } + + /// Returns the offset in [`Self::buffer()`] this slice starts at. + pub fn offset(&self) -> BufferAddress { + self.offset + } + + /// Returns the size of this slice. + pub fn size(&self) -> BufferSize { + self.size.unwrap_or_else(|| { + (|| BufferSize::new(self.buffer.size().checked_sub(self.offset)?))() + .expect("can't happen: slice has incorrect size for its buffer") + }) + } +} + +impl<'a> From> for crate::BufferBinding<'a> { + /// Convert a [`BufferSlice`] to an equivalent [`BufferBinding`], + /// provided that it will be used without a dynamic offset. + fn from(value: BufferSlice<'a>) -> Self { + BufferBinding { + buffer: value.buffer, + offset: value.offset, + size: value.size, + } + } +} + +impl<'a> From> for crate::BindingResource<'a> { + /// Convert a [`BufferSlice`] to an equivalent [`BindingResource::Buffer`], + /// provided that it will be used without a dynamic offset. + fn from(value: BufferSlice<'a>) -> Self { + crate::BindingResource::Buffer(crate::BufferBinding::from(value)) + } } /// The mapped portion of a buffer, if any, and its outstanding views.