Skip to content

Commit 640e031

Browse files
authored
Make BufferSlice's size non-optional. (#7640)
It used to be that `wgpu::Buffer` did not know its own size, and so slices had to potentially not know their endpoints. Now, buffers do, so slices can. This makes the code simpler, without modifying the API.
1 parent 65eb10e commit 640e031

File tree

5 files changed

+68
-95
lines changed

5 files changed

+68
-95
lines changed

wgpu/src/api/buffer.rs

Lines changed: 61 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,9 @@ impl Buffer {
240240
///
241241
/// - If `bounds` is outside of the bounds of `self`.
242242
/// - If `bounds` has a length less than 1.
243+
#[track_caller]
243244
pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'_> {
244-
let (offset, size) = range_to_offset_size(bounds);
245+
let (offset, size) = range_to_offset_size(bounds, self.size);
245246
check_buffer_bounds(self.size, offset, size);
246247
BufferSlice {
247248
buffer: self,
@@ -438,7 +439,7 @@ impl Buffer {
438439
pub struct BufferSlice<'a> {
439440
pub(crate) buffer: &'a Buffer,
440441
pub(crate) offset: BufferAddress,
441-
pub(crate) size: Option<BufferSize>,
442+
pub(crate) size: BufferSize,
442443
}
443444
#[cfg(send_sync)]
444445
static_assertions::assert_impl_all!(BufferSlice<'_>: Send, Sync);
@@ -456,20 +457,14 @@ impl<'a> BufferSlice<'a> {
456457
///
457458
/// - If `bounds` is outside of the bounds of `self`.
458459
/// - If `bounds` has a length less than 1.
460+
#[track_caller]
459461
pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'a> {
460-
let (offset, size) = range_to_offset_size(bounds);
461-
check_buffer_bounds(
462-
match self.size {
463-
Some(size) => size.get(),
464-
None => self.buffer.size(),
465-
},
466-
offset,
467-
size,
468-
);
462+
let (offset, size) = range_to_offset_size(bounds, self.size.get());
463+
check_buffer_bounds(self.size.get(), offset, size);
469464
BufferSlice {
470465
buffer: self.buffer,
471466
offset: self.offset + offset, // check_buffer_bounds ensures this does not overflow
472-
size: size.or(self.size), // check_buffer_bounds ensures this is essentially min()
467+
size, // check_buffer_bounds ensures this is essentially min()
473468
}
474469
}
475470

@@ -502,10 +497,7 @@ impl<'a> BufferSlice<'a> {
502497
) {
503498
let mut mc = self.buffer.map_context.lock();
504499
assert_eq!(mc.initial_range, 0..0, "Buffer is already mapped");
505-
let end = match self.size {
506-
Some(s) => self.offset + s.get(),
507-
None => mc.total_size,
508-
};
500+
let end = self.offset + self.size.get();
509501
mc.initial_range = self.offset..end;
510502

511503
self.buffer
@@ -608,10 +600,7 @@ impl<'a> BufferSlice<'a> {
608600

609601
/// Returns the size of this slice.
610602
pub fn size(&self) -> BufferSize {
611-
self.size.unwrap_or_else(|| {
612-
(|| BufferSize::new(self.buffer.size().checked_sub(self.offset)?))()
613-
.expect("can't happen: slice has incorrect size for its buffer")
614-
})
603+
self.size
615604
}
616605
}
617606

@@ -622,7 +611,7 @@ impl<'a> From<BufferSlice<'a>> for crate::BufferBinding<'a> {
622611
BufferBinding {
623612
buffer: value.buffer,
624613
offset: value.offset,
625-
size: value.size,
614+
size: Some(value.size),
626615
}
627616
}
628617
}
@@ -637,16 +626,9 @@ impl<'a> From<BufferSlice<'a>> for crate::BindingResource<'a> {
637626

638627
/// The mapped portion of a buffer, if any, and its outstanding views.
639628
///
640-
/// This ensures that views fall within the mapped range and don't overlap, and
641-
/// also takes care of turning `Option<BufferSize>` sizes into actual buffer
642-
/// offsets.
629+
/// This ensures that views fall within the mapped range and don't overlap.
643630
#[derive(Debug)]
644631
pub(crate) struct MapContext {
645-
/// The overall size of the buffer.
646-
///
647-
/// This is just a convenient copy of [`Buffer::size`].
648-
pub(crate) total_size: BufferAddress,
649-
650632
/// The range of the buffer that is mapped.
651633
///
652634
/// This is `0..0` if the buffer is not mapped. This becomes non-empty when
@@ -664,9 +646,8 @@ pub(crate) struct MapContext {
664646
}
665647

666648
impl MapContext {
667-
pub(crate) fn new(total_size: BufferAddress) -> Self {
649+
pub(crate) fn new() -> Self {
668650
Self {
669-
total_size,
670651
initial_range: 0..0,
671652
sub_ranges: Vec::new(),
672653
}
@@ -689,11 +670,8 @@ impl MapContext {
689670
/// # Panics
690671
///
691672
/// This panics if the given range overlaps with any existing range.
692-
fn add(&mut self, offset: BufferAddress, size: Option<BufferSize>) -> BufferAddress {
693-
let end = match size {
694-
Some(s) => offset + s.get(),
695-
None => self.initial_range.end,
696-
};
673+
fn add(&mut self, offset: BufferAddress, size: BufferSize) -> BufferAddress {
674+
let end = offset + size.get();
697675
assert!(self.initial_range.start <= offset && end <= self.initial_range.end);
698676
// This check is essential for avoiding undefined behavior: it is the
699677
// only thing that ensures that `&mut` references to the buffer's
@@ -716,11 +694,8 @@ impl MapContext {
716694
/// passed to [`add`].
717695
///
718696
/// [`add]`: MapContext::add
719-
fn remove(&mut self, offset: BufferAddress, size: Option<BufferSize>) {
720-
let end = match size {
721-
Some(s) => offset + s.get(),
722-
None => self.initial_range.end,
723-
};
697+
fn remove(&mut self, offset: BufferAddress, size: BufferSize) {
698+
let end = offset + size.get();
724699

725700
let index = self
726701
.sub_ranges
@@ -871,99 +846,100 @@ impl Drop for BufferViewMut<'_> {
871846
}
872847
}
873848

849+
#[track_caller]
874850
fn check_buffer_bounds(
875851
buffer_size: BufferAddress,
876-
offset: BufferAddress,
877-
size: Option<BufferSize>,
852+
slice_offset: BufferAddress,
853+
slice_size: BufferSize,
878854
) {
879855
// A slice of length 0 is invalid, so the offset must not be equal to or greater than the buffer size.
880-
if offset >= buffer_size {
856+
if slice_offset >= buffer_size {
881857
panic!(
882858
"slice offset {} is out of range for buffer of size {}",
883-
offset, buffer_size
859+
slice_offset, buffer_size
884860
);
885861
}
886862

887-
if let Some(size) = size {
888-
// Detect integer overflow.
889-
let end = offset.checked_add(size.get());
890-
if end.is_none_or(|end| end > buffer_size) {
891-
panic!(
892-
"slice offset {} size {} is out of range for buffer of size {}",
893-
offset, size, buffer_size
894-
);
895-
}
863+
// Detect integer overflow.
864+
let end = slice_offset.checked_add(slice_size.get());
865+
if end.is_none_or(|end| end > buffer_size) {
866+
panic!(
867+
"slice offset {} size {} is out of range for buffer of size {}",
868+
slice_offset, slice_size, buffer_size
869+
);
896870
}
897871
}
898872

873+
#[track_caller]
899874
fn range_to_offset_size<S: RangeBounds<BufferAddress>>(
900875
bounds: S,
901-
) -> (BufferAddress, Option<BufferSize>) {
876+
whole_size: BufferAddress,
877+
) -> (BufferAddress, BufferSize) {
902878
let offset = match bounds.start_bound() {
903879
Bound::Included(&bound) => bound,
904880
Bound::Excluded(&bound) => bound + 1,
905881
Bound::Unbounded => 0,
906882
};
907-
let size = match bounds.end_bound() {
908-
Bound::Included(&bound) => Some(bound + 1 - offset),
909-
Bound::Excluded(&bound) => Some(bound - offset),
910-
Bound::Unbounded => None,
911-
}
912-
.map(|size| BufferSize::new(size).expect("Buffer slices can not be empty"));
883+
let size = BufferSize::new(match bounds.end_bound() {
884+
Bound::Included(&bound) => bound + 1 - offset,
885+
Bound::Excluded(&bound) => bound - offset,
886+
Bound::Unbounded => whole_size - offset,
887+
})
888+
.expect("buffer slices can not be empty");
913889

914890
(offset, size)
915891
}
916892

917893
#[cfg(test)]
918894
mod tests {
919-
use super::{check_buffer_bounds, range_to_offset_size, BufferSize};
895+
use super::{check_buffer_bounds, range_to_offset_size, BufferAddress, BufferSize};
896+
897+
fn bs(value: BufferAddress) -> BufferSize {
898+
BufferSize::new(value).unwrap()
899+
}
920900

921901
#[test]
922902
fn range_to_offset_size_works() {
923-
assert_eq!(range_to_offset_size(0..2), (0, BufferSize::new(2)));
924-
assert_eq!(range_to_offset_size(2..5), (2, BufferSize::new(3)));
925-
assert_eq!(range_to_offset_size(..), (0, None));
926-
assert_eq!(range_to_offset_size(21..), (21, None));
927-
assert_eq!(range_to_offset_size(0..), (0, None));
928-
assert_eq!(range_to_offset_size(..21), (0, BufferSize::new(21)));
903+
let whole = 100;
904+
905+
assert_eq!(range_to_offset_size(0..2, whole), (0, bs(2)));
906+
assert_eq!(range_to_offset_size(2..5, whole), (2, bs(3)));
907+
assert_eq!(range_to_offset_size(.., whole), (0, bs(whole)));
908+
assert_eq!(range_to_offset_size(21.., whole), (21, bs(whole - 21)));
909+
assert_eq!(range_to_offset_size(0.., whole), (0, bs(whole)));
910+
assert_eq!(range_to_offset_size(..21, whole), (0, bs(21)));
929911
}
930912

931913
#[test]
932-
#[should_panic]
914+
#[should_panic = "buffer slices can not be empty"]
933915
fn range_to_offset_size_panics_for_empty_range() {
934-
range_to_offset_size(123..123);
916+
range_to_offset_size(123..123, 200);
935917
}
936918

937919
#[test]
938-
#[should_panic]
920+
#[should_panic = "buffer slices can not be empty"]
939921
fn range_to_offset_size_panics_for_unbounded_empty_range() {
940-
range_to_offset_size(..0);
941-
}
942-
943-
#[test]
944-
#[should_panic]
945-
fn check_buffer_bounds_panics_for_offset_at_size() {
946-
check_buffer_bounds(100, 100, None);
922+
range_to_offset_size(..0, 100);
947923
}
948924

949925
#[test]
950926
fn check_buffer_bounds_works_for_end_in_range() {
951-
check_buffer_bounds(200, 100, BufferSize::new(50));
952-
check_buffer_bounds(200, 100, BufferSize::new(100));
953-
check_buffer_bounds(u64::MAX, u64::MAX - 100, BufferSize::new(100));
954-
check_buffer_bounds(u64::MAX, 0, BufferSize::new(u64::MAX));
955-
check_buffer_bounds(u64::MAX, 1, BufferSize::new(u64::MAX - 1));
927+
check_buffer_bounds(200, 100, bs(50));
928+
check_buffer_bounds(200, 100, bs(100));
929+
check_buffer_bounds(u64::MAX, u64::MAX - 100, bs(100));
930+
check_buffer_bounds(u64::MAX, 0, bs(u64::MAX));
931+
check_buffer_bounds(u64::MAX, 1, bs(u64::MAX - 1));
956932
}
957933

958934
#[test]
959935
#[should_panic]
960936
fn check_buffer_bounds_panics_for_end_over_size() {
961-
check_buffer_bounds(200, 100, BufferSize::new(101));
937+
check_buffer_bounds(200, 100, bs(101));
962938
}
963939

964940
#[test]
965941
#[should_panic]
966942
fn check_buffer_bounds_panics_for_end_wraparound() {
967-
check_buffer_bounds(u64::MAX, 1, BufferSize::new(u64::MAX));
943+
check_buffer_bounds(u64::MAX, 1, bs(u64::MAX));
968944
}
969945
}

wgpu/src/api/device.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ impl Device {
254254
/// Creates a [`Buffer`].
255255
#[must_use]
256256
pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {
257-
let mut map_context = MapContext::new(desc.size);
257+
let mut map_context = MapContext::new();
258258
if desc.mapped_at_creation {
259259
map_context.initial_range = 0..desc.size;
260260
}
@@ -330,7 +330,7 @@ impl Device {
330330
hal_buffer: A::Buffer,
331331
desc: &BufferDescriptor<'_>,
332332
) -> Buffer {
333-
let mut map_context = MapContext::new(desc.size);
333+
let mut map_context = MapContext::new();
334334
if desc.mapped_at_creation {
335335
map_context.initial_range = 0..desc.size;
336336
}

wgpu/src/api/render_bundle_encoder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<'a> RenderBundleEncoder<'a> {
9393
&buffer_slice.buffer.inner,
9494
index_format,
9595
buffer_slice.offset,
96-
buffer_slice.size,
96+
Some(buffer_slice.size),
9797
);
9898
}
9999

@@ -112,7 +112,7 @@ impl<'a> RenderBundleEncoder<'a> {
112112
slot,
113113
&buffer_slice.buffer.inner,
114114
buffer_slice.offset,
115-
buffer_slice.size,
115+
Some(buffer_slice.size),
116116
);
117117
}
118118

wgpu/src/api/render_pass.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl RenderPass<'_> {
100100
&buffer_slice.buffer.inner,
101101
index_format,
102102
buffer_slice.offset,
103-
buffer_slice.size,
103+
Some(buffer_slice.size),
104104
);
105105
}
106106

@@ -121,7 +121,7 @@ impl RenderPass<'_> {
121121
slot,
122122
&buffer_slice.buffer.inner,
123123
buffer_slice.offset,
124-
buffer_slice.size,
124+
Some(buffer_slice.size),
125125
);
126126
}
127127

wgpu/src/util/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,7 @@ impl DownloadBuffer {
9898
buffer: &super::BufferSlice<'_>,
9999
callback: impl FnOnce(Result<Self, super::BufferAsyncError>) + Send + 'static,
100100
) {
101-
let size = match buffer.size {
102-
Some(size) => size.into(),
103-
None => buffer.buffer.map_context.lock().total_size - buffer.offset,
104-
};
101+
let size = buffer.size.into();
105102

106103
let download = device.create_buffer(&super::BufferDescriptor {
107104
size,

0 commit comments

Comments
 (0)